1
0
Fork 0

feat: Add various content types to docusaurus (#7231)

This brings blog posts, showcase posts, and newsletter editions into the Docusaurus site.

It also adds support for using TailwindCSS inside a container. So this will probably end up being the new freesewing.org site in v4.
This commit is contained in:
Joost De Cock 2024-11-18 11:05:16 +01:00 committed by GitHub
parent ef8f68bcaf
commit 469eb43c95
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
595 changed files with 20432 additions and 2469 deletions

2
.gitignore vendored
View file

@ -47,6 +47,8 @@ sites/orgdocs/src/lib/designs.mjs
sites/orgdocs/src/lib/designinfo.mjs
sites/orgdocs/src/lib/i18n.mjs
sites/orgdocs/docs/designs/*/options/readme.mdx
sites/orgdocs/authors.json
sites/orgdocs/showcase-tags.mjs
# Lab auto-generated content
sites/lab/lib

View file

@ -32,7 +32,7 @@ packageJson:
author: AlfaLyr (https://github.com/alfalyr)
i18n:
private: true
jane:
jane:
author: SeaZeeZee (https://github.com/SeaZeeZee)
lab:
private: true
@ -73,6 +73,14 @@ packageJson:
files:
- index.json
- package.json
react-components:
exports:
".":
"internal": "./src/index.mjs"
"default": "./dist/index.mjs"
"./linedrawings": "./src/linedrawings/index.mjs"
"./pattern": "./src/pattern/index.mjs"
"./xray": "./src/pattern-xray/index.mjs"
rehype-hightlight-lines:
private: true
sandy:

View file

@ -1,7 +1,6 @@
import designs from './designs.json' assert { type: 'json' }
import packages from './packages.json' assert { type: 'json' }
import plugins from './plugins.json' assert { type: 'json' }
import sites from './sites.json' assert { type: 'json' }
// Helper method to construct summary objects
const unpack = (obj, folder) =>
@ -22,14 +21,13 @@ const unpackDesigns = (obj, folder) =>
)
// Re-Export imported JSON
export { designs, packages, plugins, sites }
export { designs, packages, plugins }
// All software
export const software = {
...unpackDesigns(designs, 'designs'),
...unpack(plugins, 'plugins'),
...unpack(packages, 'packages'),
...unpack(sites, 'sites'),
}
// All software published on NPM
@ -40,4 +38,4 @@ export const publishedSoftware = {
}
export const publishedTypes = ['designs', 'packages', 'plugins']
export const types = [...publishedTypes, 'sites']
export const types = publishedTypes

View file

@ -1,7 +0,0 @@
{
"backend": "FreeSewing backend",
"dev": "FreeSewing website with documentation for contributors & developers",
"org": "FreeSewing website",
"sde": "Stand-alone develpment environment. Basis for the @freesewing/new-design package",
"shared": "Shared code and React components for different websites"
}

View file

@ -1,6 +1,5 @@
---
title: Guides
order: zbb
---
Below is an overview of the various FreeSewing guides:

View file

@ -1,21 +1,21 @@
---
title: About Frontmatter
order: 10
sidebar_position: 10
---
Frontmatter is a way to add metadata to Markdown documents.
Frontmatter sits at the top of the file (it's matter that's at the front) and is
surrounded by lines with three dashes on them. It contains several keys with a value.
surrounded by lines with three dashes on them. It contains several keys with a value.
The `title` key is required on every page, it holds the title of the page.
The `title` key is required on every page, it holds the title of the page.
The `order` key is not required and can be used to sort pages in a certain order, if there is no order key then the pages will be sorted alphabetically.
```md
---
title: About Frontmatter
order: 20
sidebar_position: 20
---
```

View file

@ -1,6 +1,6 @@
---
title: Lists or arrays
order: 30
sidebar_position: 30
---
Values are typically text or numbers, but you can also make it a list or array.
@ -9,7 +9,8 @@ There's two types of syntax for this:
```md
tags: [tag1, another, three]
categories:
- cat1
- anothercat
- somethingelse
- cat1
- anothercat
- somethingelse
```

View file

@ -1,6 +1,6 @@
---
title: Multi-line text
order: 40
sidebar_position: 40
---
To add multi-line text in Frontmatter, use a `|` character,
@ -8,6 +8,6 @@ and prefix the lines by spaces:
```md
about: |
This is a multi-line text
that will be assigned to the about key
This is a multi-line text
that will be assigned to the about key
```

View file

@ -1,6 +1,6 @@
---
title: Structure
order: 20
sidebar_position: 20
---
Frontmatter is made up of `key: value` pairs.

View file

@ -1,6 +1,5 @@
---
title: Howtos
order: zcc
---
You can find a list of all FreeSewing hotwtos below:
@ -17,4 +16,3 @@ Guides and howtos are on a spectrum with howtos being terse _do-this-then-that_
guides take more time to explain in-depth what is being done and why.
:::

View file

@ -1,6 +1,5 @@
---
title: Reference
order: zdd
---
You can find a list of all FreeSewing reference documentation below:
@ -12,4 +11,3 @@ You can find a list of all FreeSewing reference documentation below:
## Full list
<ReadMore recurse />

View file

@ -1,6 +1,5 @@
---
title: Training
order: zzz
---
FreeSewing has created the following training materials:

View file

@ -1,6 +1,6 @@
---
title: Getting started with Codespaces
order: 13
sidebar_position: 13
---
:::note
@ -29,19 +29,22 @@ to have or use your own computer to perform this development work.
## Accessing Codespaces
Access Codespaces from the GitHub website while logged in:
- The "Your Codespaces" page is accessed via the "Codespaces" link at the top
of any GitHub page.
of any GitHub page.
- A shortcut URL is: https://github.com/codespaces
## Creating a codespace
To create a new codespace:
- Select the "New codespace" button at the top of the Your Codespaces page.
- A shortcut URL is: https://github.com/codespaces/new
This will open a "Create a New Codespace" page.
On the Create a New Codespace page, select the options for your codespace:
- Repository -> ("`username/freesewing`" assuming that is the name of your GitHub repository)
- Branch -> (select the branch you want to use)
- Region -> (select the region most appropriate for you)
@ -52,14 +55,13 @@ The Codespaces app will open in the browser window.
The Codespaces app is basically the [Visual Studio Code][vs] app with
Codespaces and GitHub integration built in.
[vs]: https://code.visualstudio.com
(Wait 45 seconds or so for the Codespace app to clone the repository from
GitHub to the codespace repository and start.)
[vs]: https://code.visualstudio.com 'Wait 45 seconds or so for the Codespace app to clone the repository from
GitHub to the codespace repository and start.'
## Editing files
You can browse and edit files in your codespace repository from within the app:
- On the Activity Bar on the far left side of the page, select the
Explorer icon.
(The icon looks like two pages of paper.)
@ -72,6 +74,7 @@ Auto-save is enabled by default, so any changes you make are
automatically saved in your codespace repository.
To commit changes to your codespace repository:
- In the Activity Bar on the far left side of the page,
select the Source Control icon.
- In the Source Control sidebar you can see a list of changed files.
@ -82,6 +85,7 @@ To commit changes to your codespace repository:
To push committed changes from your codespace repository back to your
GitHub repository:
- After committing changes to your codespace repository, press the
"Sync Changes" button to push the committed changes to your GitHub
repository.
@ -94,13 +98,16 @@ GitHub repository:
In the "Terminal" panel at the bottom of the page, you can run commands.
To start the lab website (to view and test new designs and design changes):
- In the Terminal panel, run `yarn kickstart` and then `yarn lab`.
To start the dev or org websites (to view and test documentation changes):
- dev: In the Terminal panel, run `cd sites/dev` and `yarn start`.
- org: In the Terminal panel, run `cd sites/org` and `yarn start`.
After the lab, dev, or org website starts:
- The usual `localhost:8000` port will automatically be forwarded to a custom
URL.
- A pop-up will appear saying that the port has been forwarded. The "Open in
@ -122,6 +129,7 @@ However, you can make the port public so others can access it
(or so you can access it on a different browser while not logged into GitHub).
To make the port public:
- In the Ports panel, right-click on the port and select
Port Visibility -> Public.
- The custom URL will be now be a publicly-accessible forwarded port.
@ -130,6 +138,7 @@ To make the port public:
## Starting and stopping a codespace
You can start and stop your codespace via the Your Codespaces page.
- To start: Click on the codespace name to browse to the Codespaces
app URL for that codespace.
(You can also right-click to copy the URL and open it in a
@ -150,7 +159,8 @@ You can rename a codespace to make it easier to identify.
(This will help if you have more than one codespace.)
To rename a codespace:
- Go to the Your Codespaces page.
- Go to the Your Codespaces page.
- Open the "..." ellipses menu next to the codespace
and select "Rename".
@ -158,17 +168,18 @@ To rename a codespace:
You can access GitHub Settings via the user icon menu in the upper right
corner of any GitHub page (but not on the Codespaces app page).
- Selecting the "Settings" menu item will open the GitHub Settings page.
- The Codespaces settings are under "Codespaces" in the left sidebar
on the Settings page.
on the Settings page.
- A shortcut URL is: https://github.com/settings/codespaces
Among the Codespaces settings available are:
- "Default idle timeout" (how long codespaces continue to run when idle,
before being stopped)
before being stopped)
- "Default retention period" (how long codespace repositories are kept
when unused, before being deleted)
when unused, before being deleted)
:::note
Do not be confused by the other Settings icon/menu at the bottom left of the
@ -180,11 +191,13 @@ are located.
## Usage and Quotas
To check usage:
- Usage information is under "Billing and plans" in the left sidebar on
the Settings page.
- A shortcut URL is: https://github.com/settings/billing
About quotas:
- As a free account user, you have a quota of 120 core-hours of usage
and 15 GB of storage per billing month.
That is the total for all your codespaces combined.
@ -209,18 +222,20 @@ Don't worry -- you won't get a bill if you exceed your quota!
Instead, you will just be unable to start any of your codespaces
until the start of the next billing month.
And, if you have any work-in-progress changes that you need access to
before then, you can export those changes to a new GitHub branch.
before then, you can export those changes to a new GitHub branch.
:::
## Deleting a codespace
To delete a codespace:
- Go to the Your Codespaces page.
- Open the "..." ellipses menu next to the codespace
and select "Delete".
:::note RELATED
For more information, please see:
- [GitHub Codebases documentation](https://docs.github.com/en/codespaces).
- [About billing for GitHub Codespaces](https://docs.github.com/en/billing/managing-billing-for-github-codespaces/about-billing-for-github-codespaces)
:::
:::

View file

@ -1,6 +1,6 @@
---
title: Getting started with Gitpod
order: 10
sidebar_position: 10
---
If you don't want to set up a dev environment or if you just want to kick the

View file

@ -1,6 +1,6 @@
---
title: Setting up the FreeSewing development environment
order: 40
sidebar_position: 40
---
FreeSewing provides a development environment to help you design and develop

View file

@ -1,6 +1,6 @@
---
title: Start the development environment
order: 50
sidebar_position: 50
---
FreeSewing provides a development environment to help you design and develop patterns.
@ -21,7 +21,9 @@ yarn lab
Then point your browser to http://localhost:8000
:::tip
### Adding a new design
This is all you need to work on existing designs. If you'd like to add a new design, run:
```bash

View file

@ -1,6 +1,6 @@
---
title: Installing Node.js
order: 20
sidebar_position: 20
---
Now we will use `nvm` to install Node.js. Run the following command:

View file

@ -1,6 +1,6 @@
---
title: Installing nvm
order: 10
sidebar_position: 10
---
FreeSewing is built with [Node.js](https://nodejs.org/), a JavaScript runtime.

View file

@ -1,6 +1,6 @@
---
title: Using a different Node.js version
order: 30
sidebar_position: 30
---
Now that you've got Node.js setup, we can start setting up the FreeSewing

View file

@ -1,6 +1,6 @@
---
title: Getting started on Linux
order: 15
sidebar_position: 15
---
In this tutorial, we will setup Node.js and initialize the FreeSewing

View file

@ -1,6 +1,6 @@
---
title: Setting up the FreeSewing development environment
order: 40
sidebar_position: 40
---
FreeSewing provides a development environment to help you design and develop

View file

@ -1,6 +1,6 @@
---
title: Start the development environment
order: 50
sidebar_position: 50
---
FreeSewing provides a development environment to help you design and develop patterns.
@ -21,7 +21,9 @@ yarn lab
Then point your browser to http://localhost:8000
:::tip
### Adding a new design
This is all you need to work on existing designs. If you'd like to add a new design, run:
```bash

View file

@ -1,6 +1,6 @@
---
title: Installing Node.js
order: 20
sidebar_position: 20
---
Now we will use `nvm` to install Node.js. Run the following command:

View file

@ -1,6 +1,6 @@
---
title: Installing nvm
order: 15
sidebar_position: 15
---
FreeSewing is built with [Node.js](https://nodejs.org/), a JavaScript runtime.

View file

@ -1,6 +1,6 @@
---
title: Installing the Xcode command line tools
order: 10
sidebar_position: 10
---
Before we can get started, we need some basic tools for development.

View file

@ -1,6 +1,6 @@
---
title: Using a different Node.js version
order: 30
sidebar_position: 30
---
Now that you've got Node.js setup, we can start setting up the FreeSewing

View file

@ -1,6 +1,6 @@
---
title: Getting started on Mac
order: 25
sidebar_position: 25
---
In this tutorial, we will setup Node.js and initialize the FreeSewing

View file

@ -1,6 +1,6 @@
---
title: Getting started with Vercel
order: 14
sidebar_position: 14
---
## What is Vercel?
@ -23,6 +23,7 @@ containing a fork of the FreeSewing repository.
## Why you might want to use Vercel
There are reasons why you might want your own Vercel account:
- You can preview your changes:
- if you develop on a mobile device or if you do not have access to a
computer.
@ -61,7 +62,7 @@ Free Hobby accounts are limited to 3 Projects per Git repository.
Under each project there will be many, many deployments.
_Deployments are simply builds, an instance of a website/app
\_Deployments are simply builds, an instance of a website/app
built from a specific commit version of a specific branch of the repository.
These deployments can be accessed using a web browser to preview
the web app or website.
@ -103,11 +104,11 @@ account/credentials without having to create a separate username
or password.
1. On the [Vercel website][v] select the "Sign Up" button.
(A shortcut URL is: [https://vercel.com/signup][vsu].)
(A shortcut URL is: [https://vercel.com/signup][vsu].)
2. Select the "Continue with GitHub" button.
3. A pop-up window will appear asking you for permission to access
your GitHub information.
Press the green "Authorize Vercel" button to continue.
your GitHub information.
Press the green "Authorize Vercel" button to continue.
[v]: https://vercel.com
[vsu]: https://vercel.com/signup
@ -125,12 +126,12 @@ FreeSewing repository.
2. Select your personal GitHub account from the list.
3. Select the "Only select repositories" radio button.
4. In "Select repositories" drop-down menu, select your `freesewing`
repository.
repository.
5. Click the green "Install" button.
6. Confirm that you are giving permission to access the repository
by entering your GitHub password.
by entering your GitHub password.
7. Finally, back at the Import Git Repository screen complete the
import by selecting the white "Import" button.
import by selecting the white "Import" button.
## Creating a project
@ -139,15 +140,16 @@ create a project.
By default, the default Root Directory will be `sites/dev`.
The Root Directory setting will determine the build type for the project.
- `sites/dev` will build a freesewing.dev website
- `sites/org` will build a freesewing.org website
1. Change the name of the project, if you wish.
Names can consist of alphanumeric lowercase and hyphen characters.
Names can consist of alphanumeric lowercase and hyphen characters.
2. Change the Root Directory to the desired setting, as described above.
3. In the Build & Development Settings,
add `yarn build` as the Build Command override.
(All the other settings will work fine with the default values.)
add `yarn build` as the Build Command override.
(All the other settings will work fine with the default values.)
4. Press the white "Deploy" button.
Vercel will then create the project and start building the project's first
@ -167,17 +169,18 @@ ones based on the `develop` branch
`develop` instead of `main).
Created deployments include:
1. The initial production deployment. (Because you don't have a branch named
`main` in your repository, Vercel will instead create the initial
production deployment from the default `develop` branch.)
`main` in your repository, Vercel will instead create the initial
production deployment from the default `develop` branch.)
2. A new preview deployment every time you update your `develop` branch in GitHub
(for example, whenever you sync it with the latest `freesewing/freesewing`
updates)
(for example, whenever you sync it with the latest `freesewing/freesewing`
updates)
3. A new preview deployment for every new branch you push to GitHub
4. A new preview deployment for every update you make to these new branches
when you push to GitHub
when you push to GitHub
5. A new preview deployment for every update you make to your existing branches
when you push to GitHub
when you push to GitHub
If you have multiple projects for the same repository
(for example, if you have both `sites/org` and `sites/dev` projects),
@ -196,7 +199,7 @@ Once they start, deployments take about
You will manage your account and projects from the Vercel Dashboard
page, [https://vercel.com/dashboard][vd].
The default __Overview__ tab at the top of the Dashboard page will show your repositories
The default **Overview** tab at the top of the Dashboard page will show your repositories
and projects.
Click on a project name to go to its project page.
@ -204,31 +207,32 @@ Click on a project name to go to its project page.
## Project pages
The default __Project__ tab at the top of the project page will show the
The default **Project** tab at the top of the project page will show the
the production deployment and some of the most recent preview
deployments for that project.
Click on the __Deployments__ tab to see all of the project's deployments.
Click on the **Deployments** tab to see all of the project's deployments.
Click on a deployment name to go to its deployment page.
Click on the __Settings__ tab to see the project's settings.
Click on the **Settings** tab to see the project's settings.
## Deployment pages
On the default __Deployment__ tab at the top of the deployment page
On the default **Deployment** tab at the top of the deployment page
you will see information about the deployment.
Under __Domains__ you will see one or more URLs that can be used to
Under **Domains** you will see one or more URLs that can be used to
access the deployment.
These are also the URLs that you can share with others so they
can view your deployments.
- URLs containing hash characters link to the deployment for a single
commit.
commit.
- URLs without a hash point to the deployment for the latest version
of that branch.
of that branch.
If you ever want to delete a deployment you can do so on its
deployment page, under the "__...__" three dots menu.
deployment page, under the "**...**" three dots menu.
## Usage and Billing
@ -237,11 +241,12 @@ Vercel's free Hobby accounts come with
This should be at least 10-15x the amount you will actually use in
a month, so do not worry about this.
If you want to check your usage, please seee the __Usage__ tab at the
If you want to check your usage, please seee the **Usage** tab at the
top of the Dashboard page.
- A shortcut URL is [https://vercel.com/dashboard/usage][vu]
- Or, [https://vercel.com/account/billing][vb] will show a summary
of your usage.
of your usage.
## Disabling automatic deployments
@ -249,8 +254,8 @@ You can disable and enable automatic deployments for a project,
for example if you wish to temporarily stop them while working on
a bug that prevents successful builds.
On the Project Settings page, select __Git__ from the menu on the
left. Change the __Ignored Build Step__ behavior from "Automatic" to
On the Project Settings page, select **Git** from the menu on the
left. Change the **Ignored Build Step** behavior from "Automatic" to
"Don't build anything".
[vu]: https://vercel.com/dashboard/usage

View file

@ -1,6 +1,6 @@
---
title: Getting started on Windows
order: 30
sidebar_position: 30
---
:::warning

View file

@ -1,6 +1,6 @@
---
title: Setting up the development environment
order: 20
sidebar_position: 20
---
FreeSewing provides a development environment that visualizes your design for
@ -18,7 +18,7 @@ It will ask if it is ok to install the development environment in a new folder
named `freesewing`. You can accept the default, or pick a different folder name
if you prefer.
It will also ask what package manager you would like to use.
It will also ask what package manager you would like to use.
Here too the default (`npm`) is fine., unless you are certain you have **yarn** installed.
After answering these questions, files will be downloaded, dependencies installed,

View file

@ -1,6 +1,6 @@
---
title: Installing NodeJS
order: 10
sidebar_position: 10
---
FreeSewing is a JavaScript project, so you need JavaScript to work with it.
@ -9,14 +9,15 @@ precise. You can switch this website theme from light to dark mode, and
that would not work without JavaScript.
As a **user** of FreeSewing, this is all you need. To develop with FreeSewing
you are going to need to be able to run JavaScript *outside* the browser using
a JavaScript *runtime*. Which just means a thing that can *run* JavaScript.
you are going to need to be able to run JavaScript _outside_ the browser using
a JavaScript _runtime_. Which just means a thing that can _run_ JavaScript.
We are going to be using [NodeJS](https://nodejs.org/) in this tutorial. It is
the most established of the different JavaScript runtimes. But there's also
other runtimes like [Deno](https://deno.com/) or [Bun](https://bun.sh/).
the most established of the different JavaScript runtimes. But there's also
other runtimes like [Deno](https://deno.com/) or [Bun](https://bun.sh/).
## Install
If you don't have NodeJS on your system, you can go to
[NodeJS.org](https://nodejs.org/) and follow the install instructions.

View file

@ -1,6 +1,6 @@
---
title: The FreeSewing development environment
order: 30
sidebar_position: 30
---
If you have been to FreeSewing.org the FreeSewing development environment will look familiar.
@ -44,4 +44,3 @@ For the following along this tutorial, you have two options:
- Pick **From scratch** if you prefer to actively participate by recreating the design in this tutorial.
I recommend the latter. You will learn (and remember) a lot more if you are actively engaging.

View file

@ -1,6 +1,6 @@
---
title: Folder structure
order: 40
sidebar_position: 40
---
Inside the `freesewing` folder -- which might have a different name if that is
@ -23,10 +23,10 @@ this menu:
![Design templates provided by the FreeSewing development environment](./templates.png)
As you might have guessed by now, each of these options is contained in its
own subfolder under `designs`.
own subfolder under `designs`.
You can edit the files under `designs/[template]/src/` and the changes you make
will be reflected in the development environment.
Don't take my word for it though. Let's start doing exactly that
Don't take my word for it though. Let's start doing exactly that
in [Part 2](/tutorials/pattern-design/part2).

View file

@ -1,10 +1,10 @@
---
title: Adding measurements
order: 30
sidebar_position: 30
---
FreeSewing is all about _bespoke_ sewing patterns -- or *parametric
design* to use a more generic term.
FreeSewing is all about _bespoke_ sewing patterns -- or _parametric
design_ to use a more generic term.
That means that when drafting our pattern, I will take the measurements provided
by the user into account.
@ -21,7 +21,7 @@ So let's add it as a required measurement.
In our `src/bib.mjs` file, we will add a `measurements` property to the `bib` object.
This property will be an Array (a list) holding all required measurements for this part.
I am using [*the official name* of the measurement](/reference/measurements) here. For head
I am using [_the official name_ of the measurement](/reference/measurements) here. For head
circumference, that name is `head`.
:::note [FIXME]

View file

@ -1,6 +1,6 @@
---
title: Adding options
order: 40
sidebar_position: 40
---
I have shown what our bib should look like, and added the _head_ measurement
@ -12,7 +12,7 @@ to work with. But there's still a number of choices I have to make:
I could make all of these choices for the user and set them in stone, so to speak.
But since the pattern I am designing is code, it is trivial (and _IMHO_ very satisfying)
But since the pattern I am designing is code, it is trivial (and _IMHO_ very satisfying)
to make a pattern flexible and let the user choose.
All I need to do to give control to the user is add _options_ to the part.
@ -34,10 +34,10 @@ export const bib = {
measurements: [ 'head' ],
// highlight-start
options: {
neckRatio: {
pct: 80,
min: 70,
max: 90,
neckRatio: {
pct: 80,
min: 70,
max: 90,
menu: 'fit'
},
},
@ -61,7 +61,7 @@ They are all documented [in the part reference docs](/reference/api/part/config/
##### What is `menu` and why should you care?
The `menu` property on our option is *extra*.
The `menu` property on our option is _extra_.
It will be ignored by FreeSewing's core library and if we leave it out, our design will produce the same result.
Instead, this `menu` property is there for the benefit of FreeSewing's development environment which will use this to build a menu structure for the various
@ -85,25 +85,25 @@ export const bib = {
draft: draftBib,
measurements: [ 'head' ],
options: {
neckRatio: {
pct: 80,
min: 70,
max: 90,
neckRatio: {
pct: 80,
min: 70,
max: 90,
menu: 'fit'
},
// highlight-start
widthRatio: {
pct: 45,
min: 35,
max: 55,
menu: 'style'
widthRatio: {
pct: 45,
min: 35,
max: 55,
menu: 'style'
},
lengthRatio: {
pct: 75,
min: 55,
max: 85,
menu: 'style'
lengthRatio: {
pct: 75,
min: 55,
max: 85,
menu: 'style'
},
// highlight-end
},
@ -116,4 +116,3 @@ Later, I will test-drive our pattern to see how it behaves when we adapt the opt
between their minimum and maximum values. At that time, I may need to tweak these values.
With that out of the way, I will start drawing the bib.

View file

@ -1,6 +1,6 @@
---
title: Avoiding overlap
order: 92
sidebar_position: 92
---
While we've only drawn the end of one strap, it's pretty obvious they overlap,
@ -11,9 +11,10 @@ Specifically, we're going to rotate our strap out of the way until it no longer
The rest of our bib should stay as it is, so let's start by making a list of points we need
to rotate.
However, there is a catch.
However, there is a catch.
## Macros and auto-generated IDs
We have used the `round` macro to help us round the corners
of our strap, and it added a bunch of auto-generated points to our pattern. We need to
rotate these points too, but what are their names?
@ -49,7 +50,7 @@ that return value, but if we did, it would look like this:
```
Those names aren't very handy to remember. So I will rewrite this code a bit to
we'll capture these return values from the `round` macros and create
we'll capture these return values from the `round` macros and create
easy-to-remember points from them:
<Example tutorial caption="It looks the same as before, but now those macro points are accessible to us">
@ -68,36 +69,38 @@ function draftBib({
part,
}) {
/*
* Construct the quarter neck opening
*/
/\*
- Construct the quarter neck opening
_/
let tweak = 1
let target = (measurements.head * options.neckRatio) /4
let target = (measurements.head _ options.neckRatio) /4
let delta
do {
points.right = new Point(
tweak * measurements.head / 10,
0
)
points.bottom = new Point(
0,
tweak * measurements.head / 12
)
points.right = new Point(
tweak _ measurements.head / 10,
0
)
points.bottom = new Point(
0,
tweak _ measurements.head / 12
)
points.rightCp1 = points.right.shift(
90,
90,
points.bottom.dy(points.right) / 2
)
points.bottomCp2 = points.bottom.shift(
0,
0,
points.bottom.dx(points.right) / 2
)
paths.quarterNeck = new Path()
.move(points.right)
.curve(
points.rightCp1,
points.bottomCp2,
points.rightCp1,
points.bottomCp2,
points.bottom
)
.hide()
@ -105,11 +108,13 @@ function draftBib({
delta = paths.quarterNeck.length() - target
if (delta > 0) tweak = tweak * 0.99
else tweak = tweak * 1.02
} while (Math.abs(delta) > 1)
/*
* Construct the complete neck opening
*/
} while (Math.abs(delta) > 1)
/\*
- Construct the complete neck opening
\*/
points.rightCp2 = points.rightCp1.flipY()
points.bottomCp1 = points.bottomCp2.flipX()
points.left = points.right.flipX()
@ -119,97 +124,103 @@ function draftBib({
points.topCp1 = points.bottomCp2.flipY()
points.topCp2 = points.bottomCp1.flipY()
paths.neck = new Path()
.move(points.top)
.curve(points.topCp2, points.leftCp1, points.left)
.curve(points.leftCp2, points.bottomCp1, points.bottom)
.curve(points.bottomCp2, points.rightCp1, points.right)
.curve(points.rightCp2, points.topCp1, points.top)
.close()
.addClass('fabric')
paths.neck = new Path()
.move(points.top)
.curve(points.topCp2, points.leftCp1, points.left)
.curve(points.leftCp2, points.bottomCp1, points.bottom)
.curve(points.bottomCp2, points.rightCp1, points.right)
.curve(points.rightCp2, points.topCp1, points.top)
.close()
.addClass('fabric')
/*
* Drawing the bib outline
*/
const width = measurements.head * options.widthRatio
const length = measurements.head * options.lengthRatio
/\*
points.topLeft = new Point(
width / -2,
points.top.y - (width / 2 - points.right.x)
)
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
- Drawing the bib outline
_/
const width = measurements.head _ options.widthRatio
const length = measurements.head \* options.lengthRatio
/*
* Shape the straps
*/
points.topLeft = new Point(
width / -2,
points.top.y - (width / 2 - points.right.x)
)
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
/\*
- Shape the straps
\*/
points.edgeLeft = new Point(points.topLeft.x, points.left.y)
points.edgeRight = new Point(points.topRight.x, points.right.y)
points.edgeTop = new Point(0, points.topLeft.y)
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
points.edgeRightCp = points.edgeLeftCp.flipX()
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(
points.topLeft,
0.5
)
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
points.edgeRightCp = points.edgeLeftCp.flipX()
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(
points.topLeft,
0.5
)
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
// Round the straps
const strap = points.edgeTop.dy(points.top)
// Round the straps
const strap = points.edgeTop.dy(points.top)
points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
points.tipRightBottom = new Point(points.tipRight.x, points.top.y)
points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
points.tipRightBottom = new Point(points.tipRight.x, points.top.y)
// highlight-start
/*
* Macros will return the auto-generated IDs
*/
// highlight-start
/\*
- Macros will return the auto-generated IDs
\*/
const ids1 = {
tipRightTop: macro("round", {
id: "tipRightTop",
from: points.edgeTop,
to: points.tipRight,
via: points.tipRightTop,
hide: false
}),
tipRightBottom: macro("round", {
id: "tipRightBottom",
from: points.tipRight,
to: points.top,
via: points.tipRightBottom,
hide: false
})
tipRightTop: macro("round", {
id: "tipRightTop",
from: points.edgeTop,
to: points.tipRight,
via: points.tipRightTop,
hide: false
}),
tipRightBottom: macro("round", {
id: "tipRightBottom",
from: points.tipRight,
to: points.top,
via: points.tipRightBottom,
hide: false
})
}
/*
* Create points from them with easy names
*/
/\*
- Create points from them with easy names
\*/
for (const side in ids1) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
}
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
}
}
// highlight-end
/*
* Now, adapt our `rect` path so it's no longer a rectangle:
*/
paths.rect = new Path()
.move(points.edgeTop)
.curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
.line(points.bottomLeft)
.line(points.bottomRight)
.line(points.edgeRight)
.curve(points.edgeRightCp, points.edgeTopRightCp, points.edgeTop)
.close()
return part
/\*
- Now, adapt our `rect` path so it's no longer a rectangle:
\*/
paths.rect = new Path()
.move(points.edgeTop)
.curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
.line(points.bottomLeft)
.line(points.bottomRight)
.line(points.edgeRight)
.curve(points.edgeRightCp, points.edgeTopRightCp, points.edgeTop)
.close()
return part
}
```
````
</Example>
Once we have our list of points to rotate, we can rotate them. How far? Until the strap no longer overlaps.
@ -236,28 +247,28 @@ function draftBib({
let delta
do {
points.right = new Point(
tweak * measurements.head / 10,
tweak * measurements.head / 10,
0
)
points.bottom = new Point(
0,
0,
tweak * measurements.head / 12
)
points.rightCp1 = points.right.shift(
90,
90,
points.bottom.dy(points.right) / 2
)
points.bottomCp2 = points.bottom.shift(
0,
0,
points.bottom.dx(points.right) / 2
)
paths.quarterNeck = new Path()
.move(points.right)
.curve(
points.rightCp1,
points.bottomCp2,
points.rightCp1,
points.bottomCp2,
points.bottom
)
.hide()
@ -266,7 +277,7 @@ function draftBib({
if (delta > 0) tweak = tweak * 0.99
else tweak = tweak * 1.02
} while (Math.abs(delta) > 1)
/*
* Construct the complete neck opening
*/
@ -422,5 +433,6 @@ function draftBib({
return part
}
```
````
</Example>

View file

@ -1,6 +1,6 @@
---
title: Completing the neck opening
order: 80
sidebar_position: 80
---
We've constructed the perfectly sized quarter neck, and we're going to use this
@ -25,36 +25,38 @@ function draftBib({
part,
}) {
/*
* Construct the quarter neck opening
*/
/\*
- Construct the quarter neck opening
_/
let tweak = 1
let target = (measurements.head * options.neckRatio) /4
let target = (measurements.head _ options.neckRatio) /4
let delta
do {
points.right = new Point(
tweak * measurements.head / 10,
0
)
points.bottom = new Point(
0,
tweak * measurements.head / 12
)
points.right = new Point(
tweak _ measurements.head / 10,
0
)
points.bottom = new Point(
0,
tweak _ measurements.head / 12
)
points.rightCp1 = points.right.shift(
90,
90,
points.bottom.dy(points.right) / 2
)
points.bottomCp2 = points.bottom.shift(
0,
0,
points.bottom.dx(points.right) / 2
)
paths.quarterNeck = new Path()
.move(points.right)
.curve(
points.rightCp1,
points.bottomCp2,
points.rightCp1,
points.bottomCp2,
points.bottom
)
// highlight-start
@ -64,11 +66,13 @@ function draftBib({
delta = paths.quarterNeck.length() - target
if (delta > 0) tweak = tweak * 0.99
else tweak = tweak * 1.02
} while (Math.abs(delta) > 1)
return part
} while (Math.abs(delta) > 1)
return part
}
```
````
</Example>
We're saying: _hide this path_. In other words, don't show it.
@ -105,28 +109,28 @@ function draftBib({
let delta
do {
points.right = new Point(
tweak * measurements.head / 10,
tweak * measurements.head / 10,
0
)
points.bottom = new Point(
0,
0,
tweak * measurements.head / 12
)
points.rightCp1 = points.right.shift(
90,
90,
points.bottom.dy(points.right) / 2
)
points.bottomCp2 = points.bottom.shift(
0,
0,
points.bottom.dx(points.right) / 2
)
paths.quarterNeck = new Path()
.move(points.right)
.curve(
points.rightCp1,
points.bottomCp2,
points.rightCp1,
points.bottomCp2,
points.bottom
)
.hide()
@ -160,11 +164,12 @@ function draftBib({
// highlight-end
return part
}
```
````
</Example>
To add the points, we're using the `Point.flipX()` and `Point.flipY()` methods
here. There's a few new Path methods too, like `close()` and `addClass()`.
here. There's a few new Path methods too, like `close()` and `addClass()`.
Perhaps you can figure out what they do? If not, both [the Point
documentation](/reference/api/point/) and [the Path

View file

@ -1,6 +1,6 @@
---
title: Conclusion (of part 2)
order: 99
sidebar_position: 99
---
You made it to the end of part 2, in which we got to do some real parametric design.
@ -14,7 +14,7 @@ way.
You've also learned how to draw paths, which are the lines and curves that make up our pattern.
And we've used macros which can help us with repetitive tasks.
What we've gotten so far is a perfectly suitable sewing pattern. You can print this,
What we've gotten so far is a perfectly suitable sewing pattern. You can print this,
and make a nice bib out of it.
But when we stick to these basics, FreeSewing doesn't really get a chance to shine.

View file

@ -1,6 +1,6 @@
---
title: Constructing the neck opening
order: 60
sidebar_position: 60
---
Our goal is to construct an oval neck opening that has a circumference
@ -51,39 +51,41 @@ function draftBib({
}) {
// highlight-start
/*
* Construct the quarter neck opening
*/
/\*
- Construct the quarter neck opening
\*/
points.right = new Point(
measurements.head / 10,
0
measurements.head / 10,
0
)
points.bottom = new Point(
0,
measurements.head / 12
0,
measurements.head / 12
)
points.rightCp1 = points.right.shift(
90,
points.bottom.dy(points.right) / 2
)
points.bottomCp2 = points.bottom.shift(
0,
points.bottom.dx(points.right) / 2
)
points.rightCp1 = points.right.shift(
90,
points.bottom.dy(points.right) / 2
)
points.bottomCp2 = points.bottom.shift(
0,
points.bottom.dx(points.right) / 2
)
paths.quarterNeck = new Path()
.move(points.right)
.curve(
points.rightCp1,
points.bottomCp2,
points.bottom
)
paths.quarterNeck = new Path()
.move(points.right)
.curve(
points.rightCp1,
points.bottomCp2,
points.bottom
)
// highlight-end
return part
return part
}
```
````
</Example>
We've added some points to our part, and drawn our first path.
@ -93,10 +95,10 @@ Let's look at each line in detail.
```js
points.right = new Point(
measurements.head / 10,
measurements.head / 10,
0
)
```
````
- We're adding a point named `right` to the `points` object which holds our
part's points
@ -108,10 +110,7 @@ points.right = new Point(
The creation of `points.bottom` is very similar, so let's skip to the next line:
```js
points.rightCp1 = points.right.shift(
90,
points.bottom.dy(points.right) / 2
)
points.rightCp1 = points.right.shift(90, points.bottom.dy(points.right) / 2)
```
- We're adding a point named `rightCp1`, which will become the _control point_
@ -130,7 +129,9 @@ the right (0 degrees) for half of the X-delta between points `bottom` and
`right`.
:::tip
##### Further reading
The `Point.shift()` and `Point.dy()` are just the tip of the iceberg.
Points come with a bunch of these methods.
You can find them all in [the Point API docs](/reference/api/point/).
@ -144,11 +145,7 @@ introduced on the next line: Paths.
```js
paths.quarterNeck = new Path()
.move(points.right)
.curve(
points.rightCp1,
points.bottomCp2,
points.bottom
)
.curve(points.rightCp1, points.bottomCp2, points.bottom)
```
- We're adding a path named `quarterNeck` to the `paths` object which holds our
@ -166,7 +163,7 @@ From there, we drew a cubic Bézier curve to our `bottom` point by using
:::tip
Many of the methods in the FreeSewing API are *chainable* allowing you
Many of the methods in the FreeSewing API are _chainable_ allowing you
to string them together like in this example.
:::

View file

@ -1,6 +1,6 @@
---
title: Creating the closure
order: 91
sidebar_position: 91
---
Things are starting to look good, but we can't fit the bib over the baby's head like this.
@ -60,28 +60,27 @@ function draftBib({
tweak _ measurements.head / 12
)
points.rightCp1 = points.right.shift(
90,
points.bottom.dy(points.right) / 2
)
points.bottomCp2 = points.bottom.shift(
0,
points.bottom.dx(points.right) / 2
)
points.rightCp1 = points.right.shift(
90,
points.bottom.dy(points.right) / 2
)
points.bottomCp2 = points.bottom.shift(
0,
points.bottom.dx(points.right) / 2
)
paths.quarterNeck = new Path()
.move(points.right)
.curve(
points.rightCp1,
points.bottomCp2,
points.bottom
)
.hide()
paths.quarterNeck = new Path()
.move(points.right)
.curve(
points.rightCp1,
points.bottomCp2,
points.bottom
)
.hide()
delta = paths.quarterNeck.length() - target
if (delta > 0) tweak = tweak * 0.99
else tweak = tweak * 1.02
delta = paths.quarterNeck.length() - target
if (delta > 0) tweak = tweak _ 0.99
else tweak = tweak _ 1.02
} while (Math.abs(delta) > 1)

View file

@ -1,6 +1,6 @@
---
title: A part's draft method
order: 50
sidebar_position: 50
---
Time to turn our attention to the draft method of our part.

View file

@ -1,6 +1,6 @@
---
title: Drawing the bib outline
order: 88
sidebar_position: 88
---
With our neck opening in place, let us draw the basic outline of our bib.
@ -17,46 +17,47 @@ function draftBib({
part,
}) {
// Construct the quarter neck opening
let tweak = 1
let target = (measurements.head * options.neckRatio) /4
let delta
do {
points.right = new Point(
tweak * measurements.head / 10,
0
)
points.bottom = new Point(
0,
tweak * measurements.head / 12
)
// Construct the quarter neck opening
let tweak = 1
let target = (measurements.head _ options.neckRatio) /4
let delta
do {
points.right = new Point(
tweak _ measurements.head / 10,
0
)
points.bottom = new Point(
0,
tweak \* measurements.head / 12
)
points.rightCp1 = points.right.shift(
90,
90,
points.bottom.dy(points.right) / 2
)
points.bottomCp2 = points.bottom.shift(
0,
0,
points.bottom.dx(points.right) / 2
)
paths.quarterNeck = new Path()
.move(points.right)
.curve(
points.rightCp1,
points.bottomCp2,
points.bottom
)
.hide()
paths.quarterNeck = new Path()
.move(points.right)
.curve(
points.rightCp1,
points.bottomCp2,
points.bottom
)
.hide()
delta = paths.quarterNeck.length() - target
if (delta > 0) tweak = tweak * 0.99
else tweak = tweak * 1.02
} while (Math.abs(delta) > 1)
delta = paths.quarterNeck.length() - target
if (delta > 0) tweak = tweak _ 0.99
else tweak = tweak _ 1.02
} while (Math.abs(delta) > 1)
/*
* Construct the complete neck opening
*/
/\*
- Construct the complete neck opening
\*/
points.rightCp2 = points.rightCp1.flipY()
points.bottomCp1 = points.bottomCp2.flipX()
points.left = points.right.flipX()
@ -66,43 +67,45 @@ function draftBib({
points.topCp1 = points.bottomCp2.flipY()
points.topCp2 = points.bottomCp1.flipY()
paths.neck = new Path()
.move(points.top)
.curve(points.topCp2, points.leftCp1, points.left)
.curve(points.leftCp2, points.bottomCp1, points.bottom)
.curve(points.bottomCp2, points.rightCp1, points.right)
.curve(points.rightCp2, points.topCp1, points.top)
.close()
.addClass('fabric')
paths.neck = new Path()
.move(points.top)
.curve(points.topCp2, points.leftCp1, points.left)
.curve(points.leftCp2, points.bottomCp1, points.bottom)
.curve(points.bottomCp2, points.rightCp1, points.right)
.curve(points.rightCp2, points.topCp1, points.top)
.close()
.addClass('fabric')
// highlight-start
/*
* Drawing the bib outline
*/
const width = measurements.head * options.widthRatio
const length = measurements.head * options.lengthRatio
/\*
points.topLeft = new Point(
width / -2,
points.top.y - (width / 2 - points.right.x)
)
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
- Drawing the bib outline
_/
const width = measurements.head _ options.widthRatio
const length = measurements.head \* options.lengthRatio
paths.rect = new Path()
.move(points.topLeft)
.line(points.bottomLeft)
.line(points.bottomRight)
.line(points.topRight)
.line(points.topLeft)
.close()
.addClass('fabric')
points.topLeft = new Point(
width / -2,
points.top.y - (width / 2 - points.right.x)
)
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
paths.rect = new Path()
.move(points.topLeft)
.line(points.bottomLeft)
.line(points.bottomRight)
.line(points.topRight)
.line(points.topLeft)
.close()
.addClass('fabric')
// highlight-end
return part
return part
}
```
````
</Example>
First thing we did was create the `width` and `length` variables to
@ -111,7 +114,7 @@ save ourselves some typing:
```js
const width = measurements.head * options.widthRatio
const length = measurements.head * options.lengthRatio
```
````
Both the length and width of our bib are a factor of the head circumference.
This way, our bib size will adapt to the size of the baby, and the user can tweak
@ -120,10 +123,7 @@ the length and width by playing with the options we added to the pattern.
Once we have our variables, we're adding some new points, and a second path called `rect`.
```js
points.topLeft = new Point(
width / -2,
points.top.y - (width / 2 - points.right.x)
)
points.topLeft = new Point(width / -2, points.top.y - (width / 2 - points.right.x))
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
@ -142,4 +142,3 @@ We're calculating the `topLeft` point so that the top edge of our bib
and the sides are equidistant from the neck opening.
We didn't have to do that. But it looks nicely balanced this way.

View file

@ -1,6 +1,6 @@
---
title: Drawing the straps
order: 93
sidebar_position: 93
---
All we have to do now is flip a bunch of points on the other side,
@ -28,36 +28,38 @@ function draftBib({
part,
}) {
/*
* Construct the neck opening
*/
/\*
- Construct the neck opening
_/
let tweak = 1
let target = (measurements.head * options.neckRatio) /4
let target = (measurements.head _ options.neckRatio) /4
let delta
do {
points.right = new Point(
tweak * measurements.head / 10,
0
)
points.bottom = new Point(
0,
tweak * measurements.head / 12
)
points.right = new Point(
tweak _ measurements.head / 10,
0
)
points.bottom = new Point(
0,
tweak _ measurements.head / 12
)
points.rightCp1 = points.right.shift(
90,
90,
points.bottom.dy(points.right) / 2
)
points.bottomCp2 = points.bottom.shift(
0,
0,
points.bottom.dx(points.right) / 2
)
paths.quarterNeck = new Path()
.move(points.right)
.curve(
points.rightCp1,
points.bottomCp2,
points.rightCp1,
points.bottomCp2,
points.bottom
)
.hide()
@ -65,11 +67,13 @@ function draftBib({
delta = paths.quarterNeck.length() - target
if (delta > 0) tweak = tweak * 0.99
else tweak = tweak * 1.02
} while (Math.abs(delta) > 1)
/*
* Construct the complete neck opening
*/
} while (Math.abs(delta) > 1)
/\*
- Construct the complete neck opening
\*/
points.rightCp2 = points.rightCp1.flipY()
points.bottomCp1 = points.bottomCp2.flipX()
points.left = points.right.flipX()
@ -79,233 +83,239 @@ function draftBib({
points.topCp1 = points.bottomCp2.flipY()
points.topCp2 = points.bottomCp1.flipY()
// strikeout-start
/* Remove this path
paths.neck = new Path()
.move(points.top)
.curve(points.topCp2, points.leftCp1, points.left)
.curve(points.leftCp2, points.bottomCp1, points.bottom)
.curve(points.bottomCp2, points.rightCp1, points.right)
.curve(points.rightCp2, points.topCp1, points.top)
.close()
.addClass('fabric')
*/
// strikeout-end
// strikeout-start
/_ Remove this path
paths.neck = new Path()
.move(points.top)
.curve(points.topCp2, points.leftCp1, points.left)
.curve(points.leftCp2, points.bottomCp1, points.bottom)
.curve(points.bottomCp2, points.rightCp1, points.right)
.curve(points.rightCp2, points.topCp1, points.top)
.close()
.addClass('fabric')
_/
// strikeout-end
// Drawing the bib outline
const width = measurements.head * options.widthRatio
const length = measurements.head * options.lengthRatio
// Drawing the bib outline
const width = measurements.head _ options.widthRatio
const length = measurements.head _ options.lengthRatio
points.topLeft = new Point(
width / -2,
points.top.y - (width / 2 - points.right.x)
)
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
points.topLeft = new Point(
width / -2,
points.top.y - (width / 2 - points.right.x)
)
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
/*
* Shape the straps
*/
/\*
- Shape the straps
\*/
points.edgeLeft = new Point(points.topLeft.x, points.left.y)
points.edgeRight = new Point(points.topRight.x, points.right.y)
points.edgeTop = new Point(0, points.topLeft.y)
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
points.edgeRightCp = points.edgeLeftCp.flipX()
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(
points.topLeft,
0.5
)
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
points.edgeRightCp = points.edgeLeftCp.flipX()
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(
points.topLeft,
0.5
)
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
// Round the straps
const strap = points.edgeTop.dy(points.top)
// Round the straps
const strap = points.edgeTop.dy(points.top)
points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
points.tipRightBottom = new Point(points.tipRight.x, points.top.y)
points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
points.tipRightBottom = new Point(points.tipRight.x, points.top.y)
/*
* Macros will return the auto-generated IDs
*/
/\*
- Macros will return the auto-generated IDs
_/
const ids1 = {
tipRightTop: macro("round", {
id: "tipRightTop",
from: points.edgeTop,
to: points.tipRight,
via: points.tipRightTop,
// strikeout-start
/* Remove this to have the macro
* only create the points we need
* and not draw a path
hide: false
*/
// strikeout-end
}),
tipRightBottom: macro("round", {
id: "tipRightBottom",
from: points.tipRight,
to: points.top,
via: points.tipRightBottom,
// strikeout-start
/* Remove this to have the macro
* only create the points we need
* and not draw a path
hide: false
*/
// strikeout-end
})
}
/*
* Create points from them with easy names
*/
for (const side in ids1) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
}
}
/*
* This is the list of points we need to rotate
* to move our strap out of the way
*/
const rotateThese = [
"edgeTopLeftCp",
"edgeTop",
"tipRight",
"tipRightTop",
"tipRightTopStart",
"tipRightTopCp1",
"tipRightTopCp2",
"tipRightTopEnd",
"tipRightBottomStart",
"tipRightBottomCp1",
"tipRightBottomCp2",
"tipRightBottomEnd",
"tipRightBottom",
"top",
"topCp2"
]
/*
* We're rotating all the points in
* the `rotateThese` array around
* the `edgeLeft` point.
*
* We're using increments of 1 degree
* until the `tipRightBottomStart` point
* is 1 mm beyond the center of our bib.
*/
while (points.tipRightBottomStart.x > -1) {
for (const p of rotateThese) points[p] = points[p].rotate(1, points.edgeLeft)
}
tipRightTop: macro("round", {
id: "tipRightTop",
from: points.edgeTop,
to: points.tipRight,
via: points.tipRightTop,
// strikeout-start
/* Remove this repetition
macro("round", {
from: points.edgeTop,
to: points.tipRight,
via: points.tipRightTop,
prefix: "tipRightTop",
hide: false,
class: 'contrast dotted',
})
macro("round", {
from: points.tipRight,
to: points.top,
via: points.tipRightBottom,
prefix: "tipRightBottom",
hide: false,
class: 'contrast dotted',
})
paths.rect = new Path()
.move(points.edgeTop)
.curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
.line(points.bottomLeft)
.line(points.bottomRight)
.line(points.edgeRight)
.curve(points.edgeRightCp, points.edgeTopRightCp, points.edgeTop)
.close()
*/
/_ Remove this to have the macro
_ only create the points we need
_ and not draw a path
hide: false
_/
// strikeout-end
}),
tipRightBottom: macro("round", {
id: "tipRightBottom",
from: points.tipRight,
to: points.top,
via: points.tipRightBottom,
// strikeout-start
/_ Remove this to have the macro
_ only create the points we need
_ and not draw a path
hide: false
\*/
// strikeout-end
})
}
// highlight-start
// Add points for second strap
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.topCp1 = points.topCp2.flipX()
points.tipLeftTopStart = points.tipRightTopStart.flipX()
points.tipLeftTopCp1 = points.tipRightTopCp1.flipX()
points.tipLeftTopCp2 = points.tipRightTopCp2.flipX()
points.tipLeftTopEnd = points.tipRightTopEnd.flipX()
points.tipLeftBottomStart = points.tipRightBottomStart.flipX()
points.tipLeftBottomCp1 = points.tipRightBottomCp1.flipX()
points.tipLeftBottomCp2 = points.tipRightBottomCp2.flipX()
points.tipLeftBottomEnd = points.tipRightBottomEnd.flipX()
/\*
// Create one path for the bib outline
paths.seam = new Path()
.move(points.edgeLeft)
.line(points.bottomLeft)
.line(points.bottomRight)
.line(points.edgeRight)
.curve(
points.edgeRightCp,
points.edgeTopRightCp,
points.tipLeftTopStart
)
.curve(
points.tipLeftTopCp1,
points.tipLeftTopCp2,
points.tipLeftTopEnd
)
.curve(
points.tipLeftBottomCp1,
points.tipLeftBottomCp2,
points.tipLeftBottomEnd
)
.curve(
points.topCp1,
points.rightCp2,
points.right
)
.curve(
points.rightCp1,
points.bottomCp2,
points.bottom
)
.curve(
points.bottomCp1,
points.leftCp2,
points.left
)
.curve(
points.leftCp1,
points.topCp2,
points.tipRightBottomEnd
)
.curve(
points.tipRightBottomCp2,
points.tipRightBottomCp1,
points.tipRightBottomStart
)
.curve(
points.tipRightTopCp2,
points.tipRightTopCp1,
points.tipRightTopStart
)
.curve(
points.edgeTopLeftCp,
points.edgeLeftCp,
points.edgeLeft
)
.close()
.addClass("fabric")
// highlight-end
- Create points from them with easy names
\*/
for (const side in ids1) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
}
}
return part
/\*
- This is the list of points we need to rotate
- to move our strap out of the way
\*/
const rotateThese = [
"edgeTopLeftCp",
"edgeTop",
"tipRight",
"tipRightTop",
"tipRightTopStart",
"tipRightTopCp1",
"tipRightTopCp2",
"tipRightTopEnd",
"tipRightBottomStart",
"tipRightBottomCp1",
"tipRightBottomCp2",
"tipRightBottomEnd",
"tipRightBottom",
"top",
"topCp2"
]
/\*
_ We're rotating all the points in
_ the `rotateThese` array around
_ the `edgeLeft` point.
_
_ We're using increments of 1 degree
_ until the `tipRightBottomStart` point
_ is 1 mm beyond the center of our bib.
_/
while (points.tipRightBottomStart.x > -1) {
for (const p of rotateThese) points[p] = points[p].rotate(1, points.edgeLeft)
}
// strikeout-start
/\* Remove this repetition
macro("round", {
from: points.edgeTop,
to: points.tipRight,
via: points.tipRightTop,
prefix: "tipRightTop",
hide: false,
class: 'contrast dotted',
})
macro("round", {
from: points.tipRight,
to: points.top,
via: points.tipRightBottom,
prefix: "tipRightBottom",
hide: false,
class: 'contrast dotted',
})
paths.rect = new Path()
.move(points.edgeTop)
.curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
.line(points.bottomLeft)
.line(points.bottomRight)
.line(points.edgeRight)
.curve(points.edgeRightCp, points.edgeTopRightCp, points.edgeTop)
.close()
\*/
// strikeout-end
// highlight-start
// Add points for second strap
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.topCp1 = points.topCp2.flipX()
points.tipLeftTopStart = points.tipRightTopStart.flipX()
points.tipLeftTopCp1 = points.tipRightTopCp1.flipX()
points.tipLeftTopCp2 = points.tipRightTopCp2.flipX()
points.tipLeftTopEnd = points.tipRightTopEnd.flipX()
points.tipLeftBottomStart = points.tipRightBottomStart.flipX()
points.tipLeftBottomCp1 = points.tipRightBottomCp1.flipX()
points.tipLeftBottomCp2 = points.tipRightBottomCp2.flipX()
points.tipLeftBottomEnd = points.tipRightBottomEnd.flipX()
// Create one path for the bib outline
paths.seam = new Path()
.move(points.edgeLeft)
.line(points.bottomLeft)
.line(points.bottomRight)
.line(points.edgeRight)
.curve(
points.edgeRightCp,
points.edgeTopRightCp,
points.tipLeftTopStart
)
.curve(
points.tipLeftTopCp1,
points.tipLeftTopCp2,
points.tipLeftTopEnd
)
.curve(
points.tipLeftBottomCp1,
points.tipLeftBottomCp2,
points.tipLeftBottomEnd
)
.curve(
points.topCp1,
points.rightCp2,
points.right
)
.curve(
points.rightCp1,
points.bottomCp2,
points.bottom
)
.curve(
points.bottomCp1,
points.leftCp2,
points.left
)
.curve(
points.leftCp1,
points.topCp2,
points.tipRightBottomEnd
)
.curve(
points.tipRightBottomCp2,
points.tipRightBottomCp1,
points.tipRightBottomStart
)
.curve(
points.tipRightTopCp2,
points.tipRightTopCp1,
points.tipRightTopStart
)
.curve(
points.edgeTopLeftCp,
points.edgeLeftCp,
points.edgeLeft
)
.close()
.addClass("fabric")
// highlight-end
return part
}
```
</Example>
```

View file

@ -1,10 +1,10 @@
---
title: Fitting the neck opening
order: 70
sidebar_position: 70
---
We are not going to create some opening that we _hope_ is the right size, we're
going to make sure it is. Here's how we'll make sure the neck opening is _just
going to make sure it is. Here's how we'll make sure the neck opening is _just
right_:
<Example tutorial caption="It might look the same as before, but now it's just right">
@ -19,54 +19,56 @@ function draftBib({
part,
}) {
/*
* Construct the quarter neck opening
*/
// highlight-start
/\*
- Construct the quarter neck opening
_/
// highlight-start
let tweak = 1
let target = (measurements.head * options.neckRatio) /4
let target = (measurements.head _ options.neckRatio) /4
let delta
do {
// highlight-end
points.right = new Point(
// highlight-start
tweak * measurements.head / 10,
// highlight-end
0
)
points.bottom = new Point(
0,
// highlight-start
tweak * measurements.head / 12
// highlight-end
)
// highlight-end
points.right = new Point(
// highlight-start
tweak _ measurements.head / 10,
// highlight-end
0
)
points.bottom = new Point(
0,
// highlight-start
tweak _ measurements.head / 12
// highlight-end
)
points.rightCp1 = points.right.shift(
90,
90,
points.bottom.dy(points.right) / 2
)
points.bottomCp2 = points.bottom.shift(
0,
0,
points.bottom.dx(points.right) / 2
)
paths.quarterNeck = new Path()
.move(points.right)
.curve(
points.rightCp1,
points.bottomCp2,
points.rightCp1,
points.bottomCp2,
points.bottom
)
// highlight-start
delta = paths.quarterNeck.length() - target
if (delta > 0) tweak = tweak * 0.99
else tweak = tweak * 1.02
} while (Math.abs(delta) > 1)
delta = paths.quarterNeck.length() - target
if (delta > 0) tweak = tweak _ 0.99
else tweak = tweak _ 1.02
} while (Math.abs(delta) > 1)
// highlight-end
return part
return part
}
```
</Example>
@ -94,3 +96,4 @@ are within 1 mm of our target value.
Now that we're happy with the length of our quarter neck opening, let's
complete the entire neck opening.
```

View file

@ -1,14 +1,14 @@
---
title: Creating a new design
order: 10
sidebar_position: 10
---
The development environment has already setup various designs for us.
Since I am using the **From scratch** template, the files I want to edit live
Since I am using the **From scratch** template, the files I want to edit live
in `design/from-scratch`.
The design's main file is `design/from-scratch/src/index.mjs`, and our bib part
will live in `design/from-scratch/src/bib.mjs`.
will live in `design/from-scratch/src/bib.mjs`.
This `bib.mjs` file is where will be doing most of the work here in part 2 of this
tutorial. But let's start with the `index.mjs` file as an appetizer, because this
@ -37,7 +37,7 @@ Not too intimidating, is it?
## Imports
At the top of the file, we have a bunch of *imports*:
At the top of the file, we have a bunch of _imports_:
```src/index.mjs
import { Design } from '@freesewing/core'
@ -48,13 +48,13 @@ import { bib } from './bib.mjs'
An `import` is how JavaScript loads code from a different file. \
It essentially says: _import **something** from **somewhere**_:
| Something | Somewhere | Description |
| ---------:|:--------- |:----------- |
| `Design` | `@freesewing/core` | Loads the `Design` constructor from FreeSewing's core library |
| `i18n` | `../i18n/index.mjs` | Loads `i18n` from the `index.mjs` file in the `i18n` one level higher (these are the translations) |
| `bib` | `./bib.mjs` | Loads `bib` from the `bib.mjs` file in the same folder (this is our part) |
| Something | Somewhere | Description |
| --------: | :------------------ | :------------------------------------------------------------------------------------------------- |
| `Design` | `@freesewing/core` | Loads the `Design` constructor from FreeSewing's core library |
| `i18n` | `../i18n/index.mjs` | Loads `i18n` from the `index.mjs` file in the `i18n` one level higher (these are the translations) |
| `bib` | `./bib.mjs` | Loads `bib` from the `bib.mjs` file in the same folder (this is our part) |
As you can see, the *somewhere* can be different things. A local file like in
As you can see, the _somewhere_ can be different things. A local file like in
lines 2 and 3, or a package published on
[NPM](https://www.npmjs.com/package/@freesewing/core), in line 1.
@ -82,7 +82,7 @@ If you are not familiar with this syntax, you'll get the hang of it soon enough.
## Design constructor
Finally, the most interesting part of this file is the middle part where we are
Finally, the most interesting part of this file is the middle part where we are
creating a new design:
```src/index.mjs
@ -95,7 +95,7 @@ const FromScratch = new Design({
})
```
The `Design` that we imported on line 1 is a so-called **constructor**.
The `Design` that we imported on line 1 is a so-called **constructor**.
A constructor is a function that can create things out of nothing. Or,
to be more accurate, that you can use with the `new` keyword.
@ -105,8 +105,8 @@ It's a convention that constructor names start with an **C**apital letter.
:::
We are passing some info to this `Design` function, but the `data` we are
passing is optional. If we strip that away for a moment, and don't bother
assigning the result to a variable, we reveal the essence of what it takes to
passing is optional. If we strip that away for a moment, and don't bother
assigning the result to a variable, we reveal the essence of what it takes to
create a new FreeSewing design:
```src/index.mjs
@ -115,8 +115,8 @@ new Design({
})
```
In several places in this documentation, I will mention that *a design is not
much more than a thin wrapper around parts*. But I feel nothing drives
In several places in this documentation, I will mention that _a design is not
much more than a thin wrapper around parts_. But I feel nothing drives
that point home like seeing it in code like this.
To create a new design, we call the `Design` constructor and give it a list
@ -124,9 +124,8 @@ To create a new design, we call the `Design` constructor and give it a list
:::note RELATED
Refer to [the design reference documentation](/reference/api/design) for
Refer to [the design reference documentation](/reference/api/design) for
all details about what you can pass to the Design constructor.
:::
That's it. So let's look at where the real action is next: Our first part.

View file

@ -1,6 +1,6 @@
---
title: Creating a part
order: 20
sidebar_position: 20
---
Much like garments themselves, patterns are made up of _parts_.
@ -9,14 +9,14 @@ so on. The pattern you create today is very simple, and only has one part: the b
:::tip
It's a good idea to keep each part in its own file. You don't *have to* do
this, but it's a good habit to get into.
It's a good idea to keep each part in its own file. You don't _have to_ do
this, but it's a good habit to get into.
:::
## bib.mjs
I am going to use the **From scratch** template. So the files I want to edit live
I am going to use the **From scratch** template. So the files I want to edit live
in `design/from-scratch`.
Our part lives in `design/from-scratch/src/bib.mjs`, and it currently looks like this:
@ -41,8 +41,8 @@ The only mandatory keys on a part object are `name` and `draft`.
:::note RELATED
Refer to [the part reference documentation](/reference/api/part) for
all details about configuring the part object
Refer to [the part reference documentation](/reference/api/part) for
all details about configuring the part object
:::
:::note
@ -52,7 +52,6 @@ Each parts stands on its own, and parts from various designs can be combined
to create other designs.
:::
### The part name
```src/bib.mjs

View file

@ -1,6 +1,6 @@
---
title: Rounding the corners
order: 94
sidebar_position: 94
---
We already know how to round corners, we'll have the `round` macro take care of that for us.
@ -22,36 +22,38 @@ function draftBib({
part,
}) {
/*
* Construct the quarter neck opening
*/
/\*
- Construct the quarter neck opening
_/
let tweak = 1
let target = (measurements.head * options.neckRatio) /4
let target = (measurements.head _ options.neckRatio) /4
let delta
do {
points.right = new Point(
tweak * measurements.head / 10,
0
)
points.bottom = new Point(
0,
tweak * measurements.head / 12
)
points.right = new Point(
tweak _ measurements.head / 10,
0
)
points.bottom = new Point(
0,
tweak _ measurements.head / 12
)
points.rightCp1 = points.right.shift(
90,
90,
points.bottom.dy(points.right) / 2
)
points.bottomCp2 = points.bottom.shift(
0,
0,
points.bottom.dx(points.right) / 2
)
paths.quarterNeck = new Path()
.move(points.right)
.curve(
points.rightCp1,
points.bottomCp2,
points.rightCp1,
points.bottomCp2,
points.bottom
)
.hide()
@ -59,11 +61,13 @@ function draftBib({
delta = paths.quarterNeck.length() - target
if (delta > 0) tweak = tweak * 0.99
else tweak = tweak * 1.02
} while (Math.abs(delta) > 1)
/*
* Construct the complete neck opening
*/
} while (Math.abs(delta) > 1)
/\*
- Construct the complete neck opening
\*/
points.rightCp2 = points.rightCp1.flipY()
points.bottomCp1 = points.bottomCp2.flipX()
points.left = points.right.flipX()
@ -73,216 +77,225 @@ function draftBib({
points.topCp1 = points.bottomCp2.flipY()
points.topCp2 = points.bottomCp1.flipY()
// Drawing the bib outline
const width = measurements.head * options.widthRatio
const length = measurements.head * options.lengthRatio
// Drawing the bib outline
const width = measurements.head _ options.widthRatio
const length = measurements.head _ options.lengthRatio
points.topLeft = new Point(
width / -2,
points.top.y - (width / 2 - points.right.x)
)
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
points.topLeft = new Point(
width / -2,
points.top.y - (width / 2 - points.right.x)
)
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
/*
* Shape the straps
*/
/\*
- Shape the straps
\*/
points.edgeLeft = new Point(points.topLeft.x, points.left.y)
points.edgeRight = new Point(points.topRight.x, points.right.y)
points.edgeTop = new Point(0, points.topLeft.y)
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
points.edgeRightCp = points.edgeLeftCp.flipX()
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(
points.topLeft,
0.5
)
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
points.edgeRightCp = points.edgeLeftCp.flipX()
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(
points.topLeft,
0.5
)
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
// Round the straps
const strap = points.edgeTop.dy(points.top)
// Round the straps
const strap = points.edgeTop.dy(points.top)
points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
points.tipRightBottom = new Point(points.tipRight.x, points.top.y)
points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
points.tipRightBottom = new Point(points.tipRight.x, points.top.y)
/*
* Macros will return the auto-generated IDs
*/
/\*
- Macros will return the auto-generated IDs
\*/
const ids1 = {
tipRightTop: macro("round", {
id: "tipRightTop",
from: points.edgeTop,
to: points.tipRight,
via: points.tipRightTop,
}),
tipRightBottom: macro("round", {
id: "tipRightBottom",
from: points.tipRight,
to: points.top,
via: points.tipRightBottom,
})
tipRightTop: macro("round", {
id: "tipRightTop",
from: points.edgeTop,
to: points.tipRight,
via: points.tipRightTop,
}),
tipRightBottom: macro("round", {
id: "tipRightBottom",
from: points.tipRight,
to: points.top,
via: points.tipRightBottom,
})
}
/*
* Create points from them with easy names
*/
/\*
- Create points from them with easy names
\*/
for (const side in ids1) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
}
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
}
}
/*
* This is the list of points we need to rotate
* to move our strap out of the way
*/
- This is the list of points we need to rotate
- to move our strap out of the way
_/
const rotateThese = [
"edgeTopLeftCp",
"edgeTop",
"tipRight",
"tipRightTop",
"tipRightTopStart",
"tipRightTopCp1",
"tipRightTopCp2",
"tipRightTopEnd",
"tipRightBottomStart",
"tipRightBottomCp1",
"tipRightBottomCp2",
"tipRightBottomEnd",
"tipRightBottom",
"top",
"topCp2"
"edgeTopLeftCp",
"edgeTop",
"tipRight",
"tipRightTop",
"tipRightTopStart",
"tipRightTopCp1",
"tipRightTopCp2",
"tipRightTopEnd",
"tipRightBottomStart",
"tipRightBottomCp1",
"tipRightBottomCp2",
"tipRightBottomEnd",
"tipRightBottom",
"top",
"topCp2"
]
/*
* We're rotating all the points in
* the `rotateThese` array around
* the `edgeLeft` point.
*
* We're using increments of 1 degree
* until the `tipRightBottomStart` point
* is 1 mm beyond the center of our bib.
*/
while (points.tipRightBottomStart.x > -1) {
/_
- We're rotating all the points in
- the `rotateThese` array around
- the `edgeLeft` point.
-
- We're using increments of 1 degree
- until the `tipRightBottomStart` point
- is 1 mm beyond the center of our bib.
\*/
while (points.tipRightBottomStart.x > -1) {
for (const p of rotateThese) points[p] = points[p].rotate(1, points.edgeLeft)
}
// Add points for second strap
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.topCp1 = points.topCp2.flipX()
points.tipLeftTopStart = points.tipRightTopStart.flipX()
points.tipLeftTopCp1 = points.tipRightTopCp1.flipX()
points.tipLeftTopCp2 = points.tipRightTopCp2.flipX()
points.tipLeftTopEnd = points.tipRightTopEnd.flipX()
points.tipLeftBottomStart = points.tipRightBottomStart.flipX()
points.tipLeftBottomCp1 = points.tipRightBottomCp1.flipX()
points.tipLeftBottomCp2 = points.tipRightBottomCp2.flipX()
points.tipLeftBottomEnd = points.tipRightBottomEnd.flipX()
// highlight-start
/*
* Round the bottom corners
* Macros will return the auto-generated IDs
*/
const ids2 = {
bottomLeft: macro("round", {
id: "bottomLeft",
from: points.topLeft,
to: points.bottomRight,
via: points.bottomLeft,
radius: points.bottomRight.x / 4,
}),
bottomRight: macro("round", {
id: "bottomRight",
from: points.bottomLeft,
to: points.topRight,
via: points.bottomRight,
radius: points.bottomRight.x / 4,
})
}
/*
* Create points from them with easy names
*/
for (const side in ids2) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids2[side].points[id]].copy()
}
// Add points for second strap
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.topCp1 = points.topCp2.flipX()
points.tipLeftTopStart = points.tipRightTopStart.flipX()
points.tipLeftTopCp1 = points.tipRightTopCp1.flipX()
points.tipLeftTopCp2 = points.tipRightTopCp2.flipX()
points.tipLeftTopEnd = points.tipRightTopEnd.flipX()
points.tipLeftBottomStart = points.tipRightBottomStart.flipX()
points.tipLeftBottomCp1 = points.tipRightBottomCp1.flipX()
points.tipLeftBottomCp2 = points.tipRightBottomCp2.flipX()
points.tipLeftBottomEnd = points.tipRightBottomEnd.flipX()
// highlight-start
/\*
- Round the bottom corners
- Macros will return the auto-generated IDs
\*/
const ids2 = {
bottomLeft: macro("round", {
id: "bottomLeft",
from: points.topLeft,
to: points.bottomRight,
via: points.bottomLeft,
radius: points.bottomRight.x / 4,
}),
bottomRight: macro("round", {
id: "bottomRight",
from: points.bottomLeft,
to: points.topRight,
via: points.bottomRight,
radius: points.bottomRight.x / 4,
})
}
/\*
- Create points from them with easy names
\*/
for (const side in ids2) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids2[side].points[id]].copy()
}
}
// highlight-end
// Create one path for the bib outline
paths.seam = new Path()
.move(points.edgeLeft)
// strikeout-start
/* We only need to replace the start
* with the new lines below
.line(points.bottomLeft)
.line(points.bottomRight)
*/
// strikeout-end
// highlight-start
.line(points.bottomLeftStart)
.curve(points.bottomLeftCp1, points.bottomLeftCp2, points.bottomLeftEnd)
.line(points.bottomRightStart)
.curve(points.bottomRightCp1, points.bottomRightCp2, points.bottomRightEnd)
// highlight-end
.line(points.edgeRight)
.curve(
points.edgeRightCp,
points.edgeTopRightCp,
points.tipLeftTopStart
)
.curve(
points.tipLeftTopCp1,
points.tipLeftTopCp2,
points.tipLeftTopEnd
)
.curve(
points.tipLeftBottomCp1,
points.tipLeftBottomCp2,
points.tipLeftBottomEnd
)
.curve(
points.topCp1,
points.rightCp2,
points.right
)
.curve(
points.rightCp1,
points.bottomCp2,
points.bottom
)
.curve(
points.bottomCp1,
points.leftCp2,
points.left
)
.curve(
points.leftCp1,
points.topCp2,
points.tipRightBottomEnd
)
.curve(
points.tipRightBottomCp2,
points.tipRightBottomCp1,
points.tipRightBottomStart
)
.curve(
points.tipRightTopCp2,
points.tipRightTopCp1,
points.tipRightTopStart
)
.curve(
points.edgeTopLeftCp,
points.edgeLeftCp,
points.edgeLeft
)
.close()
.addClass("fabric")
// Create one path for the bib outline
paths.seam = new Path()
.move(points.edgeLeft)
// strikeout-start
/_ We only need to replace the start
_ with the new lines below
.line(points.bottomLeft)
.line(points.bottomRight)
\*/
// strikeout-end
// highlight-start
.line(points.bottomLeftStart)
.curve(points.bottomLeftCp1, points.bottomLeftCp2, points.bottomLeftEnd)
.line(points.bottomRightStart)
.curve(points.bottomRightCp1, points.bottomRightCp2, points.bottomRightEnd)
// highlight-end
.line(points.edgeRight)
.curve(
points.edgeRightCp,
points.edgeTopRightCp,
points.tipLeftTopStart
)
.curve(
points.tipLeftTopCp1,
points.tipLeftTopCp2,
points.tipLeftTopEnd
)
.curve(
points.tipLeftBottomCp1,
points.tipLeftBottomCp2,
points.tipLeftBottomEnd
)
.curve(
points.topCp1,
points.rightCp2,
points.right
)
.curve(
points.rightCp1,
points.bottomCp2,
points.bottom
)
.curve(
points.bottomCp1,
points.leftCp2,
points.left
)
.curve(
points.leftCp1,
points.topCp2,
points.tipRightBottomEnd
)
.curve(
points.tipRightBottomCp2,
points.tipRightBottomCp1,
points.tipRightBottomStart
)
.curve(
points.tipRightTopCp2,
points.tipRightTopCp1,
points.tipRightTopStart
)
.curve(
points.edgeTopLeftCp,
points.edgeLeftCp,
points.edgeLeft
)
.close()
.addClass("fabric")
return part
return part
}
```
</Example>
```

View file

@ -1,6 +1,6 @@
---
title: Shaping the straps
order: 90
sidebar_position: 90
---
Our straps should follow the neck opening, which isn't that hard to do.
@ -15,7 +15,6 @@ As always, [the API docs](/reference/api/point/) have all the details.
:::
<Example tutorial caption="All of a sudden, things are starting to look like a bib">
```design/src/bib.mjs
function draftBib({
@ -28,36 +27,38 @@ function draftBib({
part,
}) {
/*
* Construct the quarter neck opening
*/
/\*
- Construct the quarter neck opening
_/
let tweak = 1
let target = (measurements.head * options.neckRatio) /4
let target = (measurements.head _ options.neckRatio) /4
let delta
do {
points.right = new Point(
tweak * measurements.head / 10,
0
)
points.bottom = new Point(
0,
tweak * measurements.head / 12
)
points.right = new Point(
tweak _ measurements.head / 10,
0
)
points.bottom = new Point(
0,
tweak _ measurements.head / 12
)
points.rightCp1 = points.right.shift(
90,
90,
points.bottom.dy(points.right) / 2
)
points.bottomCp2 = points.bottom.shift(
0,
0,
points.bottom.dx(points.right) / 2
)
paths.quarterNeck = new Path()
.move(points.right)
.curve(
points.rightCp1,
points.bottomCp2,
points.rightCp1,
points.bottomCp2,
points.bottom
)
.hide()
@ -65,11 +66,13 @@ function draftBib({
delta = paths.quarterNeck.length() - target
if (delta > 0) tweak = tweak * 0.99
else tweak = tweak * 1.02
} while (Math.abs(delta) > 1)
/*
* Construct the complete neck opening
*/
} while (Math.abs(delta) > 1)
/\*
- Construct the complete neck opening
\*/
points.rightCp2 = points.rightCp1.flipY()
points.bottomCp1 = points.bottomCp2.flipX()
points.left = points.right.flipX()
@ -79,75 +82,81 @@ function draftBib({
points.topCp1 = points.bottomCp2.flipY()
points.topCp2 = points.bottomCp1.flipY()
paths.neck = new Path()
.move(points.top)
.curve(points.topCp2, points.leftCp1, points.left)
.curve(points.leftCp2, points.bottomCp1, points.bottom)
.curve(points.bottomCp2, points.rightCp1, points.right)
.curve(points.rightCp2, points.topCp1, points.top)
.close()
.addClass('fabric')
paths.neck = new Path()
.move(points.top)
.curve(points.topCp2, points.leftCp1, points.left)
.curve(points.leftCp2, points.bottomCp1, points.bottom)
.curve(points.bottomCp2, points.rightCp1, points.right)
.curve(points.rightCp2, points.topCp1, points.top)
.close()
.addClass('fabric')
/*
* Drawing the bib outline
*/
const width = measurements.head * options.widthRatio
const length = measurements.head * options.lengthRatio
/\*
points.topLeft = new Point(
width / -2,
points.top.y - (width / 2 - points.right.x)
)
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
- Drawing the bib outline
_/
const width = measurements.head _ options.widthRatio
const length = measurements.head \* options.lengthRatio
points.topLeft = new Point(
width / -2,
points.top.y - (width / 2 - points.right.x)
)
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
// strikeout-start
/*
* Remove this path
/\*
- Remove this path
paths.rect = new Path()
.move(points.topLeft)
.line(points.bottomLeft)
.line(points.bottomRight)
.line(points.topRight)
.line(points.topLeft)
.close()
.addClass('fabric')
*/
// strikeout-end
.move(points.topLeft)
.line(points.bottomLeft)
.line(points.bottomRight)
.line(points.topRight)
.line(points.topLeft)
.close()
.addClass('fabric')
\*/
// strikeout-end
// highlight-start
/*
* Shape the straps
*/
/\*
- Shape the straps
\*/
points.edgeLeft = new Point(points.topLeft.x, points.left.y)
points.edgeRight = new Point(points.topRight.x, points.right.y)
points.edgeTop = new Point(0, points.topLeft.y)
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
points.edgeRightCp = points.edgeLeftCp.flipX()
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(
points.topLeft,
0.5
)
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
points.edgeRightCp = points.edgeLeftCp.flipX()
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(
points.topLeft,
0.5
)
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
/*
* Now, adapt our `rect` path so it's no longer a rectangle:
*/
/\*
- Now, adapt our `rect` path so it's no longer a rectangle:
\*/
paths.rect = new Path()
.move(points.edgeTop)
.curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
.line(points.bottomLeft)
.line(points.bottomRight)
.line(points.edgeRight)
.curve(points.edgeRightCp, points.edgeTopRightCp, points.edgeTop)
.close()
.move(points.edgeTop)
.curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
.line(points.bottomLeft)
.line(points.bottomRight)
.line(points.edgeRight)
.curve(points.edgeRightCp, points.edgeTopRightCp, points.edgeTop)
.close()
// highlight-end
return part
return part
}
```
</Example>
```

View file

@ -1,11 +1,11 @@
---
title: Adding annotations
order: 20
sidebar_position: 20
---
Our pattern is still a little bit *bare*. It would be nice to add some *annotations* to it.
Our pattern is still a little bit _bare_. It would be nice to add some _annotations_ to it.
When I say *annotations* it's an umbrella term for things like text or other
When I say _annotations_ it's an umbrella term for things like text or other
bits of information that help the user understand the pattern.
## Adding snippets
@ -42,15 +42,17 @@ function draftBib({
part,
}) {
/*
* Construct the neck opening
*/
const target = (measurements.head * options.neckRatio) / 4
/\*
- Construct the neck opening
_/
const target = (measurements.head _ options.neckRatio) / 4
let tweak = 1
let delta
do {
points.right = new Point((tweak * measurements.head) / 10, 0)
points.bottom = new Point(0, (tweak * measurements.head) / 12)
points.right = new Point((tweak _ measurements.head) / 10, 0)
points.bottom = new Point(0, (tweak _ measurements.head) / 12)
points.rightCp1 = points.right.shift(90, points.bottom.dy(points.right) / 2)
points.bottomCp2 = points.bottom.shift(0, points.bottom.dx(points.right) / 2)
@ -62,108 +64,116 @@ function draftBib({
delta = paths.neck.length() - target
if (delta > 0) tweak = tweak * 0.99
else tweak = tweak * 1.02
} while (Math.abs(delta) > 1)
points.rightCp2 = points.rightCp1.flipY()
points.bottomCp1 = points.bottomCp2.flipX()
} while (Math.abs(delta) > 1)
points.left = points.right.flipX()
points.leftCp1 = points.rightCp2.flipX()
points.leftCp2 = points.rightCp1.flipX()
points.rightCp2 = points.rightCp1.flipY()
points.bottomCp1 = points.bottomCp2.flipX()
points.top = points.bottom.flipY()
points.topCp1 = points.bottomCp2.flipY()
points.topCp2 = points.bottomCp1.flipY()
points.left = points.right.flipX()
points.leftCp1 = points.rightCp2.flipX()
points.leftCp2 = points.rightCp1.flipX()
/*
* Construct the outline
*/
let width = measurements.head * options.widthRatio
let length = measurements.head * options.lengthRatio
points.top = points.bottom.flipY()
points.topCp1 = points.bottomCp2.flipY()
points.topCp2 = points.bottomCp1.flipY()
points.topLeft = new Point(width / -2, points.top.y - (width / 2 - points.right.x))
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
/\*
points.edgeLeft = new Point(points.topLeft.x, points.left.y)
points.edgeRight = new Point(points.topRight.x, points.right.y)
points.edgeTop = new Point(0, points.topLeft.y)
- Construct the outline
_/
let width = measurements.head _ options.widthRatio
let length = measurements.head \* options.lengthRatio
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
points.edgeRightCp = points.edgeLeftCp.flipX()
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(points.topLeft, 0.5)
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.topLeft = new Point(width / -2, points.top.y - (width / 2 - points.right.x))
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
/*
* Round the end of the straps
*/
points.edgeLeft = new Point(points.topLeft.x, points.left.y)
points.edgeRight = new Point(points.topRight.x, points.right.y)
points.edgeTop = new Point(0, points.topLeft.y)
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
points.edgeRightCp = points.edgeLeftCp.flipX()
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(points.topLeft, 0.5)
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
/\*
- Round the end of the straps
\*/
let strap = points.edgeTop.dy(points.top)
points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
points.tipRightBottom = new Point(points.tipRight.x, points.top.y)
points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
points.tipRightBottom = new Point(points.tipRight.x, points.top.y)
/*
* Macros will return the auto-generated IDs
*/
/\*
- Macros will return the auto-generated IDs
\*/
const ids1 = {
tipRightTop: macro('round', {
id: 'tipRightTop',
from: points.edgeTop,
to: points.tipRight,
via: points.tipRightTop,
}),
tipRightBottom: macro('round', {
id: 'tipRightBottom',
from: points.tipRight,
to: points.top,
via: points.tipRightBottom,
}),
tipRightTop: macro('round', {
id: 'tipRightTop',
from: points.edgeTop,
to: points.tipRight,
via: points.tipRightTop,
}),
tipRightBottom: macro('round', {
id: 'tipRightBottom',
from: points.tipRight,
to: points.top,
via: points.tipRightBottom,
}),
}
/*
* Create points from them with easy names
*/
/\*
- Create points from them with easy names
\*/
for (const side in ids1) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
}
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
}
}
/*
* Rotate straps so they don't overlap
*/
/\*
- Rotate straps so they don't overlap
\*/
let rotateThese = [
'edgeTopLeftCp',
'edgeTop',
'tipRight',
'tipRightTop',
'tipRightTopStart',
'tipRightTopCp1',
'tipRightTopCp2',
'tipRightTopEnd',
'tipRightBottomStart',
'tipRightBottomCp1',
'tipRightBottomCp2',
'tipRightBottomEnd',
'tipRightBottom',
'top',
'topCp2',
'edgeTopLeftCp',
'edgeTop',
'tipRight',
'tipRightTop',
'tipRightTopStart',
'tipRightTopCp1',
'tipRightTopCp2',
'tipRightTopEnd',
'tipRightBottomStart',
'tipRightBottomCp1',
'tipRightBottomCp2',
'tipRightBottomEnd',
'tipRightBottom',
'top',
'topCp2',
]
while (points.tipRightBottomStart.x > -1) {
for (let p of rotateThese) points[p] = points[p].rotate(1, points.edgeLeft)
}
while (points.tipRightBottomStart.x > -1) {
for (let p of rotateThese) points[p] = points[p].rotate(1, points.edgeLeft)
}
/*
* Add points to anchor snaps on
*/
/\*
- Add points to anchor snaps on
\*/
points.snapLeft = points.top.shiftFractionTowards(points.edgeTop, 0.5)
/*
* Mirror points to the other side
*/
/\*
- Mirror points to the other side
\*/
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.topCp1 = points.topCp2.flipX()
points.tipLeftTopStart = points.tipRightTopStart.flipX()
@ -176,83 +186,88 @@ function draftBib({
points.tipLeftBottomEnd = points.tipRightBottomEnd.flipX()
points.snapRight = points.snapLeft.flipX()
/*
* Round the bottom of the bib
* Radius is fixed, but you could use an option for it)
*
* Macros will return the auto-generated IDs
*/
/\*
- Round the bottom of the bib
- Radius is fixed, but you could use an option for it)
-
- Macros will return the auto-generated IDs
_/
const ids2 = {
bottomLeft: macro('round', {
id: 'bottomLeft',
from: points.topLeft,
to: points.bottomRight,
via: points.bottomLeft,
radius: points.bottomRight.x / 4,
}),
bottomRight: macro('round', {
id: 'bottomRight',
from: points.bottomLeft,
to: points.topRight,
via: points.bottomRight,
radius: points.bottomRight.x / 4,
}),
bottomLeft: macro('round', {
id: 'bottomLeft',
from: points.topLeft,
to: points.bottomRight,
via: points.bottomLeft,
radius: points.bottomRight.x / 4,
}),
bottomRight: macro('round', {
id: 'bottomRight',
from: points.bottomLeft,
to: points.topRight,
via: points.bottomRight,
radius: points.bottomRight.x / 4,
}),
}
/*
* Create points from them with easy names
*/
/_
- Create points from them with easy names
\*/
for (const side in ids2) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids2[side].points[id]].copy()
}
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids2[side].points[id]].copy()
}
}
/*
* Construct the path
*/
/\*
- Construct the path
\*/
paths.seam = new Path()
.move(points.edgeLeft)
.line(points.bottomLeftStart)
.curve(points.bottomLeftCp1, points.bottomLeftCp2, points.bottomLeftEnd)
.line(points.bottomRightStart)
.curve(points.bottomRightCp1, points.bottomRightCp2, points.bottomRightEnd)
.line(points.edgeRight)
.curve(points.edgeRightCp, points.edgeTopRightCp, points.tipLeftTopStart)
.curve(points.tipLeftTopCp1, points.tipLeftTopCp2, points.tipLeftTopEnd)
.curve(points.tipLeftBottomCp1, points.tipLeftBottomCp2, points.tipLeftBottomEnd)
.curve(points.topCp1, points.rightCp2, points.right)
.curve(points.rightCp1, points.bottomCp2, points.bottom)
.curve(points.bottomCp1, points.leftCp2, points.left)
.curve(points.leftCp1, points.topCp2, points.tipRightBottomEnd)
.curve(points.tipRightBottomCp2, points.tipRightBottomCp1, points.tipRightBottomStart)
.curve(points.tipRightTopCp2, points.tipRightTopCp1, points.tipRightTopStart)
.curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
.close()
.attr('class', 'fabric')
.move(points.edgeLeft)
.line(points.bottomLeftStart)
.curve(points.bottomLeftCp1, points.bottomLeftCp2, points.bottomLeftEnd)
.line(points.bottomRightStart)
.curve(points.bottomRightCp1, points.bottomRightCp2, points.bottomRightEnd)
.line(points.edgeRight)
.curve(points.edgeRightCp, points.edgeTopRightCp, points.tipLeftTopStart)
.curve(points.tipLeftTopCp1, points.tipLeftTopCp2, points.tipLeftTopEnd)
.curve(points.tipLeftBottomCp1, points.tipLeftBottomCp2, points.tipLeftBottomEnd)
.curve(points.topCp1, points.rightCp2, points.right)
.curve(points.rightCp1, points.bottomCp2, points.bottom)
.curve(points.bottomCp1, points.leftCp2, points.left)
.curve(points.leftCp1, points.topCp2, points.tipRightBottomEnd)
.curve(points.tipRightBottomCp2, points.tipRightBottomCp1, points.tipRightBottomStart)
.curve(points.tipRightTopCp2, points.tipRightTopCp1, points.tipRightTopStart)
.curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
.close()
.attr('class', 'fabric')
// highlight-start
/*
*
* Annotations
*
*/
// highlight-start
/\*
/*
* Add the snaps
*/
-
- Annotations
- \*/
/\*
- Add the snaps
\*/
snippets.snapStud = new Snippet('snap-stud', points.snapLeft)
snippets.snapSocket = new Snippet('snap-socket', points.snapRight).attr('opacity', 0.5)
/*
* Add the logo
*/
/\*
- Add the logo
\*/
points.logo = new Point(0, 0)
snippets.logo = new Snippet('logo', points.logo)
// highlight-end
return part
return part
}
```
````
</Example>
## Setting the cutlist, adding a title and scalebox
@ -522,6 +537,6 @@ function draftBib({
return part
}
```
</Example>
````
</Example>

View file

@ -1,24 +1,24 @@
---
title: Dealing with laser cutters
order: 25
sidebar_position: 25
---
Laser cutters is merely an example of a situation where your user wants not the
complete detailed pattern with all annotations, but just the outlines.
Essentially what we had at the end of part 2 of this tutorial.
Since then, we've added a bunch of embellishments, and perhaps the user does
Since then, we've added a bunch of embellishments, and perhaps the user does
not want those.
Well, good news: there is a setting for that too. That setting is `complete`,
Well, good news: there is a setting for that too. That setting is `complete`,
and more annotations will automatically take it into account.
For example, if you put a logo or title on the pattern, it will check the
For example, if you put a logo or title on the pattern, it will check the
`complete` setting and if it is `false` it will do nothing.
When we say we're going to *complete* our pattern, we mean we're going to add
When we say we're going to _complete_ our pattern, we mean we're going to add
things like a title and a scalebox and so on.
So while in most scenarios you don't have to worry about `complete`, you
So while in most scenarios you don't have to worry about `complete`, you
should keep it in mind when you are adding text or paths to the design
that should not be shown on a non-complete pattern.
@ -46,15 +46,15 @@ function draftBib({
part,
}) {
/*
* Construct the neck opening
*/
const target = (measurements.head * options.neckRatio) / 4
let tweak = 1
let delta
do {
points.right = new Point((tweak * measurements.head) / 10, 0)
points.bottom = new Point(0, (tweak * measurements.head) / 12)
/\*
_ Construct the neck opening
_/
const target = (measurements.head _ options.neckRatio) / 4
let tweak = 1
let delta
do {
points.right = new Point((tweak _ measurements.head) / 10, 0)
points.bottom = new Point(0, (tweak \* measurements.head) / 12)
points.rightCp1 = points.right.shift(90, points.bottom.dy(points.right) / 2)
points.bottomCp2 = points.bottom.shift(0, points.bottom.dx(points.right) / 2)
@ -66,229 +66,229 @@ function draftBib({
delta = paths.neck.length() - target
if (delta > 0) tweak = tweak * 0.99
else tweak = tweak * 1.02
} while (Math.abs(delta) > 1)
points.rightCp2 = points.rightCp1.flipY()
points.bottomCp1 = points.bottomCp2.flipX()
} while (Math.abs(delta) > 1)
points.left = points.right.flipX()
points.leftCp1 = points.rightCp2.flipX()
points.leftCp2 = points.rightCp1.flipX()
points.rightCp2 = points.rightCp1.flipY()
points.bottomCp1 = points.bottomCp2.flipX()
points.top = points.bottom.flipY()
points.topCp1 = points.bottomCp2.flipY()
points.topCp2 = points.bottomCp1.flipY()
points.left = points.right.flipX()
points.leftCp1 = points.rightCp2.flipX()
points.leftCp2 = points.rightCp1.flipX()
/*
* Construct the outline
*/
let width = measurements.head * options.widthRatio
let length = measurements.head * options.lengthRatio
points.top = points.bottom.flipY()
points.topCp1 = points.bottomCp2.flipY()
points.topCp2 = points.bottomCp1.flipY()
points.topLeft = new Point(width / -2, points.top.y - (width / 2 - points.right.x))
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
/\*
_ Construct the outline
_/
let width = measurements.head _ options.widthRatio
let length = measurements.head _ options.lengthRatio
points.edgeLeft = new Point(points.topLeft.x, points.left.y)
points.edgeRight = new Point(points.topRight.x, points.right.y)
points.edgeTop = new Point(0, points.topLeft.y)
points.topLeft = new Point(width / -2, points.top.y - (width / 2 - points.right.x))
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
points.edgeRightCp = points.edgeLeftCp.flipX()
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(points.topLeft, 0.5)
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.edgeLeft = new Point(points.topLeft.x, points.left.y)
points.edgeRight = new Point(points.topRight.x, points.right.y)
points.edgeTop = new Point(0, points.topLeft.y)
/*
* Round the end of the straps
*/
let strap = points.edgeTop.dy(points.top)
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
points.edgeRightCp = points.edgeLeftCp.flipX()
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(points.topLeft, 0.5)
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
points.tipRightBottom = new Point(points.tipRight.x, points.top.y)
/\*
_ Round the end of the straps
_/
let strap = points.edgeTop.dy(points.top)
/*
* Macros will return the auto-generated IDs
*/
const ids1 = {
tipRightTop: macro('round', {
id: 'tipRightTop',
from: points.edgeTop,
to: points.tipRight,
via: points.tipRightTop,
}),
tipRightBottom: macro('round', {
id: 'tipRightBottom',
from: points.tipRight,
to: points.top,
via: points.tipRightBottom,
}),
}
points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
points.tipRightBottom = new Point(points.tipRight.x, points.top.y)
/*
* Create points from them with easy names
*/
for (const side in ids1) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
}
}
/*
* Rotate straps so they don't overlap
*/
let rotateThese = [
'edgeTopLeftCp',
'edgeTop',
'tipRight',
'tipRightTop',
'tipRightTopStart',
'tipRightTopCp1',
'tipRightTopCp2',
'tipRightTopEnd',
'tipRightBottomStart',
'tipRightBottomCp1',
'tipRightBottomCp2',
'tipRightBottomEnd',
'tipRightBottom',
'top',
'topCp2',
]
while (points.tipRightBottomStart.x > -1) {
for (let p of rotateThese) points[p] = points[p].rotate(1, points.edgeLeft)
}
/*
* Add points to anchor snaps on
*/
points.snapLeft = points.top.shiftFractionTowards(points.edgeTop, 0.5)
/*
* Mirror points to the other side
*/
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.topCp1 = points.topCp2.flipX()
points.tipLeftTopStart = points.tipRightTopStart.flipX()
points.tipLeftTopCp1 = points.tipRightTopCp1.flipX()
points.tipLeftTopCp2 = points.tipRightTopCp2.flipX()
points.tipLeftTopEnd = points.tipRightTopEnd.flipX()
points.tipLeftBottomStart = points.tipRightBottomStart.flipX()
points.tipLeftBottomCp1 = points.tipRightBottomCp1.flipX()
points.tipLeftBottomCp2 = points.tipRightBottomCp2.flipX()
points.tipLeftBottomEnd = points.tipRightBottomEnd.flipX()
points.snapRight = points.snapLeft.flipX()
/*
* Round the bottom of the bib
* Radius is fixed, but you could use an option for it)
*
* Macros will return the auto-generated IDs
*/
const ids2 = {
bottomLeft: macro('round', {
id: 'bottomLeft',
from: points.topLeft,
to: points.bottomRight,
via: points.bottomLeft,
radius: points.bottomRight.x / 4,
}),
bottomRight: macro('round', {
id: 'bottomRight',
from: points.bottomLeft,
to: points.topRight,
via: points.bottomRight,
radius: points.bottomRight.x / 4,
}),
}
/*
* Create points from them with easy names
*/
for (const side in ids2) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids2[side].points[id]].copy()
}
}
/*
* Construct the path
*/
paths.seam = new Path()
.move(points.edgeLeft)
.line(points.bottomLeftStart)
.curve(points.bottomLeftCp1, points.bottomLeftCp2, points.bottomLeftEnd)
.line(points.bottomRightStart)
.curve(points.bottomRightCp1, points.bottomRightCp2, points.bottomRightEnd)
.line(points.edgeRight)
.curve(points.edgeRightCp, points.edgeTopRightCp, points.tipLeftTopStart)
.curve(points.tipLeftTopCp1, points.tipLeftTopCp2, points.tipLeftTopEnd)
.curve(points.tipLeftBottomCp1, points.tipLeftBottomCp2, points.tipLeftBottomEnd)
.curve(points.topCp1, points.rightCp2, points.right)
.curve(points.rightCp1, points.bottomCp2, points.bottom)
.curve(points.bottomCp1, points.leftCp2, points.left)
.curve(points.leftCp1, points.topCp2, points.tipRightBottomEnd)
.curve(points.tipRightBottomCp2, points.tipRightBottomCp1, points.tipRightBottomStart)
.curve(points.tipRightTopCp2, points.tipRightTopCp1, points.tipRightTopStart)
.curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
.close()
.attr('class', 'fabric')
/*
*
* Annotations
*
*/
/*
* Cut list
*/
store.cutlist.addCut({ cut: 1, from: 'fabric' })
/*
* Add the snaps
*/
snippets.snapStud = new Snippet('snap-stud', points.snapLeft)
snippets.snapSocket = new Snippet('snap-socket', points.snapRight).attr('opacity', 0.5)
// highlight-start
/*
* Add the bias tape
*/
if (complete)
paths.bias = paths.seam
.offset(-5)
.addClass('note dashed')
.addText('tutorial:finishWithBiasTape', 'center fill-note')
// highlight-end
/*
* Add the title
*/
points.title = points.bottom.shift(-90, 45)
macro('title', {
at: points.title,
nr: 1,
title: 'bib',
align: 'center',
scale: 0.8,
})
/*
* Add the scalebox
*/
points.scalebox = points.title.shift(-90, 65)
macro('scalebox', { at: points.scalebox })
/*
* Add the logo
*/
points.logo = new Point(0, 0)
snippets.logo = new Snippet('logo', points.logo)
return part
/\*
_ Macros will return the auto-generated IDs
_/
const ids1 = {
tipRightTop: macro('round', {
id: 'tipRightTop',
from: points.edgeTop,
to: points.tipRight,
via: points.tipRightTop,
}),
tipRightBottom: macro('round', {
id: 'tipRightBottom',
from: points.tipRight,
to: points.top,
via: points.tipRightBottom,
}),
}
```
/\*
_ Create points from them with easy names
_/
for (const side in ids1) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
}
}
/\*
_ Rotate straps so they don't overlap
_/
let rotateThese = [
'edgeTopLeftCp',
'edgeTop',
'tipRight',
'tipRightTop',
'tipRightTopStart',
'tipRightTopCp1',
'tipRightTopCp2',
'tipRightTopEnd',
'tipRightBottomStart',
'tipRightBottomCp1',
'tipRightBottomCp2',
'tipRightBottomEnd',
'tipRightBottom',
'top',
'topCp2',
]
while (points.tipRightBottomStart.x > -1) {
for (let p of rotateThese) points[p] = points[p].rotate(1, points.edgeLeft)
}
/\*
_ Add points to anchor snaps on
_/
points.snapLeft = points.top.shiftFractionTowards(points.edgeTop, 0.5)
/\*
_ Mirror points to the other side
_/
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.topCp1 = points.topCp2.flipX()
points.tipLeftTopStart = points.tipRightTopStart.flipX()
points.tipLeftTopCp1 = points.tipRightTopCp1.flipX()
points.tipLeftTopCp2 = points.tipRightTopCp2.flipX()
points.tipLeftTopEnd = points.tipRightTopEnd.flipX()
points.tipLeftBottomStart = points.tipRightBottomStart.flipX()
points.tipLeftBottomCp1 = points.tipRightBottomCp1.flipX()
points.tipLeftBottomCp2 = points.tipRightBottomCp2.flipX()
points.tipLeftBottomEnd = points.tipRightBottomEnd.flipX()
points.snapRight = points.snapLeft.flipX()
/\*
_ Round the bottom of the bib
_ Radius is fixed, but you could use an option for it) \*
_ Macros will return the auto-generated IDs
_/
const ids2 = {
bottomLeft: macro('round', {
id: 'bottomLeft',
from: points.topLeft,
to: points.bottomRight,
via: points.bottomLeft,
radius: points.bottomRight.x / 4,
}),
bottomRight: macro('round', {
id: 'bottomRight',
from: points.bottomLeft,
to: points.topRight,
via: points.bottomRight,
radius: points.bottomRight.x / 4,
}),
}
/\*
_ Create points from them with easy names
_/
for (const side in ids2) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids2[side].points[id]].copy()
}
}
/\*
_ Construct the path
_/
paths.seam = new Path()
.move(points.edgeLeft)
.line(points.bottomLeftStart)
.curve(points.bottomLeftCp1, points.bottomLeftCp2, points.bottomLeftEnd)
.line(points.bottomRightStart)
.curve(points.bottomRightCp1, points.bottomRightCp2, points.bottomRightEnd)
.line(points.edgeRight)
.curve(points.edgeRightCp, points.edgeTopRightCp, points.tipLeftTopStart)
.curve(points.tipLeftTopCp1, points.tipLeftTopCp2, points.tipLeftTopEnd)
.curve(points.tipLeftBottomCp1, points.tipLeftBottomCp2, points.tipLeftBottomEnd)
.curve(points.topCp1, points.rightCp2, points.right)
.curve(points.rightCp1, points.bottomCp2, points.bottom)
.curve(points.bottomCp1, points.leftCp2, points.left)
.curve(points.leftCp1, points.topCp2, points.tipRightBottomEnd)
.curve(points.tipRightBottomCp2, points.tipRightBottomCp1, points.tipRightBottomStart)
.curve(points.tipRightTopCp2, points.tipRightTopCp1, points.tipRightTopStart)
.curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
.close()
.attr('class', 'fabric')
/\* \*
_ Annotations
_
\*/
/\*
_ Cut list
_/
store.cutlist.addCut({ cut: 1, from: 'fabric' })
/\*
_ Add the snaps
_/
snippets.snapStud = new Snippet('snap-stud', points.snapLeft)
snippets.snapSocket = new Snippet('snap-socket', points.snapRight).attr('opacity', 0.5)
// highlight-start
/\*
_ Add the bias tape
_/
if (complete)
paths.bias = paths.seam
.offset(-5)
.addClass('note dashed')
.addText('tutorial:finishWithBiasTape', 'center fill-note')
// highlight-end
/\*
_ Add the title
_/
points.title = points.bottom.shift(-90, 45)
macro('title', {
at: points.title,
nr: 1,
title: 'bib',
align: 'center',
scale: 0.8,
})
/\*
_ Add the scalebox
_/
points.scalebox = points.title.shift(-90, 65)
macro('scalebox', { at: points.scalebox })
/\*
_ Add the logo
_/
points.logo = new Point(0, 0)
snippets.logo = new Snippet('logo', points.logo)
return part
}
````
</Example>
@ -556,6 +556,6 @@ function draftBib({
return part
}
```
</Example>
````
</Example>

View file

@ -1,6 +1,6 @@
---
title: Conclusion
order: 90
sidebar_position: 90
---
Congratulations, we have created our first pattern. And while it's arguably
@ -25,13 +25,14 @@ stuck at any moment, or need some help or advice, you can [join our chat
room](https://discord.freesewing.org/) and we'll help you out.
:::note COMMENT (by joost)
##### How did I do?
You could do me a real favor by letting me know what you loved or hated about
this tutorial.
Were there areas that were not clear? Did I dwell too long on one topic, or
rushed through another one too quickly? Your feedback helps improve things,
rushed through another one too quickly? Your feedback helps improve things,
so don't be shy and tell me what you think.
You can reach me at joost@freesewing.org

View file

@ -1,6 +1,6 @@
---
title: Saving space (and trees) with exand
order: 40
sidebar_position: 40
---
There is one more way we like to save space (and trees): The `expand` setting.

View file

@ -1,10 +1,10 @@
---
title: How to communicate to the user
order: 50
sidebar_position: 50
---
As a designer, there are times you want to bring something to the attention of
the user. I am not talking about generic information that can go in the
the user. I am not talking about generic information that can go in the
documentation, but rather a message that is tailored specifically to this
pattern, much like this pattern is specifically tailored to the user.
@ -47,15 +47,15 @@ function draftBib({
part,
}) {
/*
* Construct the neck opening
*/
const target = (measurements.head * options.neckRatio) / 4
let tweak = 1
let delta
do {
points.right = new Point((tweak * measurements.head) / 10, 0)
points.bottom = new Point(0, (tweak * measurements.head) / 12)
/\*
_ Construct the neck opening
_/
const target = (measurements.head _ options.neckRatio) / 4
let tweak = 1
let delta
do {
points.right = new Point((tweak _ measurements.head) / 10, 0)
points.bottom = new Point(0, (tweak \* measurements.head) / 12)
points.rightCp1 = points.right.shift(90, points.bottom.dy(points.right) / 2)
points.bottomCp2 = points.bottom.shift(0, points.bottom.dx(points.right) / 2)
@ -67,277 +67,278 @@ function draftBib({
delta = paths.neck.length() - target
if (delta > 0) tweak = tweak * 0.99
else tweak = tweak * 1.02
} while (Math.abs(delta) > 1)
points.rightCp2 = points.rightCp1.flipY()
points.bottomCp1 = points.bottomCp2.flipX()
} while (Math.abs(delta) > 1)
points.left = points.right.flipX()
points.leftCp1 = points.rightCp2.flipX()
points.leftCp2 = points.rightCp1.flipX()
points.rightCp2 = points.rightCp1.flipY()
points.bottomCp1 = points.bottomCp2.flipX()
points.top = points.bottom.flipY()
points.topCp1 = points.bottomCp2.flipY()
points.topCp2 = points.bottomCp1.flipY()
points.left = points.right.flipX()
points.leftCp1 = points.rightCp2.flipX()
points.leftCp2 = points.rightCp1.flipX()
/*
* Construct the outline
*/
let width = measurements.head * options.widthRatio
let length = measurements.head * options.lengthRatio
points.top = points.bottom.flipY()
points.topCp1 = points.bottomCp2.flipY()
points.topCp2 = points.bottomCp1.flipY()
points.topLeft = new Point(width / -2, points.top.y - (width / 2 - points.right.x))
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
/\*
_ Construct the outline
_/
let width = measurements.head _ options.widthRatio
let length = measurements.head _ options.lengthRatio
points.edgeLeft = new Point(points.topLeft.x, points.left.y)
points.edgeRight = new Point(points.topRight.x, points.right.y)
points.edgeTop = new Point(0, points.topLeft.y)
points.topLeft = new Point(width / -2, points.top.y - (width / 2 - points.right.x))
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
points.edgeRightCp = points.edgeLeftCp.flipX()
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(points.topLeft, 0.5)
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.edgeLeft = new Point(points.topLeft.x, points.left.y)
points.edgeRight = new Point(points.topRight.x, points.right.y)
points.edgeTop = new Point(0, points.topLeft.y)
/*
* Round the end of the straps
*/
let strap = points.edgeTop.dy(points.top)
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
points.edgeRightCp = points.edgeLeftCp.flipX()
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(points.topLeft, 0.5)
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
points.tipRightBottom = new Point(points.tipRight.x, points.top.y)
/\*
_ Round the end of the straps
_/
let strap = points.edgeTop.dy(points.top)
/*
* Macros will return the auto-generated IDs
*/
const ids1 = {
tipRightTop: macro('round', {
id: 'tipRightTop',
from: points.edgeTop,
to: points.tipRight,
via: points.tipRightTop,
}),
tipRightBottom: macro('round', {
id: 'tipRightBottom',
from: points.tipRight,
to: points.top,
via: points.tipRightBottom,
}),
}
points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
points.tipRightBottom = new Point(points.tipRight.x, points.top.y)
/*
* Create points from them with easy names
*/
for (const side in ids1) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
}
}
/*
* Rotate straps so they don't overlap
*/
let rotateThese = [
'edgeTopLeftCp',
'edgeTop',
'tipRight',
'tipRightTop',
'tipRightTopStart',
'tipRightTopCp1',
'tipRightTopCp2',
'tipRightTopEnd',
'tipRightBottomStart',
'tipRightBottomCp1',
'tipRightBottomCp2',
'tipRightBottomEnd',
'tipRightBottom',
'top',
'topCp2',
]
while (points.tipRightBottomStart.x > -1) {
for (let p of rotateThese) points[p] = points[p].rotate(1, points.edgeLeft)
}
/*
* Add points to anchor snaps on
*/
points.snapLeft = points.top.shiftFractionTowards(points.edgeTop, 0.5)
/*
* Mirror points to the other side
*/
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.topCp1 = points.topCp2.flipX()
points.tipLeftTopStart = points.tipRightTopStart.flipX()
points.tipLeftTopCp1 = points.tipRightTopCp1.flipX()
points.tipLeftTopCp2 = points.tipRightTopCp2.flipX()
points.tipLeftTopEnd = points.tipRightTopEnd.flipX()
points.tipLeftBottomStart = points.tipRightBottomStart.flipX()
points.tipLeftBottomCp1 = points.tipRightBottomCp1.flipX()
points.tipLeftBottomCp2 = points.tipRightBottomCp2.flipX()
points.tipLeftBottomEnd = points.tipRightBottomEnd.flipX()
points.snapRight = points.snapLeft.flipX()
/*
* Round the bottom of the bib
* Radius is fixed, but you could use an option for it)
*
* Macros will return the auto-generated IDs
*/
const ids2 = {
bottomLeft: macro('round', {
id: 'bottomLeft',
from: points.topLeft,
to: points.bottomRight,
via: points.bottomLeft,
radius: points.bottomRight.x / 4,
}),
bottomRight: macro('round', {
id: 'bottomRight',
from: points.bottomLeft,
to: points.topRight,
via: points.bottomRight,
radius: points.bottomRight.x / 4,
}),
}
/*
* Create points from them with easy names
*/
for (const side in ids2) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids2[side].points[id]].copy()
}
}
/*
* Construct the path
*/
paths.seam = new Path()
.move(points.edgeLeft)
.line(points.bottomLeftStart)
.curve(points.bottomLeftCp1, points.bottomLeftCp2, points.bottomLeftEnd)
.line(points.bottomRightStart)
.curve(points.bottomRightCp1, points.bottomRightCp2, points.bottomRightEnd)
.line(points.edgeRight)
.curve(points.edgeRightCp, points.edgeTopRightCp, points.tipLeftTopStart)
.curve(points.tipLeftTopCp1, points.tipLeftTopCp2, points.tipLeftTopEnd)
.curve(points.tipLeftBottomCp1, points.tipLeftBottomCp2, points.tipLeftBottomEnd)
.curve(points.topCp1, points.rightCp2, points.right)
.curve(points.rightCp1, points.bottomCp2, points.bottom)
.curve(points.bottomCp1, points.leftCp2, points.left)
.curve(points.leftCp1, points.topCp2, points.tipRightBottomEnd)
.curve(points.tipRightBottomCp2, points.tipRightBottomCp1, points.tipRightBottomStart)
.curve(points.tipRightTopCp2, points.tipRightTopCp1, points.tipRightTopStart)
.curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
.close()
.attr('class', 'fabric')
/*
*
* Annotations
*
*/
// highlight-start
/*
* Let the user know about the bias tape and fabric requirements
*/
store.flag.note({
msg: 'tutorial:biasTapeLength',
replace: {
l: units(paths.seam.length()),
},
})
// highlight-end
/*
* Cut list
*/
store.cutlist.addCut({ cut: 1, from: 'fabric' })
/*
* Add the snaps
*/
snippets.snapStud = new Snippet('snap-stud', points.snapLeft)
snippets.snapSocket = new Snippet('snap-socket', points.snapRight).attr('opacity', 0.5)
/*
* Add the bias tape
*/
if (complete)
paths.bias = paths.seam
.offset(-5)
.addClass('note dashed')
.addText('finishWithBiasTape', 'center fill-note')
/*
* Add the title
*/
points.title = points.bottom.shift(-90, 45)
macro('title', {
at: points.title,
nr: 1,
title: 'bib',
align: 'center',
scale: 0.8,
})
/*
* Add the scalebox
*/
points.scalebox = points.title.shift(-90, 65)
macro('scalebox', { at: points.scalebox })
/*
* Add the logo
*/
points.logo = new Point(0, 0)
snippets.logo = new Snippet('logo', points.logo)
/*
* Add dimensions
*/
macro('hd', {
id: 'wFull',
from: points.bottomLeftStart,
to: points.bottomRightEnd,
y: points.bottomLeft.y + 15,
})
macro('vd', {
id: 'hBottomToOpeningBottom',
from: points.bottomRightStart,
to: points.bottom,
x: points.bottomRight.x + 15,
})
macro('vd', {
id: 'hBottomToOpeningCenter',
from: points.bottomRightStart,
to: points.right,
x: points.bottomRight.x + 30,
})
macro('vd', {
id: 'hTotal',
from: points.bottomRightStart,
to: points.tipLeftTopStart,
x: points.bottomRight.x + 45,
})
macro('hd', {
id: 'wOpening',
from: points.left,
to: points.right,
y: points.left.y + 25,
})
macro('ld', {
id: 'wStrap',
from: points.tipLeftBottomEnd,
to: points.tipLeftTopStart,
d: -15,
})
return part
/\*
_ Macros will return the auto-generated IDs
_/
const ids1 = {
tipRightTop: macro('round', {
id: 'tipRightTop',
from: points.edgeTop,
to: points.tipRight,
via: points.tipRightTop,
}),
tipRightBottom: macro('round', {
id: 'tipRightBottom',
from: points.tipRight,
to: points.top,
via: points.tipRightBottom,
}),
}
/\*
_ Create points from them with easy names
_/
for (const side in ids1) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
}
}
/\*
_ Rotate straps so they don't overlap
_/
let rotateThese = [
'edgeTopLeftCp',
'edgeTop',
'tipRight',
'tipRightTop',
'tipRightTopStart',
'tipRightTopCp1',
'tipRightTopCp2',
'tipRightTopEnd',
'tipRightBottomStart',
'tipRightBottomCp1',
'tipRightBottomCp2',
'tipRightBottomEnd',
'tipRightBottom',
'top',
'topCp2',
]
while (points.tipRightBottomStart.x > -1) {
for (let p of rotateThese) points[p] = points[p].rotate(1, points.edgeLeft)
}
/\*
_ Add points to anchor snaps on
_/
points.snapLeft = points.top.shiftFractionTowards(points.edgeTop, 0.5)
/\*
_ Mirror points to the other side
_/
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.topCp1 = points.topCp2.flipX()
points.tipLeftTopStart = points.tipRightTopStart.flipX()
points.tipLeftTopCp1 = points.tipRightTopCp1.flipX()
points.tipLeftTopCp2 = points.tipRightTopCp2.flipX()
points.tipLeftTopEnd = points.tipRightTopEnd.flipX()
points.tipLeftBottomStart = points.tipRightBottomStart.flipX()
points.tipLeftBottomCp1 = points.tipRightBottomCp1.flipX()
points.tipLeftBottomCp2 = points.tipRightBottomCp2.flipX()
points.tipLeftBottomEnd = points.tipRightBottomEnd.flipX()
points.snapRight = points.snapLeft.flipX()
/\*
_ Round the bottom of the bib
_ Radius is fixed, but you could use an option for it) \*
_ Macros will return the auto-generated IDs
_/
const ids2 = {
bottomLeft: macro('round', {
id: 'bottomLeft',
from: points.topLeft,
to: points.bottomRight,
via: points.bottomLeft,
radius: points.bottomRight.x / 4,
}),
bottomRight: macro('round', {
id: 'bottomRight',
from: points.bottomLeft,
to: points.topRight,
via: points.bottomRight,
radius: points.bottomRight.x / 4,
}),
}
/\*
_ Create points from them with easy names
_/
for (const side in ids2) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids2[side].points[id]].copy()
}
}
/\*
_ Construct the path
_/
paths.seam = new Path()
.move(points.edgeLeft)
.line(points.bottomLeftStart)
.curve(points.bottomLeftCp1, points.bottomLeftCp2, points.bottomLeftEnd)
.line(points.bottomRightStart)
.curve(points.bottomRightCp1, points.bottomRightCp2, points.bottomRightEnd)
.line(points.edgeRight)
.curve(points.edgeRightCp, points.edgeTopRightCp, points.tipLeftTopStart)
.curve(points.tipLeftTopCp1, points.tipLeftTopCp2, points.tipLeftTopEnd)
.curve(points.tipLeftBottomCp1, points.tipLeftBottomCp2, points.tipLeftBottomEnd)
.curve(points.topCp1, points.rightCp2, points.right)
.curve(points.rightCp1, points.bottomCp2, points.bottom)
.curve(points.bottomCp1, points.leftCp2, points.left)
.curve(points.leftCp1, points.topCp2, points.tipRightBottomEnd)
.curve(points.tipRightBottomCp2, points.tipRightBottomCp1, points.tipRightBottomStart)
.curve(points.tipRightTopCp2, points.tipRightTopCp1, points.tipRightTopStart)
.curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
.close()
.attr('class', 'fabric')
/\* \*
_ Annotations
_
\*/
// highlight-start
/\*
_ Let the user know about the bias tape and fabric requirements
_/
store.flag.note({
msg: 'tutorial:biasTapeLength',
replace: {
l: units(paths.seam.length()),
},
})
// highlight-end
/\*
_ Cut list
_/
store.cutlist.addCut({ cut: 1, from: 'fabric' })
/\*
_ Add the snaps
_/
snippets.snapStud = new Snippet('snap-stud', points.snapLeft)
snippets.snapSocket = new Snippet('snap-socket', points.snapRight).attr('opacity', 0.5)
/\*
_ Add the bias tape
_/
if (complete)
paths.bias = paths.seam
.offset(-5)
.addClass('note dashed')
.addText('finishWithBiasTape', 'center fill-note')
/\*
_ Add the title
_/
points.title = points.bottom.shift(-90, 45)
macro('title', {
at: points.title,
nr: 1,
title: 'bib',
align: 'center',
scale: 0.8,
})
/\*
_ Add the scalebox
_/
points.scalebox = points.title.shift(-90, 65)
macro('scalebox', { at: points.scalebox })
/\*
_ Add the logo
_/
points.logo = new Point(0, 0)
snippets.logo = new Snippet('logo', points.logo)
/\*
_ Add dimensions
_/
macro('hd', {
id: 'wFull',
from: points.bottomLeftStart,
to: points.bottomRightEnd,
y: points.bottomLeft.y + 15,
})
macro('vd', {
id: 'hBottomToOpeningBottom',
from: points.bottomRightStart,
to: points.bottom,
x: points.bottomRight.x + 15,
})
macro('vd', {
id: 'hBottomToOpeningCenter',
from: points.bottomRightStart,
to: points.right,
x: points.bottomRight.x + 30,
})
macro('vd', {
id: 'hTotal',
from: points.bottomRightStart,
to: points.tipLeftTopStart,
x: points.bottomRight.x + 45,
})
macro('hd', {
id: 'wOpening',
from: points.left,
to: points.right,
y: points.left.y + 25,
})
macro('ld', {
id: 'wStrap',
from: points.tipLeftBottomEnd,
to: points.tipLeftTopStart,
d: -15,
})
return part
}
```
</Example>
```

View file

@ -1,6 +1,6 @@
---
title: Supporting translation
order: 80
sidebar_position: 80
---
:::note [FIXME]

View file

@ -1,6 +1,6 @@
---
title: Facilitating frontend integration
order: 60
sidebar_position: 60
---
Strictly speaking, this tutorial is about learning to use FreeSewing's core

View file

@ -1,6 +1,6 @@
---
title: Supporting paperless patterns
order: 30
sidebar_position: 30
---
The goal of paperless patterns is to create a pattern that we don't need to
@ -14,14 +14,14 @@ So let's make the extra effort to make our bib design support paperless.
## The paperless setting
Users can request paperless patterns by setting [the `paperless`
setting](/reference/settings/paperless) to a *truthy* value.
setting](/reference/settings/paperless) to a _truthy_ value.
With paperless enabled, FreeSewing will automatically render a grid for each
pattern part with metric or imperial markings, depending on the units requested
by the user.
Such a grid is already a good starting point. In addition, we'll be using
different macros to add *dimensions* to the pattern.
different macros to add _dimensions_ to the pattern.
While the grid gets added automatically, the dimensions we have to add ourselves.
Thankfully, there's macros that can help us with that, specifically:
@ -58,15 +58,15 @@ function draftBib({
part,
}) {
/*
* Construct the neck opening
*/
const target = (measurements.head * options.neckRatio) / 4
let tweak = 1
let delta
do {
points.right = new Point((tweak * measurements.head) / 10, 0)
points.bottom = new Point(0, (tweak * measurements.head) / 12)
/\*
_ Construct the neck opening
_/
const target = (measurements.head _ options.neckRatio) / 4
let tweak = 1
let delta
do {
points.right = new Point((tweak _ measurements.head) / 10, 0)
points.bottom = new Point(0, (tweak \* measurements.head) / 12)
points.rightCp1 = points.right.shift(90, points.bottom.dy(points.right) / 2)
points.bottomCp2 = points.bottom.shift(0, points.bottom.dx(points.right) / 2)
@ -78,267 +78,268 @@ function draftBib({
delta = paths.neck.length() - target
if (delta > 0) tweak = tweak * 0.99
else tweak = tweak * 1.02
} while (Math.abs(delta) > 1)
points.rightCp2 = points.rightCp1.flipY()
points.bottomCp1 = points.bottomCp2.flipX()
} while (Math.abs(delta) > 1)
points.left = points.right.flipX()
points.leftCp1 = points.rightCp2.flipX()
points.leftCp2 = points.rightCp1.flipX()
points.rightCp2 = points.rightCp1.flipY()
points.bottomCp1 = points.bottomCp2.flipX()
points.top = points.bottom.flipY()
points.topCp1 = points.bottomCp2.flipY()
points.topCp2 = points.bottomCp1.flipY()
points.left = points.right.flipX()
points.leftCp1 = points.rightCp2.flipX()
points.leftCp2 = points.rightCp1.flipX()
/*
* Construct the outline
*/
let width = measurements.head * options.widthRatio
let length = measurements.head * options.lengthRatio
points.top = points.bottom.flipY()
points.topCp1 = points.bottomCp2.flipY()
points.topCp2 = points.bottomCp1.flipY()
points.topLeft = new Point(width / -2, points.top.y - (width / 2 - points.right.x))
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
/\*
_ Construct the outline
_/
let width = measurements.head _ options.widthRatio
let length = measurements.head _ options.lengthRatio
points.edgeLeft = new Point(points.topLeft.x, points.left.y)
points.edgeRight = new Point(points.topRight.x, points.right.y)
points.edgeTop = new Point(0, points.topLeft.y)
points.topLeft = new Point(width / -2, points.top.y - (width / 2 - points.right.x))
points.topRight = points.topLeft.shift(0, width)
points.bottomLeft = points.topLeft.shift(-90, length)
points.bottomRight = points.topRight.shift(-90, length)
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
points.edgeRightCp = points.edgeLeftCp.flipX()
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(points.topLeft, 0.5)
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.edgeLeft = new Point(points.topLeft.x, points.left.y)
points.edgeRight = new Point(points.topRight.x, points.right.y)
points.edgeTop = new Point(0, points.topLeft.y)
/*
* Round the end of the straps
*/
let strap = points.edgeTop.dy(points.top)
points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
points.edgeRightCp = points.edgeLeftCp.flipX()
points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(points.topLeft, 0.5)
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
points.tipRightBottom = new Point(points.tipRight.x, points.top.y)
/\*
_ Round the end of the straps
_/
let strap = points.edgeTop.dy(points.top)
/*
* Macros will return the auto-generated IDs
*/
const ids1 = {
tipRightTop: macro('round', {
id: 'tipRightTop',
from: points.edgeTop,
to: points.tipRight,
via: points.tipRightTop,
}),
tipRightBottom: macro('round', {
id: 'tipRightBottom',
from: points.tipRight,
to: points.top,
via: points.tipRightBottom,
}),
}
points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
points.tipRightBottom = new Point(points.tipRight.x, points.top.y)
/*
* Create points from them with easy names
*/
for (const side in ids1) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
}
}
/*
* Rotate straps so they don't overlap
*/
let rotateThese = [
'edgeTopLeftCp',
'edgeTop',
'tipRight',
'tipRightTop',
'tipRightTopStart',
'tipRightTopCp1',
'tipRightTopCp2',
'tipRightTopEnd',
'tipRightBottomStart',
'tipRightBottomCp1',
'tipRightBottomCp2',
'tipRightBottomEnd',
'tipRightBottom',
'top',
'topCp2',
]
while (points.tipRightBottomStart.x > -1) {
for (let p of rotateThese) points[p] = points[p].rotate(1, points.edgeLeft)
}
/*
* Add points to anchor snaps on
*/
points.snapLeft = points.top.shiftFractionTowards(points.edgeTop, 0.5)
/*
* Mirror points to the other side
*/
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.topCp1 = points.topCp2.flipX()
points.tipLeftTopStart = points.tipRightTopStart.flipX()
points.tipLeftTopCp1 = points.tipRightTopCp1.flipX()
points.tipLeftTopCp2 = points.tipRightTopCp2.flipX()
points.tipLeftTopEnd = points.tipRightTopEnd.flipX()
points.tipLeftBottomStart = points.tipRightBottomStart.flipX()
points.tipLeftBottomCp1 = points.tipRightBottomCp1.flipX()
points.tipLeftBottomCp2 = points.tipRightBottomCp2.flipX()
points.tipLeftBottomEnd = points.tipRightBottomEnd.flipX()
points.snapRight = points.snapLeft.flipX()
/*
* Round the bottom of the bib
* Radius is fixed, but you could use an option for it)
*
* Macros will return the auto-generated IDs
*/
const ids2 = {
bottomLeft: macro('round', {
id: 'bottomLeft',
from: points.topLeft,
to: points.bottomRight,
via: points.bottomLeft,
radius: points.bottomRight.x / 4,
}),
bottomRight: macro('round', {
id: 'bottomRight',
from: points.bottomLeft,
to: points.topRight,
via: points.bottomRight,
radius: points.bottomRight.x / 4,
}),
}
/*
* Create points from them with easy names
*/
for (const side in ids2) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids2[side].points[id]].copy()
}
}
/*
* Construct the path
*/
paths.seam = new Path()
.move(points.edgeLeft)
.line(points.bottomLeftStart)
.curve(points.bottomLeftCp1, points.bottomLeftCp2, points.bottomLeftEnd)
.line(points.bottomRightStart)
.curve(points.bottomRightCp1, points.bottomRightCp2, points.bottomRightEnd)
.line(points.edgeRight)
.curve(points.edgeRightCp, points.edgeTopRightCp, points.tipLeftTopStart)
.curve(points.tipLeftTopCp1, points.tipLeftTopCp2, points.tipLeftTopEnd)
.curve(points.tipLeftBottomCp1, points.tipLeftBottomCp2, points.tipLeftBottomEnd)
.curve(points.topCp1, points.rightCp2, points.right)
.curve(points.rightCp1, points.bottomCp2, points.bottom)
.curve(points.bottomCp1, points.leftCp2, points.left)
.curve(points.leftCp1, points.topCp2, points.tipRightBottomEnd)
.curve(points.tipRightBottomCp2, points.tipRightBottomCp1, points.tipRightBottomStart)
.curve(points.tipRightTopCp2, points.tipRightTopCp1, points.tipRightTopStart)
.curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
.close()
.attr('class', 'fabric')
/*
*
* Annotations
*
*/
/*
* Cut list
*/
store.cutlist.addCut({ cut: 1, from: 'fabric' })
/*
* Add the snaps
*/
snippets.snapStud = new Snippet('snap-stud', points.snapLeft)
snippets.snapSocket = new Snippet('snap-socket', points.snapRight).attr('opacity', 0.5)
/*
* Add the bias tape
*/
if (complete)
paths.bias = paths.seam
.offset(-5)
.addClass('note dashed')
.addText('fronscratch:finishWithBiasTape', 'center fill-note')
/*
* Add the title
*/
points.title = points.bottom.shift(-90, 45)
macro('title', {
at: points.title,
nr: 1,
title: 'bib',
align: 'center',
scale: 0.8,
})
/*
* Add the scalebox
*/
points.scalebox = points.title.shift(-90, 65)
macro('scalebox', { at: points.scalebox })
/*
* Add the logo
*/
points.logo = new Point(0, 0)
snippets.logo = new Snippet('logo', points.logo)
// highlight-start
/*
* Add dimensions
*/
macro('hd', {
id: 'wFull',
from: points.bottomLeftStart,
to: points.bottomRightEnd,
y: points.bottomLeft.y + 15,
})
macro('vd', {
id: 'hBottomToOpeningBottom',
from: points.bottomRightStart,
to: points.bottom,
x: points.bottomRight.x + 15,
})
macro('vd', {
id: 'hBottomToOpeningCenter',
from: points.bottomRightStart,
to: points.right,
x: points.bottomRight.x + 30,
})
macro('vd', {
id: 'hTotal',
from: points.bottomRightStart,
to: points.tipLeftTopStart,
x: points.bottomRight.x + 45,
})
macro('hd', {
id: 'wOpening',
from: points.left,
to: points.right,
y: points.left.y + 25,
})
macro('ld', {
id: 'wStrap',
from: points.tipLeftBottomEnd,
to: points.tipLeftTopStart,
d: -15,
})
// highlight-end
return part
/\*
_ Macros will return the auto-generated IDs
_/
const ids1 = {
tipRightTop: macro('round', {
id: 'tipRightTop',
from: points.edgeTop,
to: points.tipRight,
via: points.tipRightTop,
}),
tipRightBottom: macro('round', {
id: 'tipRightBottom',
from: points.tipRight,
to: points.top,
via: points.tipRightBottom,
}),
}
/\*
_ Create points from them with easy names
_/
for (const side in ids1) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
}
}
/\*
_ Rotate straps so they don't overlap
_/
let rotateThese = [
'edgeTopLeftCp',
'edgeTop',
'tipRight',
'tipRightTop',
'tipRightTopStart',
'tipRightTopCp1',
'tipRightTopCp2',
'tipRightTopEnd',
'tipRightBottomStart',
'tipRightBottomCp1',
'tipRightBottomCp2',
'tipRightBottomEnd',
'tipRightBottom',
'top',
'topCp2',
]
while (points.tipRightBottomStart.x > -1) {
for (let p of rotateThese) points[p] = points[p].rotate(1, points.edgeLeft)
}
/\*
_ Add points to anchor snaps on
_/
points.snapLeft = points.top.shiftFractionTowards(points.edgeTop, 0.5)
/\*
_ Mirror points to the other side
_/
points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
points.topCp1 = points.topCp2.flipX()
points.tipLeftTopStart = points.tipRightTopStart.flipX()
points.tipLeftTopCp1 = points.tipRightTopCp1.flipX()
points.tipLeftTopCp2 = points.tipRightTopCp2.flipX()
points.tipLeftTopEnd = points.tipRightTopEnd.flipX()
points.tipLeftBottomStart = points.tipRightBottomStart.flipX()
points.tipLeftBottomCp1 = points.tipRightBottomCp1.flipX()
points.tipLeftBottomCp2 = points.tipRightBottomCp2.flipX()
points.tipLeftBottomEnd = points.tipRightBottomEnd.flipX()
points.snapRight = points.snapLeft.flipX()
/\*
_ Round the bottom of the bib
_ Radius is fixed, but you could use an option for it) \*
_ Macros will return the auto-generated IDs
_/
const ids2 = {
bottomLeft: macro('round', {
id: 'bottomLeft',
from: points.topLeft,
to: points.bottomRight,
via: points.bottomLeft,
radius: points.bottomRight.x / 4,
}),
bottomRight: macro('round', {
id: 'bottomRight',
from: points.bottomLeft,
to: points.topRight,
via: points.bottomRight,
radius: points.bottomRight.x / 4,
}),
}
/\*
_ Create points from them with easy names
_/
for (const side in ids2) {
for (const id of ['start', 'cp1', 'cp2', 'end']) {
points[`${side}${utils.capitalize(id)}`] = points[ids2[side].points[id]].copy()
}
}
/\*
_ Construct the path
_/
paths.seam = new Path()
.move(points.edgeLeft)
.line(points.bottomLeftStart)
.curve(points.bottomLeftCp1, points.bottomLeftCp2, points.bottomLeftEnd)
.line(points.bottomRightStart)
.curve(points.bottomRightCp1, points.bottomRightCp2, points.bottomRightEnd)
.line(points.edgeRight)
.curve(points.edgeRightCp, points.edgeTopRightCp, points.tipLeftTopStart)
.curve(points.tipLeftTopCp1, points.tipLeftTopCp2, points.tipLeftTopEnd)
.curve(points.tipLeftBottomCp1, points.tipLeftBottomCp2, points.tipLeftBottomEnd)
.curve(points.topCp1, points.rightCp2, points.right)
.curve(points.rightCp1, points.bottomCp2, points.bottom)
.curve(points.bottomCp1, points.leftCp2, points.left)
.curve(points.leftCp1, points.topCp2, points.tipRightBottomEnd)
.curve(points.tipRightBottomCp2, points.tipRightBottomCp1, points.tipRightBottomStart)
.curve(points.tipRightTopCp2, points.tipRightTopCp1, points.tipRightTopStart)
.curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
.close()
.attr('class', 'fabric')
/\* \*
_ Annotations
_
\*/
/\*
_ Cut list
_/
store.cutlist.addCut({ cut: 1, from: 'fabric' })
/\*
_ Add the snaps
_/
snippets.snapStud = new Snippet('snap-stud', points.snapLeft)
snippets.snapSocket = new Snippet('snap-socket', points.snapRight).attr('opacity', 0.5)
/\*
_ Add the bias tape
_/
if (complete)
paths.bias = paths.seam
.offset(-5)
.addClass('note dashed')
.addText('fronscratch:finishWithBiasTape', 'center fill-note')
/\*
_ Add the title
_/
points.title = points.bottom.shift(-90, 45)
macro('title', {
at: points.title,
nr: 1,
title: 'bib',
align: 'center',
scale: 0.8,
})
/\*
_ Add the scalebox
_/
points.scalebox = points.title.shift(-90, 65)
macro('scalebox', { at: points.scalebox })
/\*
_ Add the logo
_/
points.logo = new Point(0, 0)
snippets.logo = new Snippet('logo', points.logo)
// highlight-start
/\*
_ Add dimensions
_/
macro('hd', {
id: 'wFull',
from: points.bottomLeftStart,
to: points.bottomRightEnd,
y: points.bottomLeft.y + 15,
})
macro('vd', {
id: 'hBottomToOpeningBottom',
from: points.bottomRightStart,
to: points.bottom,
x: points.bottomRight.x + 15,
})
macro('vd', {
id: 'hBottomToOpeningCenter',
from: points.bottomRightStart,
to: points.right,
x: points.bottomRight.x + 30,
})
macro('vd', {
id: 'hTotal',
from: points.bottomRightStart,
to: points.tipLeftTopStart,
x: points.bottomRight.x + 45,
})
macro('hd', {
id: 'wOpening',
from: points.left,
to: points.right,
y: points.left.y + 25,
})
macro('ld', {
id: 'wStrap',
from: points.tipLeftBottomEnd,
to: points.tipLeftTopStart,
d: -15,
})
// highlight-end
return part
}
```
</Example>
```

View file

@ -1,6 +1,6 @@
---
title: Adding seam allowance
order: 10
sidebar_position: 10
---
When adding seam allowance to a pattern, there are 3 things that come into play:
@ -24,29 +24,30 @@ function draftBib({
part,
}) {
points.topLeft = new Point(0,0)
points.bottomRight = new Point(100,40)
points.topRight = new Point(points.bottomRight.x, points.topLeft.y)
points.bottomLeft = new Point(points.topLeft.x, points.bottomRight.y)
points.cp1 = new Point(50, 20)
points.cp2 = new Point(70, 60)
points.topLeft = new Point(0,0)
points.bottomRight = new Point(100,40)
points.topRight = new Point(points.bottomRight.x, points.topLeft.y)
points.bottomLeft = new Point(points.topLeft.x, points.bottomRight.y)
points.cp1 = new Point(50, 20)
points.cp2 = new Point(70, 60)
paths.shape = new Path()
.move(points.topLeft)
.line(points.bottomLeft)
.curve(points.cp1, points.cp2, points.bottomRight)
.line(points.topRight)
.line(points.topLeft)
.close()
.addClass('fabric')
paths.shape = new Path()
.move(points.topLeft)
.line(points.bottomLeft)
.curve(points.cp1, points.cp2, points.bottomRight)
.line(points.topRight)
.line(points.topLeft)
.close()
.addClass('fabric')
// highlight-start
if (sa) paths.sa = paths.shape.offset(sa).addClass('fabric sa')
// highlight-end
// highlight-start
if (sa) paths.sa = paths.shape.offset(sa).addClass('fabric sa')
// highlight-end
return part
return part
}
```
````
</Example>
As you can see from the source, we can destructure an `sa` variable (short for
@ -63,7 +64,7 @@ seam allowance:
if (sa) paths.sa = paths.shape
.offset(sa)
.addClass('fabric sa')
```
````
To refer back to our three question: Whether the user wants seam allowance, and
if so how much seam allowance is answered by the `sa` value passed to our draft

View file

@ -1,6 +1,6 @@
---
title: Testing your designs
order: 70
sidebar_position: 70
---
With the basic outline of our pattern ready, now would be a good time

View file

@ -1,6 +1,5 @@
---
title: Tutorials
order: zaa
---
You can find a list of all FreeSewing tutorials below:
@ -14,4 +13,3 @@ You can find a list of all FreeSewing tutorials below:
Tutorials are lessons that take you by the hand through a series of steps to complete a project.
:::

View file

@ -1,74 +1,46 @@
{
"name": "@freesewing/dev",
"version": "3.3.0-rc.1",
"description": "FreeSewing website with documentation for contributors & developers",
"author": "Joost De Cock <joost@joost.at> (https://github.com/joostdecock)",
"homepage": "https://freesewing.org/",
"repository": "github:freesewing/freesewing",
"license": "MIT",
"bugs": {
"url": "https://github.com/freesewing/freesewing/issues"
},
"funding": {
"type": "individual",
"url": "https://freesewing.org/patrons/join"
},
"name": "dev",
"version": "0.0.0",
"private": true,
"scripts": {
"build": "next build",
"cibuild": "yarn build && node scripts/algolia.mjs",
"clean": "rimraf prebuild/* && rimraf public/locales/*/* && rimraf public/feeds/* && rimraf ../shared/prebuild/data/*",
"dev": "next dev -p 8000",
"develop": "next dev -p 8000",
"i18n": "SITE=dev node --conditions=internal ../shared/prebuild/i18n-only.mjs",
"lint": "next lint",
"prebuild": "node --conditions=internal --experimental-json-modules ./prebuild.mjs",
"serve": "pm2 start npm --name 'dev' -- run start",
"start": "yarn prebuild && yarn dev",
"wbuild": "next build",
"prewbuild": "node --conditions=internal --experimental-json-modules ./prebuild.mjs"
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids"
},
"peerDependencies": {},
"dependencies": {
"@mdx-js/mdx": "^3.0.0",
"@docusaurus/core": "3.5.2",
"@docusaurus/preset-classic": "3.5.2",
"@mdx-js/react": "^3.0.0",
"@mdx-js/runtime": "2.0.0-next.9",
"@next/bundle-analyzer": "14.2.3",
"@tailwindcss/typography": "0.5.13",
"algoliasearch": "4.23.3",
"daisyui": "4.11.1",
"lodash.get": "4.4.2",
"lodash.orderby": "4.6.0",
"lodash.set": "4.3.2",
"next": "14.2.3",
"react": "18.3.1",
"react-copy-to-clipboard": "5.1.0",
"react-dom": "18.3.1",
"react-hotkeys-hook": "4.5.0",
"react-instantsearch-dom": "6.40.4",
"react-instantsearch-hooks-web": "6.47.3",
"react-swipeable": "7.0.1",
"react-timeago": "7.2.0",
"rehype-autolink-headings": "7.1.0",
"rehype-highlight": "7.0.0",
"rehype-sanitize": "6.0.0",
"rehype-slug": "6.0.0",
"rehype-stringify": "10.0.1",
"remark": "15.0.1",
"remark-copy-linked-files": "git+https://git@github.com/joostdecock/remark-copy-linked-files",
"remark-gfm": "4.0.0",
"strip-markdown": "6.0.0"
"clsx": "^2.0.0",
"prism-react-renderer": "^2.3.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-markdown": "^9.0.1",
"tailwindcss": "^3.4.13"
},
"devDependencies": {
"autoprefixer": "10.4.19",
"js-yaml": "4.1.0",
"postcss": "8.4.38",
"remark-extract-frontmatter": "3.2.0",
"remark-mdx-frontmatter": "5.0.0",
"tailwindcss": "3.4.3",
"yaml-loader": "0.8.1"
"@docusaurus/module-type-aliases": "3.5.2",
"@docusaurus/types": "3.5.2"
},
"browserslist": {
"production": [
">0.5%",
"not dead",
"not op_mini all"
],
"development": [
"last 3 chrome version",
"last 3 firefox version",
"last 5 safari version"
]
},
"engines": {
"node": ">= 18.17.0 <22"
},
"private": true
"node": ">=18.0"
}
}

View file

@ -0,0 +1,41 @@
---
authors: 1
caption: 'Photo by Anna Doschechko [via Pexels]'
date: '2022-01-24'
intro: 'FreeSewing 2.20: Would you like it smaller?'
title: 'FreeSewing 2.20: Would you like it smaller?'
---
I'm happy to announce that we've released FreeSewing 2.20, which comes with a
feature request that was on our roadmap: Better support for doll clothes. We
have some passionate doll enthusiasts in our community which make sense when
you consider that our patterns adapt seamlessly to all sorts of measurements,
including those of dolls.
<!-- truncate -->
But there's another issue that comes up when generating these tiny patterns,
that is that while the pattern itself adapts to the doll measurements, things
like font sizes and arrows and logos do not. That causes doll patterns to look
like a chaotic mess as the things that are typically taking up just a bit of
space tend to overwhelm the actual pattern:
![Image showing a doll pattern with snippets and text obfuscating the
pattern](https://posts.freesewing.org/uploads/pres_scale_de0edf2cd7.png 'This
is Aaron for a 1/10 size doll in 2.19. Not great')
![Image showing a doll pattern with snippets and text scaled
down](https://posts.freesewing.org/uploads/post_scale_5a422f8c73.png "This is
the same Aaron in 2.20. As you can see, it's much better")
To make this happen, we've added [a new scale
setting](/docs/about/site/draft#scale) that you can find under _Advanced_ after
enabling _Expert mode_. We hope those of you making doll clothes will find
this useful, and as always [we look forward to hearing your
feedback](https://discord.freesewing.org/).
## More in this release
2.20 also packs a bunch of changes and improvements, check the
[changelog](https://github.com/freesewing/freesewing/blob/develop/CHANGELOG.md#2200-2022-01-24)
for the full list.

View file

@ -0,0 +1,89 @@
---
authors: 1
caption: 'Close-up of a dandelion against a pink background'
date: '2019-12-10'
intro: '4109 reasons to be happy about 2019'
title: '4109 reasons to be happy about 2019'
---
Wow! What a year it's been for FreeSewing.
In August we released version 2.0 which was nothing less than a complete rewrite
of our entire technology stack.
For our users, the most obvious change is that you get to see your pattern adapt
live in your browser as you tweak options and preferences.
It's one of those things that I knew should be possible with the state of web
technology today, yet seeing it actually happen still boggles my mind somehow.
<!-- truncate -->
## Scaling is hard, but we made remarkable progress
Cool as our new technoglogy stack is, it is not the most important work we've done over the last
year. Yes, it's pretty neat, and yes it's only possible because of the work done
on 2.0. But that work itself is what matters most. The main reason for the 2.0
rewrite was to allow the project to scale horizontally. Or to put it bluntly,
to allow FreeSewing to go where I could not carry it on my own.
In a way, FreeSewing has grown up as a (software) project. We have several people
making regular contributions, [an active chat room](https://discord.freesewing.org/),
[a dedicated website for developer and translator documentation](https://freesewing.dev),
a plethora of [packages we publish on NPM](https://www.npmjs.com/search?q=keywords:freesewing),
we've contributed fixes and improvements to upstream software we depend on, and we now
also have other people and teams who depend on the packages we put out.
We (currently) have [23 patterns available](/designs/), we publish 62 packages on NPM
(the Node.js package registry). Since putting out version 2.0, on average 450 people
sign up every month, and our total tally currently stands above 15.000.
## Translation has never been easier
Since v2, we've also switched to [Crowdin](https://crowdin.com) for
[our translations](https://freesewing.dev/guides/translator/). Translation is arguably
the best way to democratize access to our platform, and I'd like to give a shout-out to
all people who have helped and continue to help with these efforts.
It's perhaps also a good time to point out that you too can help with this.
Our [documentation for translators](https://freesewing.dev/guides/translator/) is a good
place to get started, or stop by [our chat room](https://discord.freesewing.org/).
## What we're planning for next year
Our next year plans roughly fall apart into two categories:
improving our platform, and adding more patterns.
As the project grows, so does the amount of work required to keep everything
running smoothly. We still have more performance improvements to do, as well as
auxiliary tasks such as writing more tests so we can go ahead and change things with
confidence, rather than risk that rolling out a new feature causes bugs down the line.
We also know there is a lot of room for improvement of the user experience (UX), as
well as design and user interface (UI). We've been doing the best we can, but it's
not really our field of expertise, and we're hoping to find more contributors who can
help us in this regard.
## v2.2 will include a ladies bodice block
But hey, you want more patterns, right? So rest assured that that's high on our
todo list. We are going to apply some affirmative action towards the ladies who have
so far been under-served. Not only by making existing patterns available to them
(as we did recently with Simone, a ladies version of our Simon pattern) but also
by developing a dedicated ladies block to develop patterns on.
We have earmarked this as a must-have for FreeSewing v2.2, which we hope to be able
to release sometime in January.
We're also looking at ways to get more designers on board with FreeSewing.
One plan on the drawing board is to offer pair-programming sessions to designers
where they walk us through their vision, and we implement their design in FreeSewing.
We're even thinking of live-streaming these sessions so anybody who is interested
can drop by and follow along.
## 'Tis the season for giving
Thanks to our awesome patrons, revenue was up this year too. As you may or may not know,
FreeSewing donates 100% of its revenue to Médecins Sans Frontières/Doctors Without Borders.
So this morning, I had the great honour to write a 4109.38€ cheque to [MSF](https://www.msf.org/).
That felt **real good** so thanks to [all our patrons](/patrons) for their continued support.
If you'd like to join this awesome group of people, [you can do so here](/patrons/join).

View file

@ -0,0 +1,68 @@
---
authors: 1
caption: 'Picture by Snapwire - Via pexels.com'
date: '2022-01-01'
intro: "I'm not going to do a whole blog post about 2021 stuff because I feel like most of us just sorta want to move on and forget about it, but if you were looking for a longer read, then here's the table of content of our latest newsletter edition that also went out today:"
title: '2021 wrap-up: A new FreeSewing.dev and announcing our bug bounty program'
---
I'm not going to do a whole blog post about 2021 stuff because I feel like most
of us just sorta want to move on and forget about it, but if you were looking
for a longer read, then here's the table of content of [our latest newsletter
edition](/newsletter/2022q1/) that also went out today:
<!-- truncate -->
- 🎉 2021 is salted and burned
- 🧐 What our contributors have been up to in 2021
- 🎖️ FreeSewing is now an 'all contributors' project
- 🚧 Why version 3 has been put on hold
- 🤓 What I've been up to in 2021
- 🐛 FreeSewing's bug bounty program
- ⛑️ Yearly revenue and where it went (spoiler: same as always)
- 🤞 What I hope will happen this year
Here, I'd like to cherry-pick just those things that I think are exciting right
now.
## freesewing.dev has been rebuilt
[The effort I started in the summer](https://freesewing.dev/blog/project-2022)
came to fruition on the last day of the year as I deployed the new
[freesewing.dev](https://freesewing.dev/blog/project-2022) site in production.
It's a complete redesign, and the code is now [hosted in our
monorepo](https://github.com/freesewing/freesewing), which means that [our
dedicated repository for
freesewing.dev](https://github.com/freesewing/freesewing.dev) has now been
archived.
This effort implemented a bunch of items from [our v3
roadmap](https://github.com/freesewing/freesewing/discussions/1278) which has
sort of grown into this long list of ideas/plans. From the top of my head:
- Migrate to NextJS
- Better open graph support
- Migrate style to TailwindCSS
- Migrate blog posts and showcase posts to Strapi
- Migrate newsletter to Strapi
- Move markdown content into monorepo & merge Crowdin translation projects
- Add endpoint to backend for auto-generated open graph images
Have all been implemented as a direct result or side effect of this effort.
This site will also become the blueprint for an overhaul of freesewing.org,
something that's on the planning for this year.
## FreeSewing's bug bounty program
Once again, [read our newsletter](/newsletter/2022q1/) for the entire
backstory, but here's the gist of it: We are now launching the FreeSewing bug
bounty program:
> If you find a bug in one of our patterns, or in our core library, we will
> (with your permission) add you to our list of contributors, and send you a
> little something to say thanks.
So keep your eyes peeled, and if something seems off, [let us know about
it](https://discord.freesewing.org/) and we'll send you some goodies

View file

@ -0,0 +1,247 @@
---
authors: 1
caption: 'Note: This post is a long read. Although nowhere near as long as this book'
date: '2020-08-30'
intro: 'Please help grow FreeSewing beyond what I can do on my own'
title: 'Please help grow FreeSewing beyond what I can do on my own'
---
Hi everyone, Joost here. I'm writing this post to address some problems that
have been worrying me lately. Specifically, these problems:
<!-- truncate -->
1. [There is too much work for one
person](#problem-1-there-is-too-much-work-for-one-person) 2. [I feel I'm
losing track of the sewing
community](#problem-2-i-feel-im-losing-track-of-the-community) 3. [I feel
insecure about how to deal with the issue of systemic
racism](#problem-3-i-feel-insecure-about-how-to-deal-with-the-issue-of-systemic-racism)
The good news is that it's a relatively short list. The even better news is
that all of these problems be addressed by the same solution: [Community
building](#community-building).
Before we get into that, let's briefly look at each problem:
## Problem 1: There is too much work for one person
Over the course of the last week I read [Working in public: The making and
maintenance of open source software](https://www.amazon.com/dp/0578675862/) by
[Nadia Eghbal](https://nadiaeghbal.com/).
I bought it because I was hoping to find answers to some of the questions that
I ask myself. Questions like “_How do other maintainers do it?_”, or “_Am I
doing it wrong?_”
In other words, I was hoping to find a fix for what I increasingly perceive as
a problem: The inability to scale my own labour in line with how I'd like to
scale FreeSewing, the project.
I don't want to spoil the book, but it didn't provide any straightforward
answers on how to address that problem. It turns out that the vast majority of
open source maintainers are in the same boat. Most projects are run by either a
single person, or a handful of people.
There's nothing wrong with that. But it does put a firm upper limit on how much
projects like FreeSewing can accomplish.
## Problem 2: I feel I'm losing track of the sewing community
I worry that I have been neglecting the communal aspects of FreeSewing, there
are no comments or _social_ aspects on the site. I've always felt it was a
fool's errand to try to corral people onto your own website. Better to let
them have discussions on the platforms of their choice.
For the sewing community, the platform of choice is often Instagram. Since I
have left Instagram a year ago, I feel like I'm getting further away from the
sewing community.
My [reasons for
leaving](https://joost.decock.org/post/187710847164/24-hours-from-now-i-want-to-remove-my-instagram)
are as valid today as they were back then, but I wish I could connect with the
sewing community in a way that works for me.
## Problem 3: I feel insecure about how to deal with the issue of systemic
racism.
First things first: **Black lives matter** ✊🏾
My insecurity stems from my environment. I am a white, middle-aged, cis-gender
man who was born and raised in a country with a history drenched in the blood
of people of color ([that country is
Belgium](https://en.wikipedia.org/wiki/Atrocities_in_the_Congo_Free_State)).
To this day, casual racism permeates all aspects of the society I live in.
A welcoming and diverse community is a _sine qua non_ for me. But I feel
ill-equipped to figure out how to create one on FreeSewing.
## Community building
As I mentioned earlier, these things have been on my mind for a while, albeit
they were a lot more fuzzy. Then earlier this month I listened to [Black
makers matter with Julian Collins on the podcast Love To
Sew](https://lovetosewpodcast.com/episodes/episode-156-black-makers-matter-with-julian-collins/).
[Julian](https://www.instagram.com/juliancreates/) is a patron of FreeSewing
(thanks Julian) and actively involved with the [Black Makers
Matter](https://www.instagram.com/blkmakersmatter/) movement on Instagram. I
reached out to Julian looking for help, and we had a lengthy Zoom call where we
talked about his work and how he goes about organizing the community.
Julian had a lot of good advice. I couldn't possibly cram it all into this
post, but it sort of boils down to:
- Just ask people for help
- Be clear about what kind of community you want to build
So I am taking Julian's advice to heart, and asking for help. Before we get to
that though, let's make sure we're all on the same page about the kind of
community we're trying to build here.
## Quick check: Are you on board with FreeSewing's values?
To ensure that your values are aligned with those of FreeSewing, please take a
moment to familiarize yourself with:
- [Our community standards](/docs/about/community-standards/)
- [Our code of conduct](https://freesewing.dev/contributors/code-of-conduct/)
- [Our revenue pledge](/docs/about/pledge/)
If reading that made you happy rather than angry, we could use your help :)
## Please help grow FreeSewing beyond what I can do on my own
We're starting simple: We plan to hold a Zoom/Skype/Whatever call every 2 weeks
to figure it out as we go. We start the first weekend of September (next
weekend). We haven't picked a time yet, for it will depend on the time zones
the participants live in.
If you'd like to attend, please [let us know in our chat
room](https://discord.freesewing.org/).
### What kind of help is needed?
Beggars can't be choosers. All help is welcome, and I certainly don't want to
turn down any volunteers.
That being said, an overly vague call defuses the message. So I've
listed/included a number of _roles_ below to give you an idea of the kind of
work that goes into FreeSewing. It's not meant to be an exhaustive list, but
merely a starting point for a discussion.
The order is alphabetic.
**Backend Developer** You keep our backend in step with the latest frontend
developments. Express is no stranger to you. Node JS is a good friend. Or
maybe you'd like them to be.
**Body Ambassador** Maybe you're unusually short or tall. Maybe you have a bit
of a pot belly or very large breasts. Maybe you have a disability that requires
fit adjustments. Whatever it is, you represent a minority fitting issue, and
are willing to act as an ambassador to make sure your needs are heard and
understood.
**Community Builder** You're an extrovert extraordinaire, or you're good at
faking it. You enjoy chatting with all sorts of people, and networking is just
you doing you. You're like the jelly that molds a group of individuals into a
cohesive community.
**Database Administrator** You look after our database. Other people might
feel that's not important, but you know better. You're familiar with MongoDB.
**Devops Engineer** Your aim is to make almost all these other roles
irrelevant by automating the heck out of everything. CI and Github actions are
fun for you. You like to sit back and have the robots do the work for you.
**Frontend Developer** You improve our websites, specifically freesewing.org
and freesewing.dev. Both of them are built with
[Gatsby](https://www.gatsbyjs.com/), an open source framework for building
frontends that is powered by [React](https://reactjs.org/). If you know these
things, or would like to learn them, this is your jam.
**Illustrator** You create illustrations to go alongside the written
documentation. If you draw a bicycle from memory, it actually looks like a
bicycle.
**Inclusion & Diversity Manager** You have skin in the game when it comes to
inclusion and diversity. You'll help make our community welcoming and diverse.
You won't be afraid to tell this pasty white dude when he's wrong.
**Language Ambassador** You represent FreeSewing in a non-English community.
You can help answer questions or triage problem reports. Or you can point out
where translations are missing.
**Pattern Ambassador** You'll be responsible for a specific FreeSewing
design/pattern. You'll be _the person_ to ask questions about how to make that
pattern. You'll make sure the documentation is not forgotten. And you can
help with questions or triage problem reports to developers or designers.
**Pattern Designer** You come up with new pattern designs for FreeSewing. You
might not know how to turn on a computer, but damn if you can't draft a bodice.
**Pattern Developer** You program new designs for FreeSewing. You might not
know how to design sewing patterns, but you're not afraid of Javascript and are
happy to team up with a designer to work on a new pattern together.
**Proofreader** You check original English text of translations for typos
and/or grammar mistakes. You propose improvements and watch over a consistent
style and tone across FreeSewing's documentation and written text. You're
fluent in the language you're proofreading.
**Social Media Platform Manager** You represent FreeSewing on a _platform_,
where platform could be Facebook, Twitter, Instagram, Tiktok, Snapchat, Reddit
&hellip;. You manage the FreeSewing account on the platform, and use it to
interact with the community.
**System Administrator** You look after our servers. You install updates, make
sure certificates are up-to-date, the works. Linux is where your heart lies.
You secretly automated most of your work with Ansible but hey, you put the
playbooks in Git so no worries.
**Technical Writer (code)** You write documentation for freesewing.dev, our
developers website. You have good writing skills and familiarity with code
(Javascript).
**Technical Writer (sewing)** You write documentation for freesewing.org, our
makers website. You have good writing skills and familiarity with sewing.
**Translator** You translate FreeSewing into one of its additional languages
(French, German, Dutch, Spanish) or if you're ambitious, add a new one. You're
fluent in the language you're translating to, and have a good grasp of English.
**UX Designer** You know what UX is and are happy to point out where it sucks
and how it can be made better.
**Release Manager** You pull the plug on new releases, you bundle our code,
and publish new versions of our packages on NPM.
**Web Designer** You know how to make things pretty, even if you're not sure
how to actually make them work. You appreciate that we don't use #000 for
black.
### What's in it for me?
We can't offer you money. Please [read our revenue pledge](/docs/about/pledge/)
to understand why that is.
What we can offer is responsibility, recognition, and a stake in something that
strives to be a force for good in this world.
It can also be an excellent learning opportunity for those of you who would
like to pivot to a role in web development. And for as far as my time
stretches — I will gladly teach and mentor people from underprivileged
communities aiming for social mobility.
## Conclusion
Maybe you can help. Maybe you know somebody who can help, or for whom this
would be a valuable learning experience.
Either way, I'd appreciate it if you could help spread the message that I'm
asking for help.
Thank you,
Joost

View file

@ -0,0 +1,22 @@
---
authors: 1
caption: 'Keep those home-made clothes clean while cooking with the Albert apron'
date: '2020-10-17'
intro: 'What do you do if your daughter needed an apron for school?'
title: 'We haz apron now: FreeSewing 2.10 brings you Albert, a humble apron pattern'
---
What do you do if your daughter needed an apron for school?
<!-- truncate -->
- Would you run out to the store and buy one?
- Would you fire up your sewing machine and make one?
- Would you say to yourself _hmm, better make a pattern first_ and then
share your FreeSewing pattern so that now everybody can have an apron?
Because [that's what Wouter did](/showcase/albert-by-wouter/), so we wrapped it
into FreeSewing v2.10, and [voila](/designs/albert/).
![Wouter's daughter wearing the Albert
apron](https://posts.freesewing.org/uploads/albert_08ccbfc95b.jpg)

View file

@ -0,0 +1,67 @@
---
authors: 1
caption: "We're saying thanks to our contributors and want to make sure their contributions get the credit they deserve"
date: '2021-11-28'
intro: 'During our last contributor call , we decided that we would implement the All Contributors specification as a way to honour all our contributors.'
title: 'Calling all contributors'
---
During [our last contributor
call](https://github.com/freesewing/freesewing/issues/1514), we decided that we
would implement the [All Contributors
specification](https://allcontributors.org/) as a way to honour all our
contributors.
<!-- truncate -->
If on the surface, a contributor call decision to honour contributors sounds
like some sort of self-serving nonsense, let me reassure you that there's no
risk of dislocated shoulders from patting ourselves on the back. Instead it's
about (also) honouring those contributions that tend to go overlooked in Open
Source.
The All Contributors website summerazes it as:
> This is a specification for recognizing contributors to an open source
> project in a way that rewards each and every contribution, not just code.
>
> People are giving themselves and their free time to contribute to open source
> projects in so many ways, so we believe everyone should be praised for their
> contributions (code or not).
When we talk about _contributors_ in open source projects, there is often the
assumption that this is all the people who contribute code to the project. The
fact the [GitHub lists the contributors of a project based on the actual
commits](https://github.com/freesewing/freesewing/graphs/contributors) goes a
long way to cement that view.
But there's plenty of contributions that fly under the radar of an automated
system based on the commit history. Things like documentation and blog posts,
community building, answering questions or submitting bug reports, are all
valuable contributions that deserve to be recognized.
By implementing the All Contributors specification, we pledge to give those
people credit for their contributions, and make that credit visible. In
practice the list of contributors will be included in the `README.md` file of
all our packages, as well as in the main `README.md` file of [our
`freesewing/freesewing` monorepo](https://github.com/freesewing/freesewing).
In the future, we also plan to make this list available on our website.
## How does it work?
Going forward, we will make an effort to add all contributors regardless of how
they contribute to the project. But we'd also like to capture contributions
that were made up until now.
For this, we need your help. If you've contributed to FreeSewing, let us know.
You can [let us know on Discord](https://discord.freesewing.org/), or you can
[create an
issue](https://github.com/freesewing/freesewing/issues/new?assignees=joostdecock&labels=%F0%9F%92%9C+all+contributors&template=all-contributors.md&title=All+Contributors%3A+Please+add+%28username+here%29),
or you can comment on [any
issue](https://github.com/freesewing/freesewing/issues) or [pull
request](https://github.com/freesewing/freesewing/pulls) using [the
instructions for the all-contributors
bot](https://allcontributors.org/docs/en/bot/usage).
Don't be shy, add yourself :)

View file

@ -0,0 +1,18 @@
---
authors: 1
caption: 'Designer Hellgy struts their stuff, while their Yuri flows in a summer breeze'
date: '2021-09-05'
intro: "Announcing FreeSewing v2.18: Congratulations, it's a Yuri"
title: "Announcing FreeSewing v2.18: Congratulations, it's a Yuri"
---
I'm happy to announce the immediate availability of FreeSewing v2.18 and our
latest addition to our catalog of designs: [The Yuri hoodie](/designs/yuri/) .
Designed by [Hellgy](https://twitter.com/hellgy) and coded by their bae
[Biou](https://github.com/biou/), Yuri is everything they love about [our Huey
hoodie](/designs/huey/) sans their arch enemy: zippers.
<!-- truncate -->
Checkout [Hellgy's showcase for more pictures](/showcase/yuri-by-its-designer),
or check out [the pattern page itself](/designs/yuri/) for all details.

View file

@ -0,0 +1,105 @@
---
authors: 1
caption: 'Picture you and him, in matching coats.'
date: '2018-01-26'
intro: "This one's for the ladies --- and I'm not (just) talking about that picture of heart-throb Benedict Cumberbatch at the top of this post."
title: 'Announcing Carlita, the womenswear version of our Carlton coat.'
---
This one's for the ladies --- and I'm not (just) talking about that picture of
heart-throb Benedict Cumberbatch at the top of this post.
<!-- truncate -->
Instead, I'm talking about the release of our latest pattern: the [Carlita
Coat](/designs/carlita) which is out in beta as of today.
Carlita is --- you guessed it --- the womenswear version of our Carlton coat,
which recreates the iconic Sherlock Holmes coat worn by BC on the BBC series.
## How did we do it?
For details on how this project came to be, I refer you to [the Carlton
announcement post](/blog/announcing-carlton-and-bent/). Here, I'd like to
focus on the specifics of how we turned this menswear pattern into a coat for
ladies.
We wanted to stay as close to the original as possible, so all we did was make
changes to address one challenge: fitting the breasts.
### Princess, meet your seam
We drew an extra princess seam in the front panel of the coat, through which we
added shaping for the bust.
To be able to do that accurately, Carlita requires three extra measurements in
addition to the measurements required by Carlton. They are:
- The [high bust](/docs/measurements/#highBust)
- The [bust span](/docs/measurements/#bustSpan)
- The [high point shoulder to
bust](/docs/measurements/#highPointShoulderToBust)
We use your high bust measurements to draft the coat, and then do a full bust
adjustment on the princess seam based on your (full) chest circumference, bust
span and HPS to bust.
### All of the pockets, but we had to move some of them
The addition of the princess seam made the map pocket placement a bit
difficult, so we've slightly moved it, and aligned it vertically, rather than
slightly tilted as it is in Carlton.
This way, the pocket can be integrated in the princess seam, somewhere in the
underboob region.
### One extra option: The princess seam smooth factor
Carlita also has one extra option that Carlton doesn't have, the somewhat
elaborately named [Princess seam smooth
factor](/docs/designs/carlita/options#princessSeamSmoothFactor).
This controls how sharply the princess seam will revert back after having added
the extra volume for your chest.
A picture says more than a thousand words, so here's the option sampled on the
relevant part of the pattern:
![The effect of the option as shown by our sampler
service](https://posts.freesewing.org/uploads/smooth_e8f395dd4a.png)
As you can see, the option controls the urgency with which the princess seam
reverts back to your waistline after passing the fullest point of your bust.
A low factor will make for a more fitted coat, but also a more curvy seam that
is harder to sew.
A higher smooth factor will smooth this out so it's a more sloped retreat to
the downward seam. This will make the coat less fitted under your chest, and
the seam easier to sew.
## A reminder about made-to-measure patterns
This goes without saying for regular visitors to this site, but if you're new
here, it's worth repeating:
> This coat is not drafted with a certain cup size in mind. Instead, it will
> adapt to your chest based on your measurements.
## Ladies, we need your feedback
This is our first womenswear pattern with a fitted chest. As such, we're
breaking new ground here, and I'd be interested to see how this pattern adapts
to a variety of body shapes/cup sizes.
If you're planning to make this coat, or a muslin of it, please share your
experience, and don't hesitate to get in touch should you run into any issues.
I plan to design more womenswear patterns, so if there are any fit issues, I'd
like to know about it sooner rather than later.
## Shout-out
Last but not least, I'd like to thank [Anneke](http://www.annekecaramin.com/)
for her help throughout this project, and tolerating my countless rants about
my love/hate relationship with boobs.

View file

@ -0,0 +1,106 @@
---
authors: 1
caption: "You too can wear this coat. Although you're on your own for the hat. At least for now."
date: '2017-12-20'
intro: 'Announcing Carlton, aka the Sherlock Holmes coat, and the Bent Body Block'
title: 'Announcing Carlton, aka the Sherlock Holmes coat, and the Bent Body Block'
---
Good news everybody, we've got your cosplaying needs covered. That is, if you
are cosplaying as Sherlock Holmes. Our newest pattern release, [the Carlton
Coat](/designs/carlton) is exactly what the doctor prescribed if for some
weird reason your doctor wants you to look like everyone's favorite detective.
<!-- truncate -->
My memory is foggy on the exact origin of this endeavor. It probably involved
me complaining about the cold and my lack of a warm winter coat, but apart from
that, I'm not too sure.
What I do remember is that at one point,
[Anneke](http://www.annekecaramin.com/) and myself decided that we would
reverse-engineer the Sherlock Holmes coat. More specifically, the coat worn by
Benedict Cumberbatch in the BBC series Sherlock Holmes.
![That coat though](https://posts.freesewing.org/uploads/bc_f20e01a16d.jpg)
The coat is popular with cosplayers for obvious reasons, but it's also just a
really nice coat, and other brave souls [such as
Melissa](http://blog.fehrtrade.com/gallery/868/the-sherlock-coat/) have tried
their hand at it.
I must admit that I didn't know Melissa made a similar coat until mine was
finished. Furthermore, her blog post mentions that she used the instructions in
a livejournal post that is no longer available.
Anneke and myself did no such thing and just put the pattern together based on
screengrabs from the TV show. We're hardcore like that.
### About the coat
Carlton is a double-breasted long overcoat in the tradition of horseback
uniform coats where the back of the coat isn't split, but rather has wide
pleats that can drape over the back of the horse. Or, more realistically, fan
open behind you as you walk about.
![Front view of Carlton on yours
truly](https://posts.freesewing.org/uploads/front_e1d64f3ceb.jpg) ![Back view
of Carlton on yours
truly](https://posts.freesewing.org/uploads/back_05c04878c5.jpg)
It comes with two patch pockets on the front, two map pockets at the chest, and
two inner pockets. Six pockets in total, so you'll have plenty of room for all
your belongings.
![Map and patch pockets on the
outside](https://posts.freesewing.org/uploads/pockets_28099d1afe.jpg) ![Inner
pockets on the
inside](https://posts.freesewing.org/uploads/innerpocket_4e4e3f5119.jpg)
I finished mine just in time for a trip to K&ouml;ln to visit my beloved friend
and tailor [Sebastian Hoofs](http://sebastian-hoofs.de/massschneider/). It was
cold as eff over there, but the coat kept me nice and warm. Not surprising
since it's made in a hefty wool I picked up in [Bacci in
Florence](http://www.baccitessuti.it/en/index.html) over the summer, and I took
the extra step of interlining it with French terry (I really don't like to be
cold).
![I have no affiliation with Bacci, I'm just a sucker for great fabric
stores](https://posts.freesewing.org/uploads/bacci_043f91c96c.jpg) ![You can
see the terry interlining where I peeled back the sleeve
lining](https://posts.freesewing.org/uploads/interlining_220e0eab71.jpg)
### Burn the patriarchy to the ground through the power of pockets
Ladies take note: Carlita --- a female version of Carlton --- is currently on
the drawing board.
Initially, the plan was to release male and female versions side by side, but
adapting the coat has proven to be more involved than anticipated because,
well, because boobs.
I also feel like I have to keep too many plates spinning right now, so I'm
hopeful that getting Carlton out the door will create some time/space to make
Carlita follow soon(ish). Either way, I've promised Anneke that we'd do a
female version, and [she's a freesewing Captain now](/patrons), so I intend to
keep that promise.
Oh, and about that title: When Carlita's ready, she will have just as many
pockets as Carlton.
## Also, this: Introducing the Bent Body Block We're actually releasing two
patterns today. Apart from the Carlton Coat, there's also the [Bent Body
Block](/designs/bent).
Bent is a two-part sleeve variation of our [Brian Body Block](/designs/bent).
It's essentially a base for coat and jacket patterns for menswear.
I originally designed Bent as a base for my [refashioners 2017 zebra
jacket](/blog/the-refashioners-2017/), so you can expect that jacket pattern to
also hit the site in the coming months.
Carlton is also based on Bent, so we're giving you both today.
Have wonderful and happy holidays if you're into that kind of thing. And happy
making!

View file

@ -0,0 +1,103 @@
---
authors: 1
caption: 'Photo by Flo Dahm from Pexels'
date: '2021-04-18'
intro: "We've just published FreeSewing v2.15 and it comes with a new pattern: The Charlie Chinos trouser pattern ."
title: 'Charlie Chinos: who wants some new trousers?'
---
We've just published FreeSewing v2.15 and it comes with a new pattern: [The
Charlie Chinos trouser pattern](/designs/charlie/).
<!-- truncate -->
I have lost track of how long a chino trouser pattern has been on my to-do
list, but it's measured in years. I'm very happy I finally landed where I
wanted to be.
I have a picture of it here, but due to the dark fabric, you can't really make
out much:
![A pair of Charlies by
Joost](https://posts.freesewing.org/uploads/joost_b8dee41025.jpg)
So instead, let me tell you about why I am so excited about this.
##### Based on Titan
First up, Charlie is based on Titan, our unisex trouser block that is also the
foundation for [our Paco pattern](/designs/paco/). So if you're familiar with
any of those, you already know how Charlie will fit you.
##### More measurements, more options, better fit
As a token of how much I feel this is an improvement, I've deprecated
Theo. Based on an Aldrich draft, Theo uses very few
measurements, and while that worked fine for a certain set of people, it's a
less versatile pattern.
Charlie will adapt better to differently shaped bodies, and has a hell of a lot
more options that allow you to configure your trousers so you get them just as
you like. In case you're wondering, Theo has 5 options, whereas Charlie has 31.
That being said, we will keep Theo available. Deprecated merely means that
we've added a little warning message saying that we recommend Charlie instead.
##### Easier to make
Another reason to opt for Charlie rather than Theo: Charlie is easier to make.
It has a more straight-forward fly and waistband construction, and the front
pockets have been cleverly designed to give you the ease of construction that
comes with side-seam pockets, yet the classic look of slanted pockets.
Theo ranks 4 stars on our difficulty scale, and I've given Charlie 3 stars. If
you were afraid of making trousers, this might be the pattern you've been
waiting for.
##### Real pockets
Charlie is a unisex pattern and the pockets are real. You have welt (aka
jetted) pockets at the back, and slanted pockets at the front. In both cases,
you have control over the size and depth of the pockets.
The front pockets deserve a special mention. They look like traditional slanted
pockets, but are set in on the side seam. To make that possible, the back panel
of the trousers wraps around the front, following the pocket slant.
## Other 2.15 news
Charlie is the main act, but there's a lot of work that went in this 2.15
release.
As always, [the
changelog](https://github.com/freesewing/freesewing/blob/develop/CHANGELOG.md)
has all the details, but allow me to highlight some of the more noteworthy
changes:
- We have [a new bartack
plugin](https://freesewing.dev/reference/plugins/bartack/)
- The [buttons plugin](https://freesewing.dev/reference/plugins/buttons/)
provides new
[buttonhole-start](https://freesewing.dev/reference/snippets/buttonhole-start)
and
[buttonhole-end](https://freesewing.dev/reference/snippets/buttonhole-end)
snippets
- The [dimension plugin](https://freesewing.dev/reference/plugins/dimension/)
provides a new [rmad macro](https://freesewing.dev/reference/macros/rmad/)
- The [logo plugin](https://freesewing.dev/reference/plugins/logo/) now
supports dark mode
- Titan and Paco have a new `waistbandHeight` option
- Core no longer rounds point coordinates to avoid misses when using
[path.split](https://freesewing.dev/reference/api/path/split/)
- [Bella](/designs/bella/) has a fix to the shoulder to better accommodate
doll-sized clothing
- [Charlie](/designs/charlie/) is the first pattern to list some of the
absolute dimensions when configuring a pattern, but we plan to extend this
to other designs We have documented [the new raise
methods](https://freesewing.dev/reference/api/part/raise) for designers who
want to utilize this feature.
- Speaking of documentation, the examples in our [documentation for
developers](https://freesewing.dev/) now allows you to toggle a switch to
reveal the points and paths in the examples
- The [part.getId()](https://freesewing.dev/reference/api/part/getid/) method
now takes a prefix argument

View file

@ -0,0 +1,122 @@
---
authors: 1
caption: "I don't drink, but this seemed appropriate for a celebration post ¯\\_(ツ)_/¯"
date: '2018-08-25'
intro: 'Celebrating one year of freesewing.org: Announcing the freesewing library'
title: 'Celebrating one year of freesewing.org: Announcing the freesewing library'
---
Exactly one year ago, the doors of freesewing.org swung open for our users,
while those of makemypattern.com get one of those _we've moved_ signs.
<!-- truncate -->
Looking back at [that blog post from 12 months ago](/blog/open-for-business),
it almost beggars belief that the things announced then are only one year old.
The concept of a draft, the comparison functionality, or even paperless
patterns. They all celebrate their first birthday today.
Not this site though, because [driven by the looming GDPR
deadline](/blog/gdpr-plan), we dumped our Jekyll based site for a new front end
sometime in May.
## More languages with less languages
GDPR was only part of that story. Other reasons for the rewrite were our
desire to support multiple languages, and to simplify our technology stack.
In other words, we wanted to reach people who speak different languages, and
wanted to limit the number of programming languages required to do so.
### More natural languages
We've done remarkably well on this front. While you won't find every last bit
of content translated, this website's main features are now available in five
languages:
- English
- German
- Spanish
- French
- Dutch
Which really is 100% thanks to the great work of our wonderful
translators.
### Less programming languages
The switch from Jekyll to a [Nuxt](https://nuxtjs.org/)-based front-end has
removed [Ruby](https://www.ruby-lang.org/) from our technology stack.
Freesewing.org now runs on JavaScript, PHP and a little bit of C (which we'll
ignore for now).
But removing programming languages is not a goal _an sich_. Rather, the
underlying ambition is to simplify things, make it easier for people to get
involved, and ultimately attract more contributors so that the project can grow
and flourish.
Today, designing/developing patterns is not an insurmountable obstacle. We've
got [benjamin](/designs/benjamin), [florent](/designs/florent), and
[sandy](/designs/sandy) to show for it. All of these were contributed by
people for whom freesewing was initially new, they went through the design
tutorial, and in the end created a pattern of their own.
We'd like more people to follow in their footsteps. So making the process as
simple as possible is a worthy investment of our time.
## Announcing freesewing, the library
For the past 2 months, I have taken time off from pattern making and sewing to
tackle our [technical debt](https://en.wikipedia.org/wiki/Technical_debt).
Specifically, I've set out to rewrite our core back-end from the ground up in
JavaScript. But there's a twist. It's no longer a back-end. It's a library you
can use both in your browser, or on the server with
[node.js](https://nodejs.org/).
It is currently in version 0.10, and feature complete with freesewing core.
It's [available on GitHub](https://github.com/freesewing/freesewing) and
[NPM](https://www.npmjs.com/package/freesewing), and is fully documented at
[developer.freesewing.org](https://developer.freesewing.org/).
And while its API is richer than core's, it's footprint is actually a lot
smaller:
![Lines of code comparison between the new library and (the relevant portion
of) freesewing
core](https://posts.freesewing.org/uploads/corevsfreesewing_c9327c9fa3.svg)
Which is good news, in case you were wondering.
## What happens next?
A lot of work needs to be done before we can actually use this on
freesewing.org:
- All our existing patterns need to be parted to the JS version.
[Brian](https://github.com/freesewing/brian) is the first pattern to have
been ported.
- Rewrite our data back-end in JS. Since this will remove the PHP programming
language from our stack.
- Build a new website using the freesewing library and our new data back-end.
This really is a lot of work, and while I hope that by the end of the year
we'll have made good progress, I can't promise it will be done.
## But I just want patterns
Chances are, all you care about is patterns. What you want is more patterns,
better patterns, different patterns. And all of this rewriting is not exactly
pushing your buttons.
I get that. I really do. I for one have a list of patterns I'd like to see
added to the site. And my work on other aspects of the project keeps me from
adding them.
But I believe that investing now in a streamlined developer experience will
have a knock-on effect in the long term.
If we want a few extra patterns, this is not the right approach. But if we want
a lot more patterns, I believe it is.
And I want a lot more patterns.

View file

@ -0,0 +1,52 @@
---
title: 'Announcing FreeSewing.social'
caption: 'Silhouette Photo of Elephant during Golden Hour by Renato Conti'
date: '2023-11-03'
intro: 'FreeSewing now has a home on the fediverse, and you can join too'
authors: 1
---
FreeSewing has carved out a spaced for itself on the Fediverse and that space
is [FreeSewing.social](https://freesewing.social). It's a Mastodon instance
that is open to all FreeSewing users, and the larger community. ## The
FreeSewing Mastodon instance The official FreeSewing account
([@freesewing@freesewing.social](https://freesewing.social/@freesewing)) will
be used for formal announcements, so if you want the headlines, follow that
one.
<!-- truncate -->
I will be using my own account
([@joost@freesewing.social](https://freesewing.social/@joost)) for more
frequent updates, news, discussion and whatnot. So if you'd like to be more in
the loop, that's a good one to follow.
And -- it bears repeating -- you are welcome to join this instance. If you were
wondering about Mastodon/Fediverse but were not too sure about what to do or
what server to join, this is your chance.
## Leaving Twitter I need more work like I need another hole in my head, so
setting up this instance was something I've been mulling over for a while, but
in the end I decided to do it because Twitter -- where both FreeSewing and
myself have an account -- has been transformed into something that I honestly
don't want to have anything to do with.
This has been going on for a while, and leaving Twitter at this point is not
exactly a strong statement. It's just that I've been busy, and certainly was
too busy to deal with this. So I decided that the website migration was a good
time to make the formal announcement.
So, I will remove both `@j__st` and `@freesewing_org` from Twitter at the end
of this month.
## Joining Bluesky The Fediverse is not for everyone, and for those who like
the Twitter experience, Bluesky seems to be the alternative that is somewhat
closest.
This is by no means an endorsement, but I have created
[joost.at](https://bsky.app/profile/joost.at) and
[FreeSewing.org](https://bsky.app/profile/freesewing.org) on Bluesky. I will do
my best to also post there so that those people who prefer it can get their
updates that way.

View file

@ -0,0 +1,126 @@
---
title: 'Announcing FreeSewing v3.0'
caption: 'This picture by Engin Akyurt seems like the sort of understated celebration that is suitable or this announcement'
date: '2023-09-30'
intro: 'FreeSewing 3.0 is finally here. The 3.0 release culminates more than a year of work, and comes just over four years after the v2.0 release. What Im saying is: I dont make announcements like this often, and its a big deal. You should get excited.'
authors: 1
---
FreeSewing 3.0 is finally here.
FreeSewing is the leading open source platform for made-to-measure sewing
patterns, loved by home sewers and fashion entrepreneurs alike.
<!-- truncate -->
The 3.0 release culminates more than a year of work, and comes just over four
years after the v2.0 release. What I'm saying is: I don't make announcements
like this often, and it's a _big deal_. You should get excited.
## Breaking changes
Let's start with the obvious: This is a major release so there are breaking changes.
Listing all of them would be rather challenging, and probably not that useful.
The first pre-release versions of FreeSewing 3 is almost a year old, and all of
the people who contributed designs have either ported their designs, or I
did it for them.
Still, I want to list three breaking changes that are super obviously going to
break your stuff if you rely on FreeSewing code;
- **FreeSewing 3 is ESM only**: Migrating a large Javascript project to ESM
modules is enough to make even the most seasoned developers break down and
cry, but it's done.
- **FreeSewing 3 uses named exports**: There are obviously some places where a
default export is required (looking at you NextJS) but whereever we can, we
now use named exports exclusively because we all know those are better.
- **FreeSewing 3 requires Node 18 or newer**: I recommend lts/hydrogen
With that out of the way, let's talk about what's new.
A lot of work went into this release, and I couldn't possibly cover all of it.
But allow me to name-check some of the more fundamental changes.
## Designs are now JBOP
A big driver for the decision to freeze the v2 branch and start working on v3
was to make it easier to mix-and-match parts from various designs.
Design inheritance was already possible in v2, but because the configuration
was handled on the design level, it required careful re-configuration of
(required) measurements, options, part dependencies, and so on. It was possible
but came with a lot of friction.
In v3, all configuration is moved to the part level, and a design is now not
much more than _just a bunch of parts_ (JBOP). It is the parts themselves that
configure what they need. This includes anything from the measurements they
require, the options they provide, the plugins they use, their dependencies,
and so on.
This way, re-use parts from various designs, and all of their configuration,
dependencies, plugins, and so on will follow.
## Less boilerplate
Creating a design has also become a lot simpler, you essentially pass your list
of parts to our `Design` constructor and you're done:
```mjs
import { Design } from '@freesewing/core'
export const MyDesign = new Design({
parts: [
/* ... your parts here ... */
],
})
```
Speaking of less boilerplate, in v2, the most common plugins were already
bundled in the `@freesewing/plugin-bundle` package, but you still had to
include them in your design. In v3, those plugins have moved to the
`@freesewing/core-plugins` package, and will be loaded by FreeSewing's core
library by default (although you can opt out of that).
## Plugins with more powers
In addition to providing macros, snippets, or tapping into FreeSewing's
lifecycle hooks, plugins can now also add methods to the store.
This is allows further extending FreeSewing with whatever exciting thing you
can thing of. As an example, the way logging is handled in the core library
was re-implemented based on this. Which means that if you would like a
different logging solution, you can simple provide your own log handler in a
plugin.
## New development environment
With the version 3 release comes a new development environment that closely
mimics what we will be providing at FreeSewing.org (more on that later).
The development environment ships with various templates that you can use to
either start a design from scratch, or extend one of our blocks. You don't have
to choose one over the other either, you can use all of these at the same time,
and if you want even add more.
Our new development environment now allows (optionally) integrates with the
FreeSewing backend. You can authenticate with your FreeSewing account so you
can (re)use your measurements while working on your designs.
## Not everything is versioned
There's a lot more I could talk about, but I need to address the elephant in
the room: So we have 3.0 now, when do non-developers get to use this?
Well... I'm going to need a bit more time. Everything is sort of ready, but
some things always a bit more time because you can't really to them in advance.
Things like translation, some more testing, not to mention migrating 50k users
to a completely different infrastructure.
So as a regular user of FreeSewing.org who is not itching to spin up a
development environment, you will need to hold on a little longer. But
clearly, it's going to be soon now. I'd say a matter of weeks, rather than
months.
In the meanwhile, if you find a problem or bug, create an issue because
FreeSewing 3 is now production-ready and fully supported.
joost

View file

@ -0,0 +1,86 @@
---
authors: 1
caption: 'The freesewing logo'
date: '2017-03-24'
intro: 'I am are proud to announce freesewing core v1.0.0 and the accompanying documentation that describes the freesewing project in detail.'
title: 'Announcing freesewing, an open source platform for made-to-measure sewing patterns'
---
I am are proud to announce [freesewing core
v1.0.0](https://github.com/freesewing/core) and the accompanying
[documentation](/docs) that describes the freesewing project in detail.
<!-- truncate -->
But I'm also realistic, and understand that you don't have time to plow through
pages upon pages of documentation.
So instead, here's a story to give you the essence of freesewing in a nutshell:
## The freesewing origin story Sewing is easy. It really is. What's hard is
getting things to fit properly. That's why you use a sewing pattern. It's a
blueprint for whatever it is you are making. A good pattern gives you good
fit. Most patterns don't.
That's because &mdash; much like clothes in the shop &mdash; patterns come in
sizes. And sizes are a crude way to put people in boxes. They are made for an
imaginary average person, rather than for you.
There's another way, and that is to draft a pattern based on your measurements.
These made-to-measure patterns are vastly superior, but they require a lot of
work.
I wanted to change that, and that effort evolved into
[MakeMyPattern.com](https://makemypattern.com/). I ran that site for a
number of years, and it was a remarkable success. Probably helped by the fact
that I gave away all patterns for free.
In the world of home sewing, it tends to require a bit of explaining why one
would choose to give away their work for free. Things are different in the
open source world where the idea of sharing your work with others for the
benefit of all is the very thread from which communities are woven.
While I can't magically bring the culture of open source to sewing patterns, I
certainly can bring sewing patterns into the open source world.
[Freesewing.org](https://freesewing.org/) will continue to offer what
[Makemypattern.com](https://makemypattern.com/) does today: free sewing
patterns drafted to your measurements. But additionally, it will be open to
your contributions.
Here's hoping that in the Venn diagram of the somewhat geeky and sewing, it's
not just me in the middle.
joost
## Shout-outs Freesewing is a project by [Joost De
Cock](https://github.com/joostdecock) and contributors.
Hat-tip to [@jakesgordon](https://github.com/jakesgordon) and [Kevin
Lindsey](http://www.kevlindev.com) for allowing me to port some of their code.
The early-stage enthusiasm and input of
[@diggydev](https://github.com/diggydev), [@cabi](https://github.com/cabi),
[@woutervdub](https://github.com/woutervdub),
[@cloutiy](https://github.com/cloutiy),
[@straytaoist](https://github.com/straytaoist),
[@netpraxis](https://github.com/netpraxis),
[@Stefan1960](https://github.com/Stefan1960),
[@brendare1](https://github.com/brendare1),
[@JorisJoppe](https://github.com/JorisJoppe),
[@JamJenkins](https://github.com/JamJenkins), and
[@fightingrabbit](https://github.com/fightingrabbit) has been a great boon to
this project.
Thanks to [@annekecaramin](https://twitter.com/annekecaramin) for designing a
logo cool enough to put on a Tshirt.
Special thanks to [@scorchtorch](https://twitter.com/scorchtorch) for being the
best at that heart/salad/antlers thing.
Last but not least, this project would not exist without the users, supporters,
and donors of [makemypattern.com](https://makemypattern.com/).
Thanks guys!

View file

@ -0,0 +1,36 @@
---
authors: 1
caption: 'An example Hortensia made by the designer'
date: '2021-02-13'
intro: "I'm happy to announce the immeadiate availability of our latest FreeSewing pattern: the Hortensia handbag ."
title: 'Say hi to our latest pattern: The Hortensia handbag'
---
I'm happy to announce the immeadiate availability of our latest FreeSewing
pattern: [the Hortensia handbag](/designs/hortensia/).
<!-- truncate -->
Hortensia's origin story traces back to
[@stoffsuchti](https://twitter.com/stoffsuchti) who wanted to create a pattern
for a handbag, and was looking for somebody who could implement that design in
code.
It was [Wouter](https://github.com/woutervdub) who answered the call and
implemented the pattern, making this another success story from the FreeSewing
community.
## No measurements needed
This is the first pattern on FreeSewing that does not require any measurements
to make. There's some options you can choose — to control the size of the
handbag for one thing — but since this is a handbag, no measurements are
needed.
This makes Hortensia a great project to make as a gift, since there's no need
to worry about fit.
:::tip
Now show us yours If you do make a Hortensia, do [send us
pictures](https://discord.freesewing.org/).
:::

View file

@ -0,0 +1,47 @@
---
authors: 1
caption: 'The pattern illustration for Penelope'
date: '2018-06-29'
intro: "I'm very happy to announce the immeadiate availability of the Penelope Pencil Skirt , out in beta today."
title: 'Announcing the Penelope Pencil Skirt; And our womenswear roadmap'
---
I'm very happy to announce the immeadiate availability of
[the Penelope Pencil Skirt](/designs/penelope), out in beta today.
<!-- truncate -->
Penelope was designed by [@woutervdub](/users/user?id=132) who also signed for
[benjamin](/designs/benjamin).
## Freesewing is not (just) for menswear
This new foray into womenswear foreshadows our plans for the second half of
this year. That's right, we're going to be making more patterns for ladies.
Freesewing has a bit of a menswear reputation, since historically we've had a
lot more patterns for men than for ladies. But that doesn't mean women are
second-class citizens here. It's just a side-effect of how this project was
historically run by a man. Can you blame a brother for designing patterns he'd
like to wear himself?
Today, freesewing is a communal effort, and there's plenty of ladies involved.
So it only make sense to make a conscious effort to do something for our
sisters. And that's exactly what we'll be doing.
We've got another pattern in the pipeline that is not only for women, but also
by a woman. By [@AlfaLyr](/users/user?id=1230) in particular, who has designed a
circle skirt pattern that is going to land here real soon.
In parallel, I'll be working with [@AnnekeCaramin](/users/user?id=168) on a
female equivalent for [Brian](/designs/brian), our basic body block on which
many of our patterns are based. The idea is to make a quality bodice block for
ladies that is made-to-measure and can handle the common fitting issues
automatically. I'm talking about full bust adjustment, sway back, these kind of
things.
Once we have that block, expect us to spin it into a bunch of dresses and tops.
And as always, if you have pattern design skills but are a bit intimidated by
the platform, reach out to us to see if we can join forces to make your ideas a
reality.

View file

@ -0,0 +1,30 @@
---
authors: 1
caption: "Did you know that Sandy was named after Olivia Newton-John's character in the move Grease?"
date: '2018-08-01'
intro: "Ladies and gentlemen --- because who says men can't wear skirts --- I am happy to announce the immeadiate availability of our latest freesewing pattern: the Sandy circle skirt ."
title: 'Announcing Sandy, a circle skirt designed by @AlfyLyr'
---
Ladies and gentlemen --- because who says men can't wear skirts --- I am happy
to announce the immeadiate availability of our latest freesewing pattern: [the
Sandy circle skirt](/designs/sandy).
<!-- truncate -->
Sandy was designed by [@AlfaLyr](/users/user?id=1230) who really did a stellar job.
This makes it also another pattern from the freesewing community, something
that I'm very excited about.
## All the options
As many patterns here at freesewing, Sandy comes with a bunch of options to
allow you to customize your skirt to your wishes.
There's options for how much of circle you want (half? full? Something else?),
how long you want it, the shape and overlap of the waistband, whether you want
pleats, how much hem, and so on.
I can see this becoming rather the success story. So go and treat yourself to
a new skirt, and let [@AlfyLyr](/users/user?id=1230) know how it
works out for you.

View file

@ -0,0 +1,39 @@
---
authors: 1
caption: 'I guess this is what augmented reality looks like?'
date: '2018-09-29'
intro: 'A while ago I found myself in need of some new swim trunks. So I drafted a pattern and made some:'
title: 'Announcing Shin, a swim trunks pattern'
---
A while ago I found myself in need of some new swim trunks. So I drafted a
pattern and made some:
<!-- truncate -->
![An early tryout of the Shin
pattern](https://posts.freesewing.org/uploads/sample_0437fef846.jpg)
I asked if people would be interested in a pattern like this, and turns out
that yes, they were.
So, after some more tweaking, I think this is ready for you guys, and meanwhile
Roy has already provided an illustration for it too.
![I'll be the first to admit that this guy wears it better than
me](https://posts.freesewing.org/uploads/shin_0dc5fdd06d.jpg)
## Options and documentation available
Shin comes with options, and they are very close to the options available for
[Bruce](/designs/bruce).
All [options are documented](/docs/designs/shin/options), as are the [required
measurements](/docs/designs/shin#measurements), and I've also written
[instructions for how to make this](/docs/designs/shin/instructions).
I still have to do the illustrations, but it's really not hard so you'll
probably figure it out without them. Essentially, you can go and make this
right now.
Enjoy!

View file

@ -0,0 +1,25 @@
---
authors: 1
caption: 'A Sven made for the office. As in, kinda boring'
date: '2017-07-09'
intro: 'Winter caused me to design this pattern. I was cold, and needed some sweaters, so what does one do?'
title: 'Announcing the Sven Sweater; A basic sweater based on the Brian body block'
---
Winter caused me to design this pattern. I was cold, and needed some sweaters,
so what does one do?
<!-- truncate -->
Sven stayed under the radar until now because I designed it on the (then
unreleased) freesewing platform, and until now I didn't have a place to show it
to people.
That's how we got here. Now about that sweater: It's a simple design based on
the Brian body block. I've made three different version myself, changing the
neckline and fabrics, and it really does what it needs to do very well (as in,
keep me warm).
## Pictures or it didn't happen
Check out the [Sven showcases](/showcase/tags/sven) for some samples.

View file

@ -0,0 +1,62 @@
---
authors: 1
caption: 'Photo by Alex Andrews from Pexels'
date: '2020-09-09'
intro: "A while ago somebody asked whether we had a T-shirt pattern on freesewing.org and it turns out, we didn't."
title: 'FreeSewing 2.9 brings our Teagan T-shirt pattern'
---
A while ago somebody asked whether we had a T-shirt pattern on freesewing.org
and it turns out, we didn't.
<!-- truncate -->
There's no good reason for that, we just never got around to that. So today
we're rolling out version 2.9 of FreeSewing with our latest pattern: [the
Teagan T-shirt](/designs/teagan/).
![A man, seated on a purple couch and wearing a speckled grey Teagan
T-Shirt.](https://posts.freesewing.org/uploads/teagan1_2904162431.jpg)
## Who is this for?
Teagan is fitted T-shirt pattern with options for altering the neckline,
length, and sleeves.
Teagan is based on [our Brian block](/designs/brian/), which does not take
breasts into account. That being said, this will work for people with breasts
too. Since it uses the chest circumference it will simply draft a T-shirt to
fit your full chest.
We've also added an option to draft this to your high bust, which would give
you a more fitted T-shirt with ease & stretch having to accomodate for your
breasts.
![A woman wearing a striped T-shirt stands in front of a weathered fence. This
T-shirt is more fitted through the
chest.](https://posts.freesewing.org/uploads/teagan3_8ff8115d75.jpg)
## Community Updates
If you're thinking about sewing Teagan, but you're new to sewing or working
with knits, never fear! FreeSewing has more support than ever. Chat with us on
[Discord](https://discord.freesewing.org/), in a [Facebook
Group](https://www.facebook.com/groups/627769821272714), or on
[Reddit](https://www.reddit.com/r/freesewing/). Find us on social as
@freesewing_org on [Instagram](https://www.instagram.com/freesewing_org/) and
[Twitter](https://twitter.com/freesewing_org). If you're looking for tutorials,
there's a new [YouTube
channel](https://www.youtube.com/channel/UCLAyxEL72gHvuKBpa-GmCvQ) for that. It
has a whole series on [sewing up
Teagan](https://www.youtube.com/playlist?list=PLY9EmRuXR20Y7FonIHD6mX9yIpFh_emX1),
as well as this amazing preview.
<YouTube id="3UGJSNxNe8I" />
If all of this sounds awesome, and you want to get more involved with
FreeSewing, you can check out our repositories and source code on
[Github](https://github.com/freesewing/) or connect with other contributors via
[Discord](https://discord.freesewing.org/). You can find announcements about
upcoming contributor calls on Discord, in the announcements channel.
And if you sew up a Teagan T-shirt, let us know what you think!

View file

@ -0,0 +1,23 @@
---
authors: 1
caption: 'Photo by Karolina Grabowska from Pexels'
date: '2021-06-27'
intro: 'Announcing Ursula, a basic, highly-customizable underwear pattern'
title: 'Announcing Ursula, a basic, highly-customizable underwear pattern'
---
FreeSewing 2.17 is out, and it while it comes with many fixes and improvements,
the biggest news is certainly a brand new pattern: [The Ursula
Undies](/designs/uma/) by US-based designer Natalia Sayang.
<!-- truncate -->
![Example of a default
Ursula](https://posts.freesewing.org/uploads/example_aab890ee57.jpg)
The picture above is from [this showcase post](/showcase/ursula-test-pairs/)
which includes several more examples of the different styles you can accomplish
with this versatile pattern.
Ursula is a quick make, not to mention the ultimate stash-buster. We are
really excited about it.

View file

@ -0,0 +1,86 @@
---
authors: 1
caption: 'This release really is a sea-change'
date: '2019-08-25'
intro: 'Welcome to version 2 of FreeSewing, the open source platform for made-to-measure sewing patterns.'
title: 'Announcing FreeSewing v2.0'
---
Welcome to version 2 of FreeSewing, the open source platform for
made-to-measure sewing patterns.
This release culminates more than a year of work, and comes two years to the
day since FreeSewing first went live. Today, we once again raise the bar for
what you can expect from modern-day sewing patterns.
<!-- truncate -->
## 🦄 Harder, better, faster, stronger
[\*](https://www.youtube.com/watch?v=GDpmVUEjagg)
We are _extremely_ excited about this release, which is not just merely a new
major version with some breaking changes, but a complete rewrite in
JavaScript/Node.js. We won't go into the nitty gritty here, but if you'd like
to know how we've done it, check out [the developer
docs](https://freesewing.dev) or [FreeSewing on
GitHub](https://github.com/freesewing).
## ✨ Live preview
FreeSewing has always had a reputation for publishing sewing patterns with tons
of options for you to tweak. Which is great, but can be a bit overwhelming
because you only got to see the result of all those tweaks at the very end.
But those days are gone. Now, no matter what changes you make, you get a live
preview of what your pattern will look like, taking the guesswork out of
configuring your pattern exactly as you like it.
## 🧂 Recipes
Where we used to store your patterns for you, now we'll store your _recipes_
instead. You can go through as many pattern iterations as you want. And when
you're happy, we'll save all the settings it takes to create that exact pattern
in a so-called recipe.
You can then at any time re-use that recipe to recreate your pattern, or use it
as a starting point to make a slightly different pattern. You can also share
these recipes, allowing others to generate the same look for their own
measurements.
## 🤝 Getting involved was never easier
What hasn't changed is that we still support 5 languages (shout-out to all
translators) and that FreeSewing is still a 100% communal effort (shout-out to
all contributors). There's only volunteers here, and if you'd like to help out,
it's never been easier.
For those who are interested in our code, head over to our new developer
documentation at [freesewing.dev](https://freesewing.dev) to learn about the
new platform, our core API, our plugins, and more.
For those of you who are interested in designing patterns, we have updated our
[pattern design tutorial](https://freesewing.dev/tutorial) so you can hit the
ground running.
There's good news for translators too, as we now use
[Crowdin](https://crowdin.com/) which makes translation a breeze. If you'd like
to help out with translation, or maybe even add a new language to FreeSewing,
make sure to [get in touch](https://discord.freesewing.org/).
## 💩 Usual caveats apply
It's conventional wisdom in software development that refactoring code is good,
but rewriting it from scratch is bad. Still, we did it anyway because we wanted
to make it easier for people to get involved in the project, and generate
patterns in real-time in the browser.
That being said, this is a .zero release so you may stumble upon an issue left
or right. When you do, please [let us know](https://discord.freesewing.org/) or
submit an issue.
## 🤞 Tell us what you think
We'd love to hear your feedback. We're **@freesewing_org** on
[Twitter](https://twitter.com/freesewing_org) and
[Instagram](https://instagram.com/freesewing_org), use the **#freesewing**
hashtag, or [check our share page](/share).

View file

@ -0,0 +1,41 @@
---
authors: 1
caption: 'Remember when we were allowed to go outside?'
date: '2021-01-17'
intro: 'The Bella bodice block for womenswear'
title: 'The Bella bodice block for womenswear'
---
We've just published a new pattern on this website: [Bella, a bodice block for
womenswear](/designs/bella/). It's a block with bust and waist dart, and a
waist dart at the back.
<!-- truncate -->
My goal when venturing into womenswear was to make one foundational block from
which we could then spin out a range of different garments.
Which is why we have [Breanna](/designs/breanna/), which I designed from
scratch. Unfortunately, it hasn't been the end-all be-all that I — perhaps
naively — hoped it would be.
For Bella, I worked with someone from the industry to implement a design that
is commonly used for commercial garment making. As such, I am curious to see
how this one will pan out.
My initial plan was to also make a variation with a shoulder dart at the front,
to replace the bust dart, but in the end I settled for getting it released,
rather than work on it longer.
Which brings me to:
##### I'm taking a break from womenswear
I feel like I'm expending a lot of effort here for little result. It's getting
me down, and I need a win.
I want to work on something that I can not only design, but also make and wear.
Something that gives me joy, and makes me feel like I know what I'm doing.
So while I'll still help out people who want to design for women themselves, I
am taking a break from womenswear.

View file

@ -0,0 +1,55 @@
---
authors: 1
caption: 'If you like it, put a bow on it'
date: '2017-11-14'
intro: 'Our family of freesewing patterns has grown once again, and the benjamin of the family is... Benjamin .'
title: "The Benjamin Bow Tie pattern is now available in beta. And I didn't even have to do anything."
---
Our family of freesewing patterns has grown once again, and the benjamin of the
family is... [Benjamin](/designs/benjamin).
<!-- truncate -->
Benjamin is a bow tie that comes with a variety of options to control pretty
much every aspect of your bow tie.
You can opt for a made-to-measure bow tie, or draft Benjamin with an adjustment
ribbon to allow some flexibility in the fit. Something that makes this a great
handmade gift for the upcoming holiday season.
## Benjamin is more special than you realize
Here's the thing about Benjamin that I'm most excited about: I didn't do
anything.
![A collection of bow ties from Wouter, who designed and coded this
pattern](https://posts.freesewing.org/uploads/bowties_4f3e05ec53.jpg)
That's right, Benjamin was designed and coded by [Wouter Van
Wageningen](/users/user?id=132) who has over the last couple of months been teaching
himself the ins and outs of the freesewing platform:
> I had a blast making the Bow Tie pattern. I basically used it to move from
> the BabyBib tutorial into something that was original and not too complex
> (and actually useful). I wanted to get the whole process under control
> before venturing out into the real stuff.
>
> That I was able to pick things up so easily is due mostly to the great
> [BabyBib tutorial](https://freesewing.org/tutorials/pattern-design/) and the
> help you've provided [here](https://discord.freesewing.org).
## Who will be next to contribute a pattern?
When I released freesewing as an open source platform, I obviously hoped that
others would join the effort.
But I never thought that less than 3 months after the public release of
freesewing, we'd have our first pattern from the freesewing community.
![Benjamin is kind of a big
deal](https://posts.freesewing.org/uploads/giphy_7a40f62815.gif)
So yeah, Benjamin is a BFD, Wouter is a badass, and I am hopeful that this will
be the first of many patterns that we as a community will be able to make
available to all.

View file

@ -0,0 +1,156 @@
---
authors: 1
caption: "This post describes what's new in version 2.2 of FreeSewing"
date: '2020-02-22'
intro: "A look at what's new in FreeSewing 2.2, including Breanna, our bodice block for womenswear"
title: "A look at what's new in FreeSewing 2.2, including Breanna, our bodice block for womenswear"
---
Version 2.2 of FreeSewing is here, and it has a bunch of changes and
improvements big and small. The
[changelog](https://github.com/freesewing/freesewing/blob/develop/CHANGELOG.md)
lists all the changes, but here's what you need to know:
<!-- truncate -->
- [New pattern: Breanna is a bodice block for
womenswear](#new-pattern-breanna-is-a-bodice-block-for-womenswear)
- [New/Different measurements to better suit
womenswear](#newdifferent-measurements-to-better-suit-womenswear)
- [Generate patterns in _standard_ sizes (no account
required)](#generate-patterns-in-standard-sizes-no-account-required)
Let's have a look at what it all means:
## New pattern: Breanna is a bodice block for womenswear
Meet [Breanna](/designs/breanna/), our bodice block for womenswear.
A block (or sloper) may not the most exciting pattern to look at, but it plays
an important role as it is the basic form which other patterns are built on.
For example, we have a similar block for menswear called
[Brian](/designs/brian/) and our [Aaron](/designs/aaron/),
[Bent](/designs/bent/), [Carlita](/designs/carlita/),
[Carlton](/designs/carlton/), [Huey](/designs/huey/), [Hugo](/designs/hugo/),
[Jaeger](/designs/jaeger/), [Simon](/designs/simon/),
[Simone](/designs/simone/), [Sven](/designs/sven/), and
[Wahid](/designs/wahid/) patterns can all can trace their lineage back to Brain
somehow.
In other words, Breanna is an important building block for us to extend our
womenswear collection. But that does not mean it is not good news for you too.
If you have been sewing for a while, chances are drafting your own block/sloper
has perpetually been on your to-do list. Well, good news, because here is your
block ready to go.
Oh, and of course it has all the bells and whistles you can expect from us.
Have a look at [the pattern options](/docs/designs/breanna/options/) and I
think you'll be positively impressed. If nothing else, this block can
accommodate 1 or 2 bust darts in 15 different places, giving you 120 unique
ways to place your bust dart(s).
Breanna has has been a while in the making, and we'd love to get your feedback
on it. Making a muslin from a block like this really isn't much work. So if
you've got some time to spare to whip this one up and let us know how it went,
that would be great.
## New/Different measurements to better suit womenswear
Now that we're making our jouney into womenswear, we realized that the
measurements we use on the site are somewhat skewed towards menswear. In
addition, we had a lot of people stuggling with the shoulder slope measurement.
So we decided to re-visit our measuremets, and we've made the following
changes:
### We have a bunch of new _HPS_ measurements
We've settled on the High-Point Shoulder, the so-called HPS point, as the basis
for many of the vertical torso measurements. If you're not certain what/where
the HPS point is, [check the HPS documentation](/docs/sewing/hps/).
### We discontinued the Center Back To Neck measurement
As we mentioned earlier, we use measurements from the HPS point now for
vertical torso measurements. As a result, we've discontinued the old _Center
back neck to waist_ measurement.
Given that this measurement is now gone, you may need to add another
measurement for certain patterns.
### We changed the way the shoulder slope is measured
We noticed that people struggles with the way we asked them to measure [the
shoulder slope measurement](/docs/measurements/shoulderslope) so we have
changed how to do that.
Because the new method yields a very different number (that ultimately captures
the same thing, how much your shoulder slopes downward) here too we had to go
in and reset all the existing data. In other words, you'll have to re-measure
your shoulder slope.
## Generate patterns in _standard_ sizes (no account required
To get great patterns, you need good measurements, and a bunch of them. It's
what we do here at FreeSewing, and most of the problems with patterns are
because something goes wrong with taking measurements.
Unfortunately, there is no magic shortcut for this. If you want something
made-to-measure, it's always going to start with acurate measurements.
This does raise the bar for people who are new to the site, and want to kick
the tires.
> _Oooh, free patterns, nice_ 😍 _Wait, I have to create an account first?_ 🤔
> _And take all these measurements?_ 😬 _That seems like a lot of work._
Fair enough.
In addition, our regular visitors also asked us whether they could get to see a
pattern and play around with it without first having to put in a bunch of
measurements.
So **_drumroll_** that is no longer required. We now offer all our patterns in
_standard sizes_. Not only do you not have to take measurements to try out the
patterns, you don't even have to sign up or log in. No account needed, just
pick a pattern, a size, and you're good to go.
Obvously, made-to-measure patterns is _our thing_, and we're not changing that.
But we're hoping that by lowering the bar to try out our platform, more people
will give FreeSewing a try.
### What are standard sizes anyway?
The hardest thing about adding support for standard sizes? Figuring out what
the heck standard sizes are supposed to be. We took a stab at it, and if you're
curious you can check out our sizing table here (link no longer exists).
For menswear, our size range is 32 to 50, and they are based on a size 38
baseline that we then graded up and down. For womenswear, our size range is 28
to 48, and they are based on a size 34 baseline that we then graded up and
down.
:::tip
If you're not sure what these numbers are, they are the neck circumference in
cm.
:::
While we tried to make our size ranges inclusive, and these tables are an
honest attempt to come up with something that makes sense, please understand
that this is not our core business. We've love to hear your feedback on the
sizing tables, and are open to tweaks and suggestions, but at the end of the
day, what we want is to give you a pattern drafted to your measurements. These
sizes are just a way to lure you in 🤫
## Also: All the other stuff
Now go and click around, for we've changed a bunch of other stuff too.
If you bump into any problems or have questions, as always [our chat room is
the place to get in touch](https://discord.freesewing.org/).
And if you happen to like what we do here, perhaps now is a good time to tell
your friends about FreeSewing. After all, they can now check it out without
needing to sign up.
PS: We have [a handy share page](/share/) you can use for this.

View file

@ -0,0 +1,100 @@
---
authors: 1
caption: "A photo by <a href='https://unsplash.com/@onevagabond'>Paulo Silva</a> shows an empty Times Square in New York during the COVID pandemic"
date: '2021-01-10'
intro: "Aren't we all glad 2020 is over?"
title: 'The year 2020 was something alright'
---
Aren't we all glad 2020 is over?
Realistically, at least some of you, or your loved ones, got caught in the
absolute gut-punch that is the COVID-19 pandemic. Either indirectly through
lost income or other practical concerns, or directly by battling the virus
itself.
<!-- truncate -->
I can only offer my sympathy, and the belief that things will get better. So
hang in there, and let me briefly take your mind of things by sharing some good
news against the bleak backdrop of the annus horribilis that 2020 turned out to
be.
##### The face mask tsunami
FreeSewing found itself — absolutely unexpected I might add — under the glaring
spotlight of international media this year. A tidal wave of attention rolled
over us, and while the peak lasted only a good month, the repercussions and
long tail of these events have shaped our entire year.
It all started in February when COVID-19 was starting to rear its ugly head in
Europe and the supplies of personal protective equipment (PPE) started
dwindling. It seemed that face masks were going to be a big factor in trying to
slow down the spread of this disease, but they were getting harder to come by
every day.
So on February 28th, after some design and experimentation, we [published our
face mask pattern on freesewing.org](/blog/florence-face-mask/). Three weeks
later, we followed up with [a one-page PDF that people could share and
adapt](/blog/facemask-frenzy/).
We did not really want to be in the center of this, we just wanted to help
people make masks. But when [Forbes ran an editorial calling on people to help
out with the healthcare workers PPE
shortage](https://www.forbes.com/sites/tjmccue/2020/03/20/calling-all-people-who-sew-and-make-you-can-help-solve-2020-n95-type-mask-shortage/),
we suddenly found ourselves in the middle of a gigantic spotlight. The article
prominently featured FreeSewing, and linked to [our blog
post](/blog/facemask-frenzy).
At that time, there were few patterns available for face masks, and soon enough
a long list of other publications started running links to our pattern and
website. When I found [the instruction video that I made for the pattern
staring back at me on the website of the New York
Times](https://www.nytimes.com/2020/03/31/opinion/coronavirus-n95-mask.html),
it was a veritable oh-crap moment. Sure enough, in the month following the
Forbes publication, a million people descended on freesewing.org.
The sudden jump in visitors and users (not to mention patrons) made it clear
that I am a bottleneck of the project, and so in the latter half of the year
we've set out to [try and remedy that with some community
building](/blog/a-call-for-help/). On one hand it's slow going, but on the
other hand if I look at how vibrant the FreeSewing community is today, it
almost beggars belief that we did all of this throughout 2020. We now have
regular [contributor
calls](https://github.com/freesewing/freesewing/discussions), and [our chat
rooms are never empty](https://discord.freesewing.org/).
I hope to continue to fade into the background and let other people carry some
of the torches. Not because I don't want to work on this anymore, but because I
want to grow FreeSewing beyond what I can do on my own.
##### Black Lives Matter Since this is a look at 2020, I also want to pause and
acknowledge the **Black Lives Matter** movement and the ongoing problem of
systemic racism. It is an area where my background and genetic material makes
me ill-equipped to take a leading role, so here too I rely on our community for
guidance.
We are also expanding our efforts to provide patterns that work for all body
types, and I am particularly proud of how many of our users come to us because
our patterns help them project the gender of their choice.
##### We helped more people than ever this year On the practical side of
things, I have extended our book year with a couple of weeks so that going
forward, we will just follow the calendar years. With a couple of hours left in
this year, FreeSewing's revenue for 2020 clocks in at **10.736,82 euro**. That
makes 2020 and absolute bumper year, and it's more than all previous years
combined (2015: € 256.65, 2016: € 473.50, 2017: € 673.14, 2018: € 3162.14,
2019: € 4109.38).
As always, all of FreeSewing's revenue -- the entire 10.736,82 euro -- [goes to
Doctors Without Borders](/docs/about/pledge/).
I am truly grateful for your continued support and contributions. I feel like
in a very small way, we are able to apply some balm to the hurt that seems at
times omnipresent in the world.
Thank you so much, and stay safe
love joost

View file

@ -0,0 +1,191 @@
---
authors: 1
caption: 'We are building a bedrock of loyal supporters to ensure a sustainable future for freesewing.org, our code, our patterns, and our community.'
date: '2017-12-10'
intro: 'Calling all Patrons; Join our bedrock of loyal supporters.'
title: 'Calling all Patrons; Join our bedrock of loyal supporters.'
---
Every year, on this day, I write about what happened in the previous 12 months,
and look ahead at what you can expect in the year ahead.
This year is no different, but boy do I have a lot to talk about.
<!-- truncate -->
## 2017: the year freesewing was born
While freesewing was conceived in 2016 -- that's when I started working on it
-- 2017 was the year it saw the light of day.
Freesewing core v1.0.0 [was released in March this
year](/blog/announcing-freesewing/). For most people, that event passed under
the radar. An open source platform for made-to-measure sewing patterns? What
does this thing actually do?
A lot, as it turns out. Something that's become more obvious since [the release
of freesewing.org](/blog/open-for-business/) at the end of August.
![Image from the original announcement blog
post](https://posts.freesewing.org/uploads/open_e799153fa2.jpg)
On Christmas day, it will (only) have been four months since this release. Yet
in that time, we've signed up **2735 users** for whom we host **1522 drafts**,
and **2359 models**.
We launched with 11 patterns, and have since added 4, bringing the current
total to 15. Half of those new patterns are a community effort, which is
particularly promising.
Yes, it's still early days. But I think it's safe to say that the decision to
reinvent makemypattern.com as an open source project was the right call.
Speaking of which.
## MakeMyPattern.com is no more Shortly before publishing this blog post, I've
pulled the plug on [makemypattern.com](https://makemypattern.com/).
You can try to visit that link, but you'll only end up back here.
MakeMyPattern.com saw the light of day in 2012. It went through a number of
iterations since, and today is finally superseded by freesewing.org.
![Was it an M? A book? A bird? Boobs? We may never
know](https://posts.freesewing.org/uploads/mmp_27886c8346.png)
We had a good run, and I feel that for any project, it's a good way to go when
you get cannibalized by something better that you inspired.
## Let's talk money Like every year on this day, I've transferred all donations
into the account of [Médecins Sans Frontières/Doctors Without
Borders](http://www.msf.org/).
This year we came in at **673.14€**, so that's what I've transferred.
> Why should we give you money in the first place if you're just going to give
> it away?
I've also come to understand that people have a bunch of questions about this.
Like _Why are you doing this?_ and _Why should we give you money in the first
place if you're just going to give it away?_
These questions have led to some soul-searching recently. What I've learned is
that it's not easy to talk about money. Nor is it easy to put in words things
that you know to be right, almost instinctively.
But I wanted to be completely transparent about what's going on, so I made the
effort to write about these things on [our pledge page](/docs/about/pledge).
And I'm copying them here verbatim:
> ##### Noblesse oblige
>
> You probably assume that we ask for money to keep the servers running. But
> thats not exactly true.
>
> I dont know if youre familiar with the phrase _noblesse oblige_ but it
> essentially means that privilege entails responsibility.
>
> I am privileged, and thus I have responsibilities. I am very fortunate to
> have been born in Western Europe, have a good job, and a roof over my head.
>
> Could I use the money? Yes I could. Do I need the money? No I dont.
>
> ##### The value of your support
>
> The main risk to Freesewing is the same as any open source project out there:
> maintainer burnout.
>
> While I no longer carry Freesewing alone — and I cant stress enough how much
> I value the work of all contributors — that doesnt make me immune to
> feelings of _Why the fuck am I doing this?_
>
> When people become Patrons (or donate), they give more than money. To me, the
> main value is the message they send to me and other people working on this.
> And that message is: _Hey, youre doing a worthwhile thing. Keep up the good
> work_.
>
> ##### The value of your money
>
> It is not just about the money. But that doesnt mean the money is not
> important. Much to the contrary.
>
> Raising money by doing something I love and then passing it on to charity
> allows me to sleep at night.
>
> I could volunteer at a soup kitchen, or teach underprivileged children how to
> sew. But instead Im working on Freesewing.
>
> Which is why all the money raised through freesewing goes to charity. It
> makes this project not only fun to do, but also socially responsible. And I
> need that to convince myself that yes, its OK to spend all my time doing
> this, because at the end of the year, I get to write a check to people who
> need it so much more.
>
> ##### Charity is not sexy
>
> Heres the tricky part: People donate less once they know the money goes to
> charity in the end. I wish it wasnt the case, but it is.
>
> So Im not explicitly mentioning it on our Patrons page, which is presented
> like you would see on a business site.
>
> Yes, everything is free, and the money doesnt actually go to paying the
> server bills (because I choose to pay them out of my pocket for reasons
> outlined above). But that doesnt mean that these contributions are not
> crucial to the well-being of the project, or at the very least its maintainer
> (that would be me).
Did you notice that in the text above I mentioned our Patrons page? That is
because I've rolled out a bunch of changes today.
Having taken the time to reflect on the money side of things, I've realized
that it's an important factor in the well-being of this project. I also
believe that donations -- while motivating and appreciated -- are not the best
way to go about this.
So, as of today, we are calling all Patrons.
## Become a Patron of Freesewing
To ensure a sustainable future for freesewing.org, our code, our patterns, and
our community, we need to build a bedrock of loyal supporters. Patrons who
support us in our core work; Developing an open source platform for
made-to-measure sewing patterns.
![We have three tiers of
Patronage](https://posts.freesewing.org/uploads/patron_medals_2160e69d77.jpg)
We have different tiers of Patronage, each with their own perks. You can
support us for as little as 2€, and it only takes a minute.
> We are not changing the nature of the site. All patterns, and all our code,
> will remain free.
We are not changing the nature of the site. All patterns, and all our code,
will remain free. What we are changing is the way we raise funds. From a
system of impulsive donations to a community of caring Patrons. If you are
someone who cares, then [check out what we have to offer](/patrons/join).
I sincerely believe we are doing a worthwhile thing here. If you feel that way
too, then I ask you to pledge whatever you can so that we can write many more
chapters in this book.
> ### Become a Patron today #### You can sign up for as little as 2€, and it
>
> only takes a minute.
> [Find out more](/patrons/join)
Thank you, and have a great year.
joost
<small>
PS: It would be a shame if there's people out there willing to support us who don't know about
this. So perhaps you could share this image on social media?{' '}
<i class="fa fa-arrow-down" aria-hidden="true"></i>
</small>
![Sharing is
caring!](https://posts.freesewing.org/uploads/patrons_ig_45e11fe270.png)

View file

@ -0,0 +1,63 @@
---
title: 'Claim your showcase posts for eternal glory'
caption: "Who's that cat?"
date: '2024-02-25'
intro: 'Showcase posts have alwasy been loosely attributed, we want to fix that'
authors: 1
---
There are [over 300 showcase posts on FreeSewing.org](/showcase) where our
users showcase their makes. That's a truly great thing because it's not only
nice to see what people come up with, it also gives new visitors to the site a
good idea of what to expect from a given design.
<!-- truncate -->
Showcase posts have been around for a while and they have survived a number of
technical overhauls in how things work under the hood. And that's starting to
show.
## Who made what?
There was a time when showcase posts were attributed by a
name or description. Like _tony made this_. At one point, we assigned them to
the username, but because users can change their username, such a system decays
into chaos over time.
We'd like to address this growing library of showcase posts and make sure that
as many as possible are properly credited to their makers.
For this reason, we've implemented a few changes:
- Showcase posts are now assigned to the user's FreeSewing ID.
- When a showcase post is credited like this, we'll show the user's bio under
the post
- When there is no user credited, we will show a button to either claim this
post as your own, or suggest another user in case you know who is the maker.
This way, given some time and collective effort, we hopefully will soon enough
have properly credited all posts.
:::tip What is my FreeSewing ID?
You can find your FreeSewing ID on [your
account page](/account) or go to [FreeSewing.org/id](/id).
:::
## Next steps The first thing to do is to make sure everything is properly
credited.
Afterwards, we can use this link between the showcase post and user to -- for
example -- show a list of showcase posts on a user's profile.
If you have your own showcase posts on FreeSewing, please go ahead and claim
them. Or if you know who made them, let us know.
## Also applies to blog posts
The same principle applies to blog posts, but since all but 2 posts on the site
are written by that same dude, it's not really something where we need you
help.
That being said, it does give you a good idea of what things will look like, as
you can see below.

View file

@ -0,0 +1,92 @@
---
authors: 1
caption: 'This release brought to you from Bangkok'
date: '2018-03-21'
intro: "We've just pushed the button on core 1.8.0. That bump in minor number is typically because we have a new pattern, but in this case, there's two reasons:"
title: 'Freesewing core 1.8: Jaeger Jacket is in, across back measurement is out'
---
We've just pushed the _release_ button on core 1.8.0. That bump in minor number
is typically because we have a new pattern, but in this case, there's two
reasons:
- The [Jaeger Jacket](/designs/jaeger) is now available
- We've gotten rid of the _across back_ measurement
<!-- truncate -->
Read on for the details.
## Announcing the Jaeger Jacket
For [my refasioners entry last year](/blog/the-refashioners-2017/) I designed a
jacket pattern that I intially wanted to release alongside my make. It didn't
work out that way, and I asked for a bit more time to get the pattern out the
door.
Turns out that when I say _a bit more time_ it means 6 and a half months, so
please forgive me for the delay, but here it is, [the Jaeger
Jacket](/designs/jaeger).
![Note that in my post back then, I was talking about the Blake Blazer, but
I've since renamed it because Jaeger Jacket is just
cooler](https://posts.freesewing.org/uploads/jaeger_1cb91a3cd3.jpg)
### Jaeger Sport Coat is not an alliteration
Jaeger is a sport coat style of jacket. As in, a single-breasted jacket with a
2-button closure, and patch pockets.
In other words, this is a garment that's typically worn on jeans or other
trousers, and not as part of a suit.
### Options galore
Jaeger comes with 38 options, so you can change _a lot_ about this pattern.
No need to worry though, it also comes with sensible defaults, so you can just
as well ignore all those choices.
## The across back measurement is no more
Speaking of sensible defaults, Jaeger is not the only new thing in freesewing
core 1.8.0, which is out today.
We've also gotten rid of the _across back_ measurement.
The across back measurement was cause of a great deal of confusion among our
users. More often than not, when someone contacted us because their pattern
looked wonky, an unrealistic across back measurement was to blame.
The roots of the across back measurement go back to a time when, instead of the
_shoulder to shoulder_ measurement we use now, we had the _shoulder length_
measurement. That one was also source of some confusion, so we phased it out
in favour of the _shoulder to shoulder_ measurement.
The thing is that if we know the _shoulder to shoulder_ measurement, we can
guestimate with reasonable accuracy what the _across back_ measurement will be.
So, instead of asking you for it, we simply assume now.
### But you're dumbing down the pattern
In case the _this pattern comes with 38 options_ bit above wasn't enough of a
giveaway, we're pretty committed here at freesewing to give you all the knobs
to tweak your drafts.
Replacing a measurement with a value calculated based on another measurement
may seem to go against that, but there's no need to worry. We've made sure you
can still muck about with your across back.
Patterns that used to require the _across back_ measurement now have a new
advanced option: the _across back factor_. It allows you to tweak how we
calculate your across back measurement, but does make sure to keep it within
boundaries that are sensible.
![The across back factor
option](https://posts.freesewing.org/uploads/acrossback_60791a4392.png)
As such, we feel we're preventing mistakes for the casual user, without taking
away power from you, the pattern option guru.
Or to put it differently, you can still muck about with your across back, but
by default, you don't have to worry about it anymore.

View file

@ -0,0 +1,108 @@
---
authors: 1
caption: 'Scales, how do they work?'
date: '2018-01-04'
intro: 'Freesewing core v1.3.0 is out; Comes with fixes so good that we back-ported them to all your drafts'
title: 'Freesewing core v1.3.0 is out; Comes with fixes so good that we back-ported them to all your drafts'
---
On the last day of 2017, in our [monthly roundup of all the freesewing
news](/blog/roundup-2017-12/) , we wrote about the looming issue with
incorrectly scaled drafts, aka [Core issue #204 - The Inkscape default units
quandary](https://github.com/freesewing/core/issues/204).
<!-- truncate -->
I won't go over [all that](/blog/roundup-2017-12/) again, but it boils down to
the fact that the [Inkscape](http://inkscape.org/) maintainers have changed
Inkscape's internal DPI (dots per inch) from 90 to 96. A change that goes in
effect from version 0.92 onwards.
Left unchecked, this change would cause all freesewing patterns to be
incorrectly scaled. That's because we assume 90DPI in our SVG output, and
scale accordingly.
![That 'oh-shit' moment when we realized the full impact of the DPI
change](https://posts.freesewing.org/uploads/oh_shit_90b4969a5d.gif)
When the switch to 96DPI goes into effect, all patterns would be off by 6.66%.
Which is really the kind of difference that is too small to notice when
eyeballing a pattern, yet large enough to completely mess up your garment.
The issue is also more troublesome than it would seem at the surface. First of
all because we can't just switch to 96DPI as there are now two versions out
there that use a different default DPI under the hood. We need a solution that
works for both.
![Screenshot of a freesewing pattern that is incorrectly scaled in the latest
Inkscape release](https://posts.freesewing.org/uploads/inkscape_b96e2bb510.png)
Furthermore, while any fix we implement would apply to new drafts, all existing
drafts generated before the fix would still be impacted.
In other words, if you drafted a pattern last week, or a month ago, that
pattern would not scale correctly in a recent version of Inkscape. And since
we use Inkscape in our SVG-to-PDF tool-chain, it would also be incorrectly
scaled if you came here and downloaded a PDF.
Clearly, something needed to be done. And fast.
## The fix for new drafts
From today's release of core v1.3.0 onwards, our SVG files no longer depend on
any DPI setting.
Rather than use the internal units and apply an SVG transform to scale the
entire pattern, we've bolted down the units to mm and updated the SVG viewBox
to apply the scaling.
Obviously, this is how we should have done it from the start. Everyday is a
school day.
If you're worried about the use of mm in your draft (because you're used to
imperial units), rest assured that those mm will stay under the hood. You won't
be able to tell the difference.
## The fix for pre-existing drafts
To avoid problems with pre-existing drafts, we needed to come up with a
solution for those too.
We essentially have two options:
- Re-draft all those drafts
- Patch them in-place without changing the draft itself
Re-drafting fixes the issue as every new draft will be handled by the latest
core version that does include the fix.
However, core also ships with regular updates, tweaks, and fixes in the
patterns themselves. So by re-drafting a draft generated on a previous version
of core, there's no guarantee the draft won't change.
In principle that change would always be an improvement. But one person's bug
is another person's feature, and we do prefer not to [move your
cheese](https://en.wikipedia.org/wiki/Who_Moved_My_Cheese%3F).
![Don't touch my
stuff](https://posts.freesewing.org/uploads/who_moved_my_cheese_0cd51a25d6.jpg)
So, instead we decided to patch all drafts we have on file in-place with the
new scaling code, without touching any other aspect of the draft.
As you're reading this, this has already been done, and all freesewing drafts
should now scale correctly. Everywhere.
## Also: version awareness
We've also made changes to our backend systems to store the version of
freesewing core that generated your draft.
If since you generated your draft we've rolled out new features or fixes,
you'll be notified that an update is available:
![If you draft is generated with an old version of freesewing core, we'll tell
you about it](https://posts.freesewing.org/uploads/upgrade_dee342e3fb.png)
Whether you update your draft or not is up to you. If you don't want to loose
the info in your _old_ draft, rather than update it in-place, you can fork it.

View file

@ -0,0 +1,44 @@
---
authors: 1
caption: 'Cycling like a true gentlemen'
date: '2021-03-06'
intro: "Wouter is at it again, this time around he's treating us to classic cycling breeches."
title: "These Cornelius cycling breeches are Wouter's latest gift to us all"
---
[Wouter](https://www.instagram.com/wouter.vdub/) is at it again, this time
around he's treating us to classic cycling breeches.
<!-- truncate -->
He writes:
> When I was a child my dad would tell me stories about hiking in the
> mountains, something magical for a kid living in The Netherlands. He had a
> suitcase with his mountaineering things in it, and part of that were some
> 'knickerbockers', baggy trousers that reached down to your calves.
>
> When I moved to the USA in my thirties and started hiking myself, I would
> often reflect on those talks and felt sad that I didn't inherit those
> knickerbockers. Then when I discovered sewing, I decided I would make myself
> a pair, but no good patterns presented themselves.
>
> This year someone on the Freesewing discord server posted a link to The
> 'Keystone' Systems, Practical methods of cutting, from around the turn of the
> century. This contained a drafting system for 'Cycling Breeches', which
> seemed to be close to what my father used to wear, and could be translated
> into a Freesewing pattern. A project was born, and I'm happy to be able to
> present the results to you.
As Wouter mentioned; This pattern is based on [the Keystone drafting
method](https://archive.org/details/keystonesystemsc00heck/page/n5/mode/2up).
Not that it matters because as always you get it made-to-measure for you [right
here on FreeSewing.org](/designs/cornelius/).
![Here's the man
himself](https://posts.freesewing.org/uploads/wouter_9fbc821146.jpg)
Note that you don't have to cycle to wear these. All that's required is a
willingness to show of those socks (or calfs).
Cornelius is available at [/designs/cornelius/](/designs/cornelius/).

View file

@ -0,0 +1,102 @@
---
title: 'Email communication breakdown post-mortem'
caption: "I love email, but it's hard to handle a lot of it"
date: '2024-01-02'
intro: 'From the end of 29 October 2023 until 2 January 2024, some emails sent to me fell between the cracks'
authors: 1
---
Between the 29th of October 2023 until the 2nd of January 2024, emails sent to
joost@joost.at (my personal email) or various @freesewing.org email addresses
went unnoticed. Since noticing the issue today I have gone through the backlog
and set aside any messages that I need to deal with.
<!-- truncate -->
However, this is a manual and tedious process so it's possible that I'll miss a
few. In addition, it's also very possible that emails sent to me 2 months ago
required a speedier response.
These emails would have included users asking for help, but also notifications
about new patrons who've signed up or any donations FreeSewing received.
I'd like to apologize to all those who I should have been in touch with but
didn't. To be as transparent as possible, I will explain in detail what
happened below, and outline the steps I've taken to avoid this from happening
again in the future.
## My email setup To understand what happened, I should start by explaining my
email setup.
I have historically used joost@decock.org as my personal email. It's tied to
Google in the way that is no longer possible today, using one of those
grandfathered-in domain setups that they don't allow you to have any longer. I
don't trust Google as far as I can throw them, but Gmail is the best mail
client for my needs because I don't want to spend my time carefully organizing
email, I just want to search and find what I'm looking for. Nothing comes close
to Gmail when it comes to that.
Furthermore, as an Android user, this primary not-really-gmail-but-still-a-bit
Google account is also tied to my phone, and a host of other things that are
important. In other words, I need to have _some_ Google account, so this is it.
That being said, I don't trust Google to not one day accidentally disable my
account and I know full well that when that happens, I won't have any way of
getting it back because Google doesn't do support. And I can't even blame them
as I'm not a paying customer. So I want a different email provider, one where
I am a paying customer, and that provider is fastmail. I could have migrated
the decock.org domain to it, but that poses two problems:
- I still need a Google account
- Some members of my family have a decock.org email address, so I would have to
find a solution for them too and they are not too tech-savvy so that would
have been a hassle.
So, a couple of years ago I decided to bite the bullet, bought the joost.at
domain, and made that my primary email.
Obviously, my previous emails is still used by people and companies, and I have
to keep the Google account active too, so now I have two inboxes to manage. I
thought I had found a clever solution for that, and that's where things went
wrong.
## It worked until it didn't
I had setup my Google account to pull in email from my fastmail account via
POP3. This worked great and since both mailboxes are configured to allow me to
send email from both addresses, it's transparent to my correspondents.
That all worked fine. But if I go into the settings and check that rule today,
I see this:
![The image alt goes
here](https://imagedelivery.net/ouSuR9yY1bHt-fuAokSA5Q/blog-email-breakdown-post-mortem-1/public 'An error message saying that emails have not been imported since 29 October
2023')
Google stopped pulling in these emails, and somehow neglected to notify me of
this. Because of this setup, I had not been checking my Fastmail inbox, so
from one day to the next I didn't see anything sent to my new email address.
At this point, you're probably wondering why I didn't notice. The answer is
partially that I get a lot of email, but if I'm being honest, at some level I
probably suspected something was _off_ but I didn't realize exactly what, and
because I was busy looking into it was kicked down the road.
## Going forward
Since I cannot trust Google to reliably pull in the emails from my Fastmail
inbox, I will instead pivot to a _inbox double-zero_ approach. By which I mean,
I will manage both inboxes and apply _inbox-zero_ as that's how I do things.
Given my neglect of my Fastmail inbox, it had 100k+ unread messages in them. I
went through the messages since the end of October and set aside emails that I
need to follow-up on. Then I archived the rest and now am back on top of
things, albeit with a small pile of backlog to deal with.
I am particularly sorry for those people who signed up as patrons or donated to
FreeSewing and didn't even get as much as an acknowledgment. I admit that this
sort of _administrativia_ is not my strong suit, but my response time is not
typically measured in months.
Apologies, and I will try to do better this year.

View file

@ -0,0 +1,165 @@
---
authors: 1
caption: 'Would you close a lane because one driver was playing their music too loud?'
date: '2017-09-07'
intro: "Thanks for nothing Microsoft; Email shouldn't be this hard"
title: "Thanks for nothing Microsoft; Email shouldn't be this hard"
---
People with an email address from Microsoft --- think Hotmail, MSN, live.com,
outlook.com and their numerous variants --- are significantly less likely to
sign up for this website.
That's because more than 4 times out of 10, they never receive their account
activation email.
<!-- truncate -->
## What's going on?
Let's first look at what's happening. Here's a relevant snippet from the logs:
`````Failed: postmaster@mg.freesewing.org -> ********@hotmail.co.uk 'Confirm
your freesewing account' Server response: 550 5.7.1 Unfortunately, messages
from [104.130.122.15] weren't sent. Please contact your Internet service
provider since part of their network is on our block list. ````
What this means is that part of the MailGun network is on their block list. As
a result, they (more on who they are later) are not delivering any messages
that go out.
[MailGun](https://www.mailgun.com/) is a popular email service for developers.
It's used by this site to send out emails, like the account activation emails.
Other people use this service too, and perhaps some of them, at some point,
delivered some spam messages through mailgun. Or it may just have been some guy
with a last name that tends to trigger spam filters.
![Some other MailGun customers. Not exactly a dodgy service is
it?](https://posts.freesewing.org/uploads/mailgun_19f315d4d6.png)
Point is, this IP address or one of its neighbours got *a bad rep*. It happens.
But to flat-out refuse to accept any messages from this host (or an entire
network of hosts) is the equivalent of shutting down a highway lane (or entire
highway) because one car in that lane played its music obnoxiously loud that
one time.
Which brings me to our next question:
## Who would do something like that?
Good question. Here are some numbers:
![A graph of mail delivery since the launch of this
site](https://posts.freesewing.org/uploads/emailgraph_d14d476efa.png)
The graph above represents emails that were sent out since the launch of this
site. The small subsection of the graph that is red are emails that are
dropped.
This website sends out different kinds of email:
- The account confirmation email
- The *I forgot my password* emails
- Comment reply notifications
The graph represents all email, but I'm focussing on the account confirmation
emails only. They are the most important after all.
> Apart from the 1 outlier, every message that was blocked, was blocked by
> Microsoft
Here's a list of all domains that blocked legitimate activation emails to their
users:
- btinternet.com
- hotmail.com
- hotmail.co.uk
- live.ca
- live.com
- live.com.au
- live.nl
- msn.com
- outlook.com
Apart from that very first entry in the list (on which only 1 message was
blocked) all of these are Microsoft domains.
Let me restate that: Apart from the 1 outlier, every message that was blocked,
was blocked by Microsoft.
## What's the impact?
So what sort of impact does that have on people?
Well, at the time I'm writing this, there are 817 registered users, and about
80% (661) have also activated their account.
![A disproportionate amount of pending activations is from users with an email
address managed by
Microsoft](https://posts.freesewing.org/uploads/activations_06987b6065.svg)
From those people who were able to activate their account, less than 1% (6)
have an email address managed by Microsoft. In the group of people who did
not, or were not able to, activate their account, more than half have such an
address.
More than 40% of account confirmation emails are simply blocked by Microsoft
and, based on the number of activations, it seems likely that even when they
aren't block at the SMTP relay, they get filtered somewhere further down the
line.
As things stand, it seems almost impossible for the average
hotmail/outlook/live/MSN/... user to sign up for this site.
## What can we do about it?
I chose mailgun for a number of reasons. Not having to handle SMTP outselves
simplifies the code. Not depending on a local SMTP deamon makes the code more
portable, and MailGun has a bunch of cool features that allow you to do things
like replying to comments via email.
Microsoft's crude methods of spam filtering don't invalidate any of those
reasons.
Using MailGun means using their SMTP relays, and being at the mercy of the
reputation of that relay. The only way around that is to configure a dedicated
relay in MailGun so that freesewing.org traffic is shielded from others, and we
become masters of our own reputation.
![$59 per month? Perhaps
not](https://posts.freesewing.org/uploads/pricing_52f0e817cb.png)
For that priviledge, MailGun charges 59 dollar per month, which amounts to 708
dollar yearly. I invite you to take a look at [the donations
history](/about/pledge#donations-history), and you'll understand that's not
going to happen either.
I could challenge the block list, and try to get the relay unblocked. But
that's pretty much tilting at windmills when the host is not under my control.
Not to mention that MailGun doesn't just have that one host.
It seems that I'm running low on options and quiet frankly, I'm also running
out of patience.
## What I'm going to do about it
Microsoft is a behemoth, and I'm just a guy. I can't fight them on this.
Unless I Titanfall their ass.
![Block this,
bitch](https://posts.freesewing.org/uploads/titanfall_cb5a210468.gif)
Do you think Gmail is ever abused to send out spam? You know it is. Do you
think they would ever block all email coming from Gmail? You know they won't.
So last night, I rolled out some changes to work around the issue. If you have
a *problemtic* email address, in addition to the regular email, this site will
send out a second email through Gmail.
I'd like to see them block that.
> ##### Signup trouble? Help is available If you are (still) having problems
> signing up, don't hesitate to [get in touch](/contact).
`````

View file

@ -0,0 +1,29 @@
---
authors: 1
caption: 'Dr. Dragnea from Antwerp University hospital wearing a Florence facemask'
date: '2020-03-19'
intro: "Calling all makers: Here's a 1-page PDF facemask pattern; Now go make some and help beat this thing"
title: "Calling all makers: Here's a 1-page PDF facemask pattern; Now go make some and help beat this thing"
---
Well that escalated quickly. We published [our Florence Face Mask
pattern](/designs/florence/) at the end of last month, hoping it would be
helpful. Now, [hospitals are actively reaching out to people to beg them to
make fabric face masks for their staff](https://www.uza.be/mondmaskers).
<!-- truncate -->
So obviously, we want to help:
- PDF pattern for our Florence face mask:
- A4 facemask pattern
- Letter facemask pattern
- [Instructions for the facemask pattern](/docs/designs/florence/instructions/)
No go and make a bunch. Our healthcare workers are counting on you!
<YouTube id="VcQ69_ANsRA" />
:::tip
You can support us by [becoming a patron](/patrons/join/) ❤️
:::

View file

@ -0,0 +1,47 @@
---
authors: 1
caption: 'Keep those virus-infesed droplets at bay with our Florence face mask'
date: '2020-02-28'
intro: 'We just published a face mask pattern, because coronavirus'
title: 'We just published a face mask pattern, because coronavirus'
---
The spread of the Covid-19 coronavirus seems relentless and despite the world's
best attempts, the possibility of a global pandemic is getting more real every
day. Which begs the question, what can we do?
<!-- truncate -->
Perhaps not much, but we can design patterns. So say hi to the [Florence face
mask](/designs/florence/), a simple pattern that allows you make your own face
masks. One that you can wear all day, because those medical ones aren't exactly
comfortable.
Whether or not a face mask actually helps is subject of debate. I've been
to-ing and fro-ing a bit about whether to publish a pattern for a face mask.
Then — earlier today — I read a [Guardian](https://www.theguardian.com/)
article called [Yes, it is worse than the flu: busting the coronavirus
myths](https://www.theguardian.com/world/2020/feb/28/coronavirus-truth-myths-flu-covid-19-face-masks).
It included the following passage under the _Face masks dont work_ myth:
:::note Claim: Face masks dont work
_Wearing a face mask is not an iron clad guarantee that you wont get sick
viruses can also transmit through the eyes and tiny viral particles, known as
aerosols, can still penetrate masks. However, masks are effective at
capturing droplets, which is the main transmission route of coronavirus, and
some studies have estimated a roughly five-fold protection versus no barrier.
If you are likely to be in close contact with someone infected, a mask cuts
the chance of the disease being passed on._
:::
This only focusses on preventing you from getting coronavirus. There's also
the fact that the mask can help (a bit) in keeping you from spreading it
further. Because the way things are going right now, it looks like we should
do every little thing we can to try to slow this thing down.
So when I got home, I set out to rush out a face mask pattern. Common sense
says it can't hurt, and apparently there's more to it too. If nothing else, I
want a cute face mask, not one of the ugly ones you see on the news.
Stay safe out there.

View file

@ -0,0 +1,49 @@
---
authors: 1
caption: 'Quentin surrounded by our future robot overlords.'
date: '2017-12-06'
intro: "The Florent Flat Cap pattern is now available in beta. That's another gift sorted."
title: "The Florent Flat Cap pattern is now available in beta. That's another gift sorted."
---
After last month's release of [Benjamin](/designs/benjamin), here's another
pattern release to channel the dapper gentleman within: the [Florent Flat
Cap](/designs/florent).
<!-- truncate -->
Like Benjamin, this pattern was contributed by the freesewing community, rather
than yours truly. In this case, it was [Quentin Felix](/users/user?id=241) who
signed for the design.
I asked Quentin if he wanted to write this announcement blog post himself, but
he argued that it was time I did something too. He did have this to share
though, on his reasons for designing this pattern in the first place:
:::note Quentin on his reasons for designing Florent
I like wearing flat caps myself. I didn't use to, but I have a friend Florent
who inspired me to wear them (now you know where the name comes from).
The first time I made a flat cap, it took me ages to scale the pattern to an
exact fit, involving a lot of messing about with tracing paper. There's a
number of patterns available online for flat caps. But they all come in one
size only.
I wanted to make this more accessible to people. And, while I was at it, I
also drew on my experience to add some tweaks for a better fit.
For example, this design extends lower at the back of the head, which gives
it better hold.
:::
This _why should we always have to re-invent the wheel?_ could pretty much be
freesewing's slogan.
## It's the season for giving
Florent requires only one measurement: the head circumference. As such, this
release is perfectly timed. With the holiday season coming up, here's another
gift you can make yourself.
Thanks Quentin!

View file

@ -0,0 +1,66 @@
---
authors: 1
caption: 'Like history? Then this release is going to be right up your alley.'
date: '2021-10-17'
intro: "I've just pulled the release lever on version 2.19 of FreeSewing and there's a lot that went into this release. For full details, you can check out the changelog , here I'll stick to the highlights:"
title: 'FreeSewing 2.19 brings Bee, Lunetius, Tiberius, Walburga, a new plugin, and a bunch of improvements and fixes'
---
I've just pulled the release lever on version 2.19 of FreeSewing and there's a
lot that went into this release. For full details, you can [check out the
changelog](https://github.com/freesewing/freesewing/blob/develop/CHANGELOG.md#2190-2021-10-17),
here I'll stick to the highlights:
<!-- truncate -->
## Lunetius, Tiberius, and Walburga
[Lunetius](/designs/lunetius), [Tiberius](/designs/tiberius/), and
[Walburga](/designs/walburga/) are three new patters from **Rika Tamaike** who
is the latest addition to our growing team of designers. These are all
historical pattern:
- Lunetius is a lacerna, a historical Roman cloak
- Tiberius is a historical Roman tunic
- Walburga is a tabard/surcoat, a historical garment from medieval Europe
I'm not much of a history buff myself, so I'm looking forward to see what these
will look like when people are going to start making them. What I can say for
sure is that all of these are pretty straight-forward, so they should be fun to
make.
## The Bee bikini
Also new in this release is [Bee](/designs/bee/), a bikini pattern that you can
pair with the [Uma Undies pattern](/designs/uma/). **Bobgeorgethe3rd**
signed for the code, the design of the bikini was a collaboration with
**PrudenceRabbit**.
I'm hoping for some people in the Southern hemisphere to make this one because
I suspect it might be a while before bikini weather returns to those of use
living above the equator.
This too is a fast and simple make, so go and check it out.
## The versionfree-svg plugin
This is a bit more under-the-hood stuff, but we've also published
[plugin-versinofree-svg](https://www.npmjs.com/package/@freesewing/plugin-versionfree-svg),
a new plugin that will strip the version information from FreeSewing's SVG
output.
This is handy because it allows diffing between different versions. By not
including the version information, you can see what (if anything) has changed
between different versions of a pattern, something we use ourselved in our QA
pipeline.
## Backported snap options from the v3 roadmap
Speaking of under-the-hood, the [snap
proposal](https://github.com/freesewing/freesewing/discussions/1331) on [our v3
roadmap](https://github.com/freesewing/freesewing/discussions/1278) was
something we liked so much that we promptly backported it to v2, and it's
already being used in different patterns.
Have fun with the new patterns, and for feedback, questions, or suggestions,
[come hang out with us on Discord](https://discord.freesewing.org).

View file

@ -0,0 +1,66 @@
---
authors: 1
caption: 'A pink Hi and its BLÅHAJ ancestor'
date: '2022-06-27'
intro: 'FreeSewing 2.21 adds Bob, Hi, Lucy, Noble and Unice designs'
title: 'FreeSewing 2.21 adds Bob, Hi, Lucy, Noble and Unice designs'
---
We've rolled out FreeSewing v2.21 today, and it's a massive update with tons of
changes, improvements, and behind the scenes work. Check the CHANGELOG if
you'd like get all the nitty-gritty detail. For this blog post, I'll focus on
what you are probably going to be most interested in: New designs.
<!-- truncate -->
This release brings 5 new designs to our catalog, so let's dive right in:
## Bob is a bib
We've been using the construction of a Bib pattern in our [pattern design
tutorial](https://freesewing.dev/tutorials/pattern-design) for years. Yet we
never added the pattern to our catalog. An oversight that's been corrected now,
and we now carry [Bob the bib](/designs/bob) in our collection.
Perfect for babies and grown-ups who are messy eaters alike, since you can make
Bob in any size.
## Hi is a shark
It's been kinda hard to contain our excitement about this, but hear me out: A
while ago rumours started swirling that IKEA would discontinue it's
[BLÅHAJ](https://www.ikea.com/us/en/p/blahaj-soft-toy-shark-90373590/) stuffed
shark, which is a universally beloved toy.
The FreeSewing community wasn't just going to idly stand by while the worlds
friendliest shark ran the risk of becoming extinct. A conservation effort
gained momentum, and ultimately Wouter matter-of-factively dropped the first
**Hi** pictures in our Discord.
Oh, and you should know that [Hi](/designs/hi) can be made big or small. The
patterns scales up to a 5m shark. What are you waiting for?
## Lucy is a tie-on pocket
We've seen a number of historical designs recently, specifically from Starf.
This time around, it's SeaZeeZee who added [Lucy](/designs/lucy) which is a
pattern for a tie-on pocket.
It's a quick make for cosplay, historical re-enactment, or for all those
dresses that don't come with pockets.
## Noble is a body block with prince(ss) seams
Wouter (of Hi fame) has another contribution in this release: Noble is a
princess seam body block. Blocks tend to be undervalued but they are the
foundation that other designs are built on, so having a new body block with
prince(ss) seams is very exciting.
## Unice is for undies
Unice is a variant of Ursula, another
undies pattern. It was designed by Anna who describes it as _the undies for
those with significant rear estate_.
It has a few changes and tweaks in comparison to Ursula, and is specifically
intended to fit _any_ body. So if you're rear estate mogul, try these out.

View file

@ -0,0 +1,148 @@
---
authors: 1
caption: "Picture by <a href='https://stock.tookapic.com/jenniferforjoy' target='_BLANK' rel='nofollow'>Jennifer</a>"
date: '2017-06-12'
intro: 'When we released freesewing core at the end of March, my focus immeadiatly shifted to building our front-end so that freesewing.org could fully replace makemypattern.com .'
title: "We're JAMstack, we're JAMstack, we're JAMstack, we're JAMstack, we're JAMstack, we're JAMstack, we're JAMstack, and I hope you like JAMstack too"
---
When we released freesewing core at the end of March, my focus immeadiatly
shifted to building our front-end so that [freesewing.org](/) could fully
replace [makemypattern.com](https://makemypattern.com/).
<!-- truncate -->
I believe that the value of freesewing lies with the core platform and our
patterns. But without a user friendly way to expose that value, it will
largely go ignored.
So we needed a website that lets people generate patterns. Makemypattern.com
&mdash; arguably the best comparison of something similar &mdash; runs on
Drupal 7, and my initial idea was to run the new site on Drupal 8. I went down
that path far enought to be confident I could get it to work, and hook it up to
our backend. At which point I switched gears and turned my attention to what is
now known as freesewing core.
Core took about 7 months to build, and a lot has changed since then. Or perhaps
I have changed, I certainly learned a lot along the way. Either way, I've
decided to do things different.
## The problem with a CMS
I have no beef with Drupal but the idea of managing the freesewing website
through any Content Management System (CMS) does not appeal to me.
One of the main reason is that so much information is stored under an opaque
database layer which makes it difficult to manage. That goes for content where
posts, metadata, images, and so on is all spread across tables, locations, and
folders. But there's also the theme that has a bunch of stuff in it, there's
the custom Drupal modules to connect to the backend, and so on and so forth.
> I wanted that same approach in a website. Except, it can't be static because
> it has to, you know, do stuff.
When we were finalizing core, I built a documentation site for it based on
[Jekyll](https://jekyllrb.com/). It felt like a breath of fresh air in
comparison. Just a bunch of markdown files, with some SASS, images, and some
JavaScript thrown in the mix, and it all compiles into a neat static website.
It's easy to manage, and it integrates nicely with a GitHub-centered workflow
that is going to be famliar to potential contributors.
I wanted that same approach in a website. Except, it can't be static because it
has to, you know, do stuff.
## An alternative approach: JAMstack
I first learned about JAMstack when I started looking into hosting for said
core documentation site. It was initially hosted on GitHub pages which
provides free hosting. They also have SSL or a custom domain name, but you
can't have both. Which was kind of a deal breaker.
Looking for alternatives, I stumbled onto [Netlify](https://www.netlify.com/),
who do both SSL and custom domains and have a free-tier for open source
projects (thanks guys). Furthermore, [this video by Netlify CEO Mathias
Biilmann](https://vimeo.com/163522126) got me really excited about JAMstack.
Unless you're familiar with JAMstack, I suggest you check out the video, but it
boils down to this:
- **J** = JavaScript
- **A** = APIs
- **M** = Markup
The idea is that you build your static site (markup) that you then make
interactive with JavaScript that hooks up to one or more APIs.
So in our case, rather than having a straight-forward documentation site with
easy-to-edit markdown and a complex CMS to handle the dymanic stuff, let's just
build one simple site that is statically generated, yet uses JavaScript and
APIs to do the smart stuff.
## Running before you can walk
I must admit that in my enthousiasm to embrace this new approach I got a little
ahead of myself. Suddenly, I was no longer building a simple site, but I was
up to my eyeballs in isomorphic rendering, client-side routing, React and
Redux, Node.js and ES6 transpiling.
> If you don't know what any of that means, you might get a hint of the
> frustration I felt as I was trying to tame all these new beasts.
>
> If you do know what it all means, where were you back in April when I walked
> through the valley of the React of death?
Point is, I'm not a developer and I was in way over my head. While I was
learning new things every day, I wasn't making much progress on the actual task
at hand, and felt frustrated with my inability to do even the most mundane
things.
After a month of frustration, loads of trial and seemingly even more error, I
threw in the towel. Eff this newfangled shiny JavaScript all the young kids
are using, I'll stick to what I know.
Which is essentially the basics of jQuery. In other words, stuff that was
pretty cool 10 years ago.
## 10 year old jam is still jam right?
So here we are, freesewing.org is a site powered by the JAMstack. And you know
what, it seems to do what it needs to do.
We have Jekyll build out static site, and when we push to our master branch, it
gets autmatically deployed to Netlify.
> Eff this newfangled shiny JavaScript all the young kids are using
We have [a brand new data API](https://github.com/freesewing/data) build on
[the Slim framework](https://www.slimframework.com/). It handles all user
data. Things like accounts, measurements, models, and drafts, but also comments
on this website and so on.
It also talks to core for us, and every time you draft a pattern, we don't just
give you the pattern, but we also run a comparison of your pattern to a range
of standard sizes, which is kinda cool.
And we have other cool stuff, like the ability to fork or redraft an existing
draft.
## This is a starting point
I hope the user experience/interface is not going to be a roadblock for people.
I've made a great deal of effort to make the drafting process as intuitive as
possible and I think that in comparison to our demo (or the makemypattern
interface for that matter) it's a vast improvement.
Then again, I'm sure things will break left or right, or that some of you don't
like the colours or whatnot.
The point is that I set out to build something that can replace
makemypattern.com so that I could tell all of you _Hey, come over and play with
this new thing_.
I think if nothing else, I can do that now. And if you see room for
improvement, please join the effort, we're only getting started.
<small>PS: For those of you wondering about the title of this post:</small>
<YouTube id="oFRbZJXjWIA" />

View file

@ -0,0 +1,30 @@
---
title: 'FreeSewing is now pay-what-you-want'
caption: 'A Person Putting Coin in a Piggy Bank, by Maitree Rimthong'
date: '2023-11-03'
intro: "We have updated our pricing, here's why"
authors: 1
---
I am clearly doing a terrible job at convincing people to [become a FreeSewing
patron](/patrons/join), because while user growth is ever increasing, our
revenue is not. In addition, inflation is very real, meaning that even when
revenue remains the same, we're actually less able to make a difference.
<!-- truncate -->
The rise in users also brings additional costs. Simply put, running
FreeSewing.org is getting more expensive year by year, while revenue does not
keep up, and that's a trend that has me worried for a while now.
I considered my options for how to deal with this. I could raise prices, but
that seems to punish our patrons who are already supporting us, while the vast
majority of users does not contribute. Having more patrons is the obvious
answer, but I don't like asking for money and in general seem to be rather bad
at this sort of thing.
So, after thinking it over for a while, I have decided to go the other
direction. Rather than raise prices, I have removed pricing altogether.
FreeSewing is now pay-what-you-want. Yes, you can still have everything for
free, but [please consider supporting us with whatever amount you can
spare](/patrons/join).

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