1
0
Fork 0

Merge branch 'develop' into cdocs

This commit is contained in:
joostdecock 2025-05-24 12:07:19 +02:00
commit b914b6299f
89 changed files with 1571 additions and 737 deletions

View file

@ -1,50 +1,51 @@
# Contributing to FreeSewing # Contributing to FreeSewing
First off, First off,
thank you for being part of the freesewing community, thank you for being part of the FreeSewing community,
and for taking the time to contribute! ❤️ and for taking the time to contribute! ❤️
The following is a set of guidelines for contributing to FreeSewing. The following is a set of guidelines for contributing to FreeSewing.
These are mostly guidelines, not rules. These are mostly guidelines, not rules.
Use your best judgment, and feel free to propose changes to this document in a pull request. Use your best judgment, and feel free to propose changes to this document in a pull request.
## Code of Conduct ## Code of Conduct
When you engage with us, or when you engage with others, When you engage with us, or when you engage with others,
please remember [the FreeSewing community standards](https://freesewing.org/docs/about/community-standards/). please remember [the FreeSewing community standards](https://freesewing.eu/docs/about/community-standards/).
As a contributor, you are also expected to uphold [the FreeSewing Code of Conduct](https://freesewing.dev/guides/code-of-conduct). As a contributor, you are also expected to uphold [the FreeSewing Code of Conduct](https://freesewing.dev/guides/code-of-conduct).
<Tip> :::tip
##### See something, say something ##### See something, say something
Please report unacceptable behavior to [us@freesewing.org](mailto:us@freesewing.org). Please report unacceptable behavior to [us@freesewing.eu](mailto:us@freesewing.eu).
</Tip> :::
## I don't want to read this whole thing I just have a question! ## I don't want to read this whole thing I just have a question!
Please don't file an issue to ask a question. Please don't file an issue to ask a question.
You'll get faster results by contacting us on Discord. You'll get faster results by contacting us on Discord or the Forum.
You can get to our Discord server via https://discord.freesewing.org/. You can get to our Discord server via https://discord.freesewing.eu/ .
You can get to our Forum via https://forum.freesewing.eu/ .
Please keep in mind that our community members live all over the world. Please keep in mind that our community members live all over the world.
So what's daytime for you might be the middle of the night for others. So what's daytime for you might be the middle of the night for others.
Please be patient. Sooner or later, somebody will answer. Please be patient. Sooner or later, somebody will answer.
## What should I know before I get started? ## What should I know before I get started?
Most of FreeSewing's source code lives in [our monorepo](https://github.com/freesewing/freesewing). Most of FreeSewing's source code lives in [our monorepo](https://codeberg.org/freesewing/freesewing).
There are some exceptions: There are some exceptions:
- [`svgtopdf`](https://github.com/freesewing/svgtopdf): The repository holding the source code for our on-demand tiler backend
- [`tile`](https://github.com/freesewing/tile): The repository holding the source code for our command-line tiler - [`tile`](https://github.com/freesewing/tile): The repository holding the source code for our command-line tiler
### FreeSewing packages ### FreeSewing packages
We publish a lot of JavaScript packages on NPM. You can find the full list [in the `packages` folder of our monorepo](https://github.com/freesewing/freesewing/tree/develop/packages). We publish a lot of JavaScript packages on NPM. You can find the full list
[in the `packages` folder of our monorepo](https://codeberg.org/freesewing/freesewing/src/branch/develop/packages).
## How Can I Contribute? ## How Can I Contribute?
@ -52,10 +53,7 @@ This file is mostly geared towards code contributors, but there's plenty of othe
### Your first contribution ### Your first contribution
Unsure where to begin contributing to FreeSewing? Unsure where to begin contributing to FreeSewing?
You can start by looking through the issues labeled [good first issue](https://codeberg.org/freesewing/freesewing/issues?q=&type=all&sort=&state=open&labels=344963). You can start by looking through the issues labeled [good first issue](https://codeberg.org/freesewing/freesewing/issues?q=&type=all&sort=&state=open&labels=344963).
Don't be afraid to take on an issue. If you get stuck, [we'll help you out](https://discord.freesewing.org/). Don't be afraid to take on an issue. If you get stuck, [we'll help you out](https://discord.freesewing.eu/).
<ReadMore />

View file

@ -23,7 +23,9 @@
"cutNeckBinding.t": "The neck binding is not shown", "cutNeckBinding.t": "The neck binding is not shown",
"cutNeckBinding.d": "The **Neck Binding** (10) is a rectangular piece of main fabric {{{ w }}} wide and {{{ l }}} long, with the grainline parallel to the length.", "cutNeckBinding.d": "The **Neck Binding** (10) is a rectangular piece of main fabric {{{ w }}} wide and {{{ l }}} long, with the grainline parallel to the length.",
"cutWaistband.t": "The waistband is not shown", "cutWaistband.t": "The waistband is not shown",
"cutWaistband.d": "The **Waistband** (8) is a rectangular piece of ribbing fabric {{{ w }}} wide and {{{ l }}} long, with the grainline parallel to the width." "cutWaistband.d": "The **Waistband** (8) is a rectangular piece of ribbing fabric {{{ w }}} wide and {{{ l }}} long, with the grainline parallel to the width.",
"neckCircumference.t": "The neck opening is too small",
"neckCircumference.d": "The neck opening of {{{ opening }}} is smaller than the head circumference of {{{ head }}}.\n\nPlease check and adjust Collar Ease option setting as needed to ensure the neck opening is large enough to accommodate head circumference."
}, },
"o": { "o": {
"ribbingHeight": { "ribbingHeight": {

View file

@ -34,6 +34,8 @@ function hugoBack({
const neckOpening = new Path() const neckOpening = new Path()
.move(points.cbNeck) .move(points.cbNeck)
.curve(points.cbNeck, points.neckCp2, points.neck) .curve(points.cbNeck, points.neckCp2, points.neck)
const length = neckOpening.length() * 2
store.set('front_neck_len', length)
points.raglanTipBack = neckOpening.shiftFractionAlong(0.7) points.raglanTipBack = neckOpening.shiftFractionAlong(0.7)
const neckOpeningParts = neckOpening.split(points.raglanTipBack) const neckOpeningParts = neckOpening.split(points.raglanTipBack)
// Paths // Paths

View file

@ -61,6 +61,8 @@ function hugoFront({
const neckOpening = new Path() const neckOpening = new Path()
.move(points.cfNeck) .move(points.cfNeck)
.curve(points.cfNeckCp1, points.neckCp2, points.neck) .curve(points.cfNeckCp1, points.neckCp2, points.neck)
const length = neckOpening.length() * 2
store.set('back_neck_len', length)
points.raglanTipFront = neckOpening.shiftFractionAlong(0.8) points.raglanTipFront = neckOpening.shiftFractionAlong(0.8)
const neckOpeningParts = neckOpening.split(points.raglanTipFront) const neckOpeningParts = neckOpening.split(points.raglanTipFront)

View file

@ -1,4 +1,3 @@
export const collarEase = 0.05
export const armholeDepthFactor = 0.5 export const armholeDepthFactor = 0.5
export const shoulderEase = 0 export const shoulderEase = 0
export const shoulderSlopeReduction = 0 export const shoulderSlopeReduction = 0
@ -14,3 +13,4 @@ export const lengthBonus = { pct: 10, min: 0, max: 20, menu: 'style' }
export const sleeveLengthBonus = { pct: 2, min: 0, max: 10, menu: 'style' } export const sleeveLengthBonus = { pct: 2, min: 0, max: 10, menu: 'style' }
export const ribbingHeight = { pct: 10, min: 4, max: 20, menu: 'style' } export const ribbingHeight = { pct: 10, min: 4, max: 20, menu: 'style' }
export const pocketWidth = { pct: 50, min: 35, max: 65, menu: 'style' } export const pocketWidth = { pct: 50, min: 35, max: 65, menu: 'style' }
export const collarEase = { pct: 15, min: 0, max: 40, menu: 'fit' }

View file

@ -17,6 +17,8 @@ function hugoSleeve({
options, options,
measurements, measurements,
macro, macro,
log,
units,
part, part,
}) { }) {
// Top of raglan sleeve // Top of raglan sleeve
@ -233,6 +235,46 @@ function hugoSleeve({
y: points.wristLeft.y + 15 + sa, y: points.wristLeft.y + 15 + sa,
}) })
/*
* Check neck opening size and warn if necessary.
*/
const sleeve_neck_len =
new Path()
.move(points.raglanTipBack)
.curve(points.raglanTipBackCp2, points.raglanTopCp1, points.raglanTop)
.curve(points.raglanTopCp2, points.raglanTipFrontCp1, points.raglanTipFront)
.length() * 2
const neck_opening = store.get('front_neck_len') + store.get('back_neck_len') + sleeve_neck_len
const diff = measurements.head - neck_opening
const discrepancy = diff / measurements.head
const warning_limit = 0.04
let label = ' bigger than '
if (diff > 0) label = ' smaller than '
log.info(
': ' +
units(neck_opening) +
' neck opening is ' +
units(Math.abs(diff)) +
label +
' than ' +
units(measurements.head) +
' head circumference, with Collar Ease set to ' +
(options.collarEase * 100).toFixed(0) +
'%.'
)
if (discrepancy > warning_limit) {
store.flag.warn({
msg: `hugo:neckCircumference`,
replace: {
opening: units(neck_opening),
head: units(measurements.head),
},
})
}
return part return part
} }

View file

@ -129,7 +129,10 @@ export const draftBarrelCuff = (part) => {
export const decorateBarrelCuff = (part) => { export const decorateBarrelCuff = (part) => {
const { macro, store, snippets, Snippet, points, measurements, options, Point } = part.shorthand() const { macro, store, snippets, Snippet, points, measurements, options, Point } = part.shorthand()
// Cutlist // Cutlist
store.cutlist.setCut({ cut: 4, from: 'fabric' }) store.cutlist.setCut([
{ cut: 4, from: 'fabric' },
{ cut: 2, from: 'interfacing' },
])
// Title // Title
points.title = new Point(points.bottomRight.x / 2, points.bottomRight.y / 2) points.title = new Point(points.bottomRight.x / 2, points.bottomRight.y / 2)
@ -188,7 +191,10 @@ export const draftFrenchCuff = (part) => {
export const decorateFrenchCuff = (part) => { export const decorateFrenchCuff = (part) => {
const { macro, store, snippets, Snippet, points, measurements, options, Point } = part.shorthand() const { macro, store, snippets, Snippet, points, measurements, options, Point } = part.shorthand()
// Cutlist // Cutlist
store.cutlist.setCut({ cut: 4, from: 'fabric' }) store.cutlist.setCut([
{ cut: 4, from: 'fabric' },
{ cut: 2, from: 'interfacing' },
])
// Title // Title
points.title = new Point(points.bottomRight.x / 2, points.bottomRight.y / 2) points.title = new Point(points.bottomRight.x / 2, points.bottomRight.y / 2)

View file

@ -117,6 +117,18 @@
"centerBackDart": { "centerBackDart": {
"t": "Center back dart", "t": "Center back dart",
"d": "Whether or not to include a center back dart to fit a rounded back." "d": "Whether or not to include a center back dart to fit a rounded back."
},
"legacyWaistHips": {
"t": "Legacy waist and hips widths",
"d": "Enable this option to use the legacy (v3) way to calculate the waist and hips widths (using chest circumference) rather than the new way (using the waist and hips measurements)."
},
"legacyWaistHipsNo": {
"t": "Calculate waist and hips widths the new way",
"d": "Uses the waist and hips measurements to calculate the waist and hips widths"
},
"legacyWaistHipsYes": {
"t": "Calculate waist and hips widths the legacy (v3) way",
"d": "Uses the chest measurement to approximate the waist and hips widths"
} }
} }
} }

View file

@ -1,6 +1,13 @@
import { constructMainDart, shapeSideSeam, dartPath } from './shared.mjs' import { constructMainDart, shapeSideSeam, dartPath } from './shared.mjs'
import { back as brianBack } from '@freesewing/brian' import { back as brianBack } from '@freesewing/brian'
import { backInset, shoulderInset, neckInset, centerBackDart, backScyeDart } from './options.mjs' import {
backInset,
shoulderInset,
neckInset,
centerBackDart,
backScyeDart,
legacyWaistHips,
} from './options.mjs'
import { hidePresets } from '@freesewing/core' import { hidePresets } from '@freesewing/core'
function wahidBack({ function wahidBack({
@ -20,6 +27,13 @@ function wahidBack({
for (let i of Object.keys(paths)) delete paths[i] for (let i of Object.keys(paths)) delete paths[i]
delete snippets.armholePitchNotch delete snippets.armholePitchNotch
if (!options.legacyWaistHips) {
// Use actual measurements to set waist and hips points
points.waist = new Point((measurements.waist * (1 + options.waistEase)) / 4, points.cbWaist.y)
points.hips = new Point((measurements.hips * (1 + options.hipsEase)) / 4, points.cbHips.y)
points.hem = new Point(points.hips.x, points.cbHem.y)
}
// Back inset // Back inset
let shoulderLen = points.shoulder.dist(points.neck) let shoulderLen = points.shoulder.dist(points.neck)
let backInset = shoulderLen * options.backInset let backInset = shoulderLen * options.backInset
@ -279,6 +293,7 @@ export const back = {
neckInset, neckInset,
centerBackDart, centerBackDart,
backScyeDart, backScyeDart,
legacyWaistHips,
}, },
draft: wahidBack, draft: wahidBack,
} }

View file

@ -30,6 +30,7 @@ import {
s3Armhole, s3Armhole,
shoulderSlopeReduction, shoulderSlopeReduction,
backNeckCutout, backNeckCutout,
legacyWaistHips,
} from './options.mjs' } from './options.mjs'
function wahidFront({ function wahidFront({
@ -51,6 +52,14 @@ function wahidFront({
// Cleanup from Brian // Cleanup from Brian
for (let i of Object.keys(paths)) delete paths[i] for (let i of Object.keys(paths)) delete paths[i]
delete snippets.armholePitchNotch delete snippets.armholePitchNotch
if (!options.legacyWaistHips) {
// Use actual measurements to set waist and hips points
points.waist = new Point((measurements.waist * (1 + options.waistEase)) / 4, points.cfWaist.y)
points.hips = new Point((measurements.hips * (1 + options.hipsEase)) / 4, points.cfHips.y)
points.hem = new Point(points.hips.x, points.cfHem.y)
}
// Neck cutout // Neck cutout
points.closureTop = new Point( points.closureTop = new Point(
measurements.chest * options.frontOverlap * -1, measurements.chest * options.frontOverlap * -1,
@ -176,15 +185,31 @@ function wahidFront({
points.dartHipLeft, points.dartHipLeft,
points.pocketTopMid.y + pwvh points.pocketTopMid.y + pwvh
) )
points.pocketTopLeft = points.pocketTopMidLeft.shift(180 + options.pocketAngle, pw / 2) // The pocket can start out offset from the horizontal/vertical.
points.pocketBottomLeft = points.pocketTopLeft.shift(options.pocketAngle - 90, pwh) const startingAngleOffset = points.pocketBottomMidLeft.angle(points.pocketTopMidLeft) - 90
points.pocketTopLeft = points.pocketTopMidLeft.shift(
180 + options.pocketAngle + startingAngleOffset,
pw / 2
)
points.pocketBottomLeft = points.pocketTopLeft.shift(
options.pocketAngle - 90 + startingAngleOffset,
pwh
)
points.pocketTopMidRight = points.pocketTopMidLeft.flipX(points.pocketTopMid) points.pocketTopMidRight = points.pocketTopMidLeft.flipX(points.pocketTopMid)
points.pocketBottomMidRight = points.pocketBottomMidLeft.flipX(points.pocketTopMid) points.pocketBottomMidRight = points.pocketBottomMidLeft.flipX(points.pocketTopMid)
points.pocketTopRight = points.pocketTopMidRight.shift(options.pocketAngle, pw / 2) points.pocketTopRight = points.pocketTopMidRight.shift(
points.pocketBottomRight = points.pocketTopRight.shift(options.pocketAngle - 90, pwh) options.pocketAngle - startingAngleOffset,
pw / 2
)
points.pocketBottomRight = points.pocketTopRight.shift(
options.pocketAngle - 90 - startingAngleOffset,
pwh
)
// Store pocket bag length // Store pocket bag length
store.set('pocketBagLength', points.pocketTopMid.dy(points.cfHem) * 0.75) store.set('pocketBagLength', points.pocketTopMid.dy(points.cfHem) * 0.75)
if (options.frontScyeDart) { if (options.frontScyeDart) {
// Save original armhole width so we can restore it later
const original_chest_width = points.cfArmhole.dist(points.armhole)
// Front scye dart // Front scye dart
points._dartWidth = points.dartTop.shiftFractionTowards( points._dartWidth = points.dartTop.shiftFractionTowards(
points.armholeHollow.rotate(options.frontScyeDart, points.dartTop), points.armholeHollow.rotate(options.frontScyeDart, points.dartTop),
@ -230,6 +255,10 @@ function wahidFront({
if (typeof points[p] !== 'undefined') if (typeof points[p] !== 'undefined')
points[p] = points[p].rotate(options.frontScyeDart, points.dartTop) points[p] = points[p].rotate(options.frontScyeDart, points.dartTop)
} }
if (!options.legacyWaistHips) {
// Set armhole back to original width
points.armhole = points.cfArmhole.shiftTowards(points.armhole, original_chest_width)
}
points.armholeHollowCp1 = points.armholeHollowCp2.rotate(180, points.armholeHollow) points.armholeHollowCp1 = points.armholeHollowCp2.rotate(180, points.armholeHollow)
} }
// Facing/Lining boundary (flb) // Facing/Lining boundary (flb)
@ -314,6 +343,7 @@ function wahidFront({
*/ */
// Cutlist // Cutlist
store.cutlist.setCut({ cut: 2, from: 'fabric' }) store.cutlist.setCut({ cut: 2, from: 'fabric' })
store.cutlist.addCut({ cut: 2, from: 'interfacing' })
// Buttons // Buttons
points.button1 = new Point(0, points.closureTop.y + 10) points.button1 = new Point(0, points.closureTop.y + 10)
@ -561,6 +591,7 @@ export const front = {
s3Armhole, s3Armhole,
shoulderSlopeReduction, shoulderSlopeReduction,
backNeckCutout, backNeckCutout,
legacyWaistHips,
}, },
draft: wahidFront, draft: wahidFront,
} }

View file

@ -28,6 +28,7 @@ export const shoulderInset = { pct: 10, min: 0, max: 20, menu: 'advanced' }
export const neckInset = { pct: 5, min: 0, max: 10, menu: 'advanced' } export const neckInset = { pct: 5, min: 0, max: 10, menu: 'advanced' }
export const pocketAngle = { deg: 5, min: 0, max: 5, menu: 'advanced' } export const pocketAngle = { deg: 5, min: 0, max: 5, menu: 'advanced' }
export const shoulderSlopeReduction = { pct: 0, min: 0, max: 80, menu: 'advanced' } export const shoulderSlopeReduction = { pct: 0, min: 0, max: 80, menu: 'advanced' }
export const legacyWaistHips = { bool: false, menu: 'advanced' }
// Hide inherited options // Hide inherited options
export const bicepsEase = 0.15 export const bicepsEase = 0.15

View file

@ -16,6 +16,22 @@ export const constructMainDart = (part) => {
store.set('wr12', wr12) store.set('wr12', wr12)
store.set('hr12', hr12) store.set('hr12', hr12)
if (!options.legacyWaistHips) {
// Use largest chest-or-hips measurement to determine reduction
const largest_measurement = chest >= hips ? chest : hips
reduce.waist = largest_measurement - waist
reduce.hips = largest_measurement - hips
if (reduce.hips < 0) reduce.hips = 0
if (reduce.waist < 0) reduce.waist = 0
// Use a different reduction factor
const reduction_factor = 15
wr12 = reduce.waist / reduction_factor
hr12 = reduce.hips / reduction_factor
// Shift side seam to other direction by total amount added to dart
store.set('wr12', wr12 * -2)
store.set('hr12', hr12 * -2)
}
points.dartWaistCenter = new Point(points.armhole.x / 2, points.waist.y) points.dartWaistCenter = new Point(points.armhole.x / 2, points.waist.y)
points.dartWaistRight = points.dartWaistCenter.shift(0, wr12) points.dartWaistRight = points.dartWaistCenter.shift(0, wr12)
points.dartWaistLeft = points.dartWaistCenter.shift(180, wr12) points.dartWaistLeft = points.dartWaistCenter.shift(180, wr12)

View file

@ -264,7 +264,12 @@ export const BookmarkButton = ({ slug, type, title }) => {
className={`tw:daisy-btn tw:daisy-btn-secondary tw:daisy-btn-outline ${horFlexClasses}`} className={`tw:daisy-btn tw:daisy-btn-secondary tw:daisy-btn-outline ${horFlexClasses}`}
onClick={() => onClick={() =>
setModal( setModal(
<ModalWrapper flex="col" justify="top lg:justify-center" slideFrom="right"> <ModalWrapper
flex="col"
justify="top lg:justify-center"
slideFrom="right"
keepOpenOnClick="true"
>
<CreateBookmark {...{ type, title, slug }} /> <CreateBookmark {...{ type, title, slug }} />
</ModalWrapper> </ModalWrapper>
) )
@ -292,13 +297,12 @@ const CreateBookmark = ({ type, title, slug }) => {
const { setLoadingStatus } = useContext(LoadingStatusContext) const { setLoadingStatus } = useContext(LoadingStatusContext)
const { setModal } = useContext(ModalContext) const { setModal } = useContext(ModalContext)
const url = slug.toLowerCase().slice(0,4) === 'http' const url = slug.toLowerCase().slice(0, 4) === 'http' ? slug : `/${slug}`
? slug
: `/${slug}`
const bookmark = async (evt) => { const bookmark = async (evt) => {
evt.stopPropagation() evt.stopPropagation()
setLoadingStatus([true, 'Contacting backend']) setLoadingStatus([true, 'Contacting backend'])
let title = name
const [status] = await backend.createBookmark({ type, title, url }) const [status] = await backend.createBookmark({ type, title, url })
if (status === 201) { if (status === 201) {
setLoadingStatus([true, 'Bookmark created', true, true]) setLoadingStatus([true, 'Bookmark created', true, true])

View file

@ -118,7 +118,7 @@ export const Pattern = ({ id, Link }) => {
delete data.data delete data.data
delete data.userId delete data.userId
delete data.img delete data.img
data.settings = JSON.parse(data.settings) data.name += ' (clone)'
const [status, body] = await backend.createPattern(data) const [status, body] = await backend.createPattern(data)
if (status === 201 && body.result === 'created') { if (status === 201 && body.result === 'created') {
setLoadingStatus([true, 'Loading newly created pattern', true, true]) setLoadingStatus([true, 'Loading newly created pattern', true, true])

View file

@ -278,19 +278,19 @@ const DesignCard = ({ name, lineDrawing = false, linkTo, Link, onClick }) => {
const exampleImageUrl = examples.href[name] ? examples.href[name] : noExample const exampleImageUrl = examples.href[name] ? examples.href[name] : noExample
const bg = { aspectRatio: '1/1.4' } const bg = { aspectRatio: '1/1.4' }
if (!lineDrawing) { if (!lineDrawing) {
bg.backgroundImage = `url(${exampleImageUrl}` bg.backgroundImage = `url(${exampleImageUrl})`
bg.backgroundSize = 'cover' bg.backgroundSize = 'cover'
bg.backgroundPosition = 'center center' bg.backgroundPosition = 'center center'
} }
const inner = ( const inner = (
<div <div
className={`tw:flex tw:flex-col tw:flex-nowrap tw:items-start tw:justify-between tw:gap-2 tw:border-neutral-500 tw:group-hover:border-secondary className={`tw:flex tw:flex-col tw:flex-nowrap tw:justify-between tw:gap-2 tw:border-neutral-500 tw:group-hover:border-secondary
tw:w-full tw:h-full tw:border tw:border-2 tw:border-solid tw:p-0 tw:relative tw:rounded-lg tw:rounded-lg`} tw:w-full tw:h-full tw:border tw:border-2 tw:border-solid tw:p-0 tw:relative tw:rounded-lg`}
style={bg} style={bg}
> >
<h5 <h5
className={`tw:text-center tw:py-2 tw:px-4 tw:rounded-t tw:m-0 tw:w-full tw:group-hover:no-underline tw:group-hover:bg-secondary/80 tw:group-hover:text-secondary-content className={`tw:text-center tw:py-2 tw:px-4 tw:rounded-t tw:m-0 tw:group-hover:no-underline tw:group-hover:bg-secondary/80 tw:group-hover:text-secondary-content
${lineDrawing ? '' : 'tw:bg-neutral/80'}`} ${lineDrawing ? '' : 'tw:bg-neutral/80'}`}
> >
<span className={lineDrawing ? 'tw:text-base-100-content' : 'tw:text-neutral-content'}> <span className={lineDrawing ? 'tw:text-base-100-content' : 'tw:text-neutral-content'}>
@ -298,14 +298,14 @@ const DesignCard = ({ name, lineDrawing = false, linkTo, Link, onClick }) => {
</span> </span>
</h5> </h5>
{lineDrawing ? ( {lineDrawing ? (
<div className="tw:grow tw:w-full tw:h-auto tw:square tw:text-center"> <div className="tw:flex-auto tw:flex tw:justify-center">
<LineDrawing className="tw:w-5/6 tw:m-auto tw:my-0 tw:text-base-content" /> <LineDrawing className="tw:w-5/6 tw:text-base-content" />
</div> </div>
) : ( ) : (
<span /> <span />
)} )}
<div <div
className={`tw:flex tw:flex-row tw:items-center tw:justify-center tw:py-1 tw:px-2 tw:rounded-b tw:m-0 tw:w-full className={`tw:flex tw:flex-row tw:items-center tw:justify-center tw:py-1 tw:px-2 tw:rounded-b tw:m-0
${lineDrawing ? '' : `tw:text-neutral-content`}`} ${lineDrawing ? '' : `tw:text-neutral-content`}`}
> >
<Difficulty score={about[name].difficulty} className="tw:group-hover:text-secondary" /> <Difficulty score={about[name].difficulty} className="tw:group-hover:text-secondary" />

View file

@ -11,6 +11,7 @@ import { ViewIcon, viewLabels } from './views/index.mjs'
import { Tooltip } from './Tooltip.mjs' import { Tooltip } from './Tooltip.mjs'
import { import {
AsideIcon, AsideIcon,
CompactIcon,
DetailIcon, DetailIcon,
ExpandIcon, ExpandIcon,
ExportIcon, ExportIcon,
@ -241,7 +242,8 @@ export const HeaderMenuDraftViewUiPreferences = (props) => {
} }
export const HeaderMenuDraftViewFlags = (props) => { export const HeaderMenuDraftViewFlags = (props) => {
const count = Object.keys(flattenFlags(props.flags)).length const flatFlags = flattenFlags(props.flags)
const count = Object.keys(flatFlags).length
return ( return (
<HeaderMenuDropdown <HeaderMenuDropdown
@ -253,7 +255,12 @@ export const HeaderMenuDraftViewFlags = (props) => {
<HeaderMenuIcon name="flag" extraClasses="tw:text-secondary" /> <HeaderMenuIcon name="flag" extraClasses="tw:text-secondary" />
<span className="tw:hidden tw:lg:inline"> <span className="tw:hidden tw:lg:inline">
Flags Flags
<span>({count})</span> <NumberBadge
value={count}
color={
Object.values(flatFlags).some((it) => it.type === 'error') ? 'error' : 'secondary'
}
/>
</span> </span>
</> </>
} }
@ -263,6 +270,17 @@ export const HeaderMenuDraftViewFlags = (props) => {
) )
} }
const NumberBadge = ({ value, color = 'secondary', className = '' }) => {
return (
<div
className={`tw:ml-2 tw:inline-flex tw:items-center tw:justify-center tw:rounded-full tw:bg-${color} tw:text-${color}-content tw:text-xs tw:w-5 tw:h-5 tw:leading-none tw:font-semibold ${className}`}
style={{ lineHeight: '1rem' }}
>
{value}
</div>
)
}
export const HeaderMenuDraftViewIcons = (props) => { export const HeaderMenuDraftViewIcons = (props) => {
const { update, state } = props const { update, state } = props
const { settings = {} } = state // Guard against undefined settings const { settings = {} } = state // Guard against undefined settings
@ -302,7 +320,7 @@ export const HeaderMenuDraftViewIcons = (props) => {
tooltip="Switches Units between metric and imperial (see Core Settings)" tooltip="Switches Units between metric and imperial (see Core Settings)"
> >
<UnitsIcon <UnitsIcon
className={`${size} ${settings.units === accountUnits ? style.dflt : style.custom}`} className={`${size} ${settings.units === accountUnits || typeof settings.units === 'undefined' ? style.dflt : style.custom}`}
/> />
</Button> </Button>
) : null} ) : null}
@ -336,9 +354,11 @@ export const HeaderMenuDraftViewIcons = (props) => {
} }
tooltip="Turns Expand on or off (see Core Settings)" tooltip="Turns Expand on or off (see Core Settings)"
> >
<ExpandIcon {[false, 0, '0'].includes(settings.expand) ? (
className={`${size} ${[false, 0, '0'].includes(settings.expand) ? style.custom : style.dflt}`} <CompactIcon className={`${size} ${style.custom}`} />
/> ) : (
<ExpandIcon className={`${size} ${style.dflt}`} />
)}
</Button> </Button>
) : null} ) : null}
<HeaderMenuIconSpacer /> <HeaderMenuIconSpacer />

View file

@ -130,10 +130,15 @@ export const BookmarkedSetPicker = ({
const [status, body] = await backend.getBookmarks() const [status, body] = await backend.getBookmarks()
const loadedSets = {} const loadedSets = {}
if (status === 200 && body.result === 'success') { if (status === 200 && body.result === 'success') {
const setsRE = /\/set(\?id=|s\/)(\d+)$/
const unique_ids = new Set()
for (const bookmark of body.bookmarks.filter((bookmark) => bookmark.type === 'set')) { for (const bookmark of body.bookmarks.filter((bookmark) => bookmark.type === 'set')) {
let set const match = bookmark.url.match(setsRE)
if (match) unique_ids.add(match[2])
}
for (const id of unique_ids) {
try { try {
const [status, body] = await backend.getSet(bookmark.url.slice(6)) const [status, body] = await backend.getSet(id)
if (status === 200 && body.result === 'success') { if (status === 200 && body.result === 'success') {
const [hasMeasies] = hasRequiredMeasurements(Design, body.set.measies) const [hasMeasies] = hasRequiredMeasurements(Design, body.set.measies)
loadedSets[body.set.id] = { ...body.set, hasMeasies } loadedSets[body.set.id] = { ...body.set, hasMeasies }

View file

@ -216,6 +216,7 @@ export const MenuItemGroup = ({
config={item} config={item}
changed={menuValueWasChanged(currentValues[itemName], item)} changed={menuValueWasChanged(currentValues[itemName], item)}
Design={Design} Design={Design}
units={state?.settings?.units}
/> />
</div> </div>
</div>, </div>,

View file

@ -149,7 +149,9 @@ export const MenuListInput = ({
: 'tw:flex-col tw:items-start' : 'tw:flex-col tw:items-start'
}`} }`}
> >
<div className="tw:font-semibold">{config.choiceTitles[entry]}</div> <div className="tw:font-semibold">
{config.doNotTranslate ? entry : config.choiceTitles[entry]}
</div>
{compact || !config.choiceDescriptions ? null : ( {compact || !config.choiceDescriptions ? null : (
<div <div
className={`${config.dense ? 'tw:text-sm tw:leading-5 tw:py-1' : 'tw:text-base'} tw:font-normal`} className={`${config.dense ? 'tw:text-sm tw:leading-5 tw:py-1' : 'tw:text-base'} tw:font-normal`}
@ -203,12 +205,18 @@ export const MenuMmInput = (props) => {
/* /*
* Set a default step that matches the unit. * Set a default step that matches the unit.
* *
* Note that we could try to use something like 1.5875 for imperial to move in steps of 1/16 inches, * Imperial:
* but we round the mm values to two digits in the options, which would accumulate rounding errors. * mm, inch, decimal digits precision needed:
* * 1.27 mm, 0.1 in, 2 digits
* Because of this, we use 10ths of inches instead of 16ths of inches. * 3.175 mm, 1/8 in, 3 digits
* 1.5875 mm, 1/16 in, 4 digits
* 0.79375 mm, 1/32 in, 5 digits
* We previously rounded mm values to 2 digits in the options and used
* a 1.27 mm (1/10 in) step because precision didn't support 1/16 in
* or 1/32 in steps.
* We now round mm values to 4 digits and use a 1.5875 mm (1/16 in.) step.
*/ */
const defaultStep = imperial ? 1.27 : 1 // mm const defaultStep = imperial ? 1.5875 : 1 // mm
return ( return (
<MenuSliderInput <MenuSliderInput

View file

@ -88,7 +88,7 @@ const SampleOptionButton = ({ name, i18n, update }) => (
onClick={() => update.settings('sample', { type: 'option', option: name })} onClick={() => update.settings('sample', { type: 'option', option: name })}
> >
<BeakerIcon className="tw:w-5 tw:h-5" /> <BeakerIcon className="tw:w-5 tw:h-5" />
<span>{i18n.en.o[name].t}</span> <span>{i18n.en?.o[name]?.t ?? name}</span>
</button> </button>
) )

View file

@ -79,7 +79,7 @@ export const MenuListValue = ({ current, config, changed }) => {
else if (val) key = <BoolYesIcon /> else if (val) key = <BoolYesIcon />
else key = <BoolNoIcon /> else key = <BoolNoIcon />
const translated = config.doNotTranslate || key const translated = config.doNotTranslate ? val : key
return <MenuHighlightValue changed={changed}>{translated}</MenuHighlightValue> return <MenuHighlightValue changed={changed}>{translated}</MenuHighlightValue>
} }
@ -119,7 +119,7 @@ export const MenuMmValue = ({ current, config, units, changed }) => (
* Displays the current percentage value, and the absolute value if configured * Displays the current percentage value, and the absolute value if configured
*/ */
export const MenuPctOptionValue = ({ config, current, settings, changed, patternConfig }) => { export const MenuPctOptionValue = ({ config, current, settings, changed, patternConfig }) => {
const val = changed ? current : config.pct / 100 const val = changed ? current : config.dflt
return ( return (
<MenuHighlightValue changed={changed}> <MenuHighlightValue changed={changed}>

View file

@ -1,5 +1,5 @@
// Dependencies // Dependencies
import { linkClasses } from '@freesewing/utils' import { linkClasses, capitalize } from '@freesewing/utils'
// Hooks // Hooks
import React from 'react' import React from 'react'
// Components // Components
@ -20,7 +20,22 @@ export const DocsView = ({ state, config, update }) => {
<> <>
<HeaderMenu state={state} {...{ config, update }} /> <HeaderMenu state={state} {...{ config, update }} />
<div className="tw:m-auto tw:mt-8 tw:max-w-2xl tw:px-4 tw:mb-8"> <div className="tw:m-auto tw:mt-8 tw:max-w-2xl tw:px-4 tw:mb-8">
<H1>Documenation</H1> <H1>Documentation</H1>
{state?.design ? (
<Popout link>
<H5>Design Documentation</H5>
<p className="tw:text-lg">
You can find documentation for the {capitalize(state.design)} design at:
<br />
<b>
<a
className={linkClasses}
href={`https://freesewing.eu/docs/designs/${state.design}`}
>{`FreeSewing.eu/docs/designs/${state.design}`}</a>
</b>
</p>
</Popout>
) : null}
<Popout link> <Popout link>
<H5>Understanding the FreeSewing Pattern Editor</H5> <H5>Understanding the FreeSewing Pattern Editor</H5>
<p className="tw:text-lg"> <p className="tw:text-lg">
@ -29,8 +44,8 @@ export const DocsView = ({ state, config, update }) => {
<b> <b>
<a <a
className={linkClasses} className={linkClasses}
href="https://freesewing.org/docs/about/site/editor" href="https://freesewing.eu/docs/about/editor"
>{`FreeSewing.org/docs/about/editor`}</a> >{`FreeSewing.eu/docs/about/editor`}</a>
</b> </b>
</p> </p>
</Popout> </Popout>

View file

@ -43,7 +43,7 @@ export const TestView = ({ Design, state, update, config }) => {
if (missingMeasurements(state)) return <Null /> if (missingMeasurements(state)) return <Null />
const { settings } = state const { settings } = state
if (settings.sample) { if (settings?.sample) {
/* /*
* When testing/sampling one design, and then switching the editor to a different design, * When testing/sampling one design, and then switching the editor to a different design,
* we run the risk that settings.sample holds invalid configuration. Like testing an unused * we run the risk that settings.sample holds invalid configuration. Like testing an unused

View file

@ -158,7 +158,9 @@ export const handleExport = async ({
workerArgs.strings.setName = settings?.metadata?.setName workerArgs.strings.setName = settings?.metadata?.setName
? settings.metadata.setName ? settings.metadata.setName
: 'ephemeral' : 'ephemeral'
workerArgs.strings.yaml = yaml.dump(settings) const settingsWithoutLayout = structuredClone(settings)
delete settingsWithoutLayout.layout
workerArgs.strings.yaml = yaml.dump(settingsWithoutLayout)
workerArgs.strings.version = store?.data?.version ? store.data.version : '' workerArgs.strings.version = store?.data?.version ? store.data.version : ''
const notes = store?.plugins?.['plugin-annotations']?.flags?.note const notes = store?.plugins?.['plugin-annotations']?.flags?.note
? store?.plugins?.['plugin-annotations']?.flags?.note ? store?.plugins?.['plugin-annotations']?.flags?.note
@ -190,9 +192,9 @@ const flagsToString = (flags, mustache, t) => {
let first = true let first = true
let string = '' let string = ''
for (const flag of Object.values(flags)) { for (const flag of Object.values(flags)) {
let title = flag.replace ? mustache.render(flag.title, flag.replace) : flag.title let title = flag.replace ? mustache.render(t(flag.title), flag.replace) : t(flag.title)
title = he.decode(title) title = he.decode(title)
let desc = flag.replace ? mustache.render(flag.desc, flag.replace) : flag.desc let desc = flag.replace ? mustache.render(t(flag.desc), flag.replace) : t(flag.desc)
desc = desc.replaceAll('\n\n', '\n') desc = desc.replaceAll('\n\n', '\n')
desc = desc.replaceAll('\n', ' ') desc = desc.replaceAll('\n', ' ')
desc = he.decode(desc) desc = he.decode(desc)

View file

@ -20,7 +20,8 @@ const defaultPrintSettings = (units) => ({
size: units === 'imperial' ? 'letter' : 'a4', size: units === 'imperial' ? 'letter' : 'a4',
orientation: 'portrait', orientation: 'portrait',
margin: units === 'imperial' ? 12.7 : 10, margin: units === 'imperial' ? 12.7 : 10,
coverPage: true, coverPage: 1,
iconSize: 0.5,
}) })
export function menuLayoutSettingsStructure(units) { export function menuLayoutSettingsStructure(units) {
@ -76,6 +77,7 @@ export function menuLayoutSettingsStructure(units) {
), ),
}, },
icon: PageOrientationIcon, icon: PageOrientationIcon,
dflt: defaults.orientation,
}, },
margin: { margin: {
dense: true, dense: true,
@ -98,7 +100,7 @@ export function menuLayoutSettingsStructure(units) {
0: 'Do not include a cover page', 0: 'Do not include a cover page',
1: 'Include a cover page', 1: 'Include a cover page',
}, },
dflt: 0, dflt: defaults.coverPage,
}, },
iconSize: { iconSize: {
dense: true, dense: true,
@ -108,7 +110,7 @@ export function menuLayoutSettingsStructure(units) {
about: about:
'Controls the size of the icons that allow you to rotate/flip individual pattern parts', 'Controls the size of the icons that allow you to rotate/flip individual pattern parts',
min: 10, min: 10,
dflt: 0.5, dflt: defaults.iconSize,
step: 1, step: 1,
max: 200, max: 200,
}, },

View file

@ -333,6 +333,25 @@ export const CodeIcon = (props) => (
</IconWrapper> </IconWrapper>
) )
/**
* An SVG icon that looks like arrows pointing inwards
*
* @component
* @param {object} props - All component props
* @param {boolean} [props.className = 'tw:m-6 tw:h-6'] - The CSS classes to apply to the SVG element
* @param {number} [props.stroke = 2] - The stroke width
* @param {JSX.Element} props.children - The component childer, the inner content of the SVG tag
* @param {boolean} [props.fill = false] - Whether or not to fill the icon
* @param {number} [props.fillOpacity = 1] - The fillOpacity to apply
* @param {string} [props.dashArray = null] - An optional stroke dashArray to apply to the stroke
* @returns {JSX.Element}
*/
export const CompactIcon = (props) => (
<IconWrapper {...props}>
<path d="m15 15v4.5m0-4.5h4.5m-15.8-11.3 5.25 5.25m6 0v-4.5m0 4.5h4.5m-15.8 11.3 5.25-5.25h-4.5m4.5 0v4.5m11.3-15.8-5.25 5.25m-6 0h-4.5m4.5 0v-4.5m11.3 15.8-5.25-5.25" />
</IconWrapper>
)
/** /**
* An SVG icon that looks like FIXME * An SVG icon that looks like FIXME
* *
@ -2106,4 +2125,3 @@ export const ZoomOutIcon = (props) => (
<path d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607ZM13.5 10.5h-6" /> <path d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607ZM13.5 10.5h-6" />
</IconWrapper> </IconWrapper>
) )

View file

@ -74,7 +74,7 @@ export const translateStrings = (list, translations = {}) => {
else if (string) { else if (string) {
if (translations[string]) { if (translations[string]) {
translated += `${translations[string]}`.replace(/&quot;/g, '"') + ' ' translated += `${translations[string]}`.replace(/&quot;/g, '"') + ' '
} else translated += `${string}` } else translated += `${string}` + ' '
} }
} }

View file

@ -60,7 +60,7 @@ export const PointXray = ({
: null : null
} }
></circle> ></circle>
<text x={point.x + 3} y={point.y} className="text-sm"> <text x={point.x + 3} y={point.y} className="text-sm tw:pointer-events-none">
<tspan>{pointName}</tspan> <tspan>{pointName}</tspan>
<tspan x={point.x + 3} dy={5}> <tspan x={point.x + 3} dy={5}>
{round(point.x)},{round(point.y)} {round(point.x)},{round(point.y)}

View file

@ -2,6 +2,25 @@ import { themes as prismThemes } from 'prism-react-renderer'
import { docusaurusPlugins } from './plugins/index.mjs' import { docusaurusPlugins } from './plugins/index.mjs'
import smartypants from 'remark-smartypants' import smartypants from 'remark-smartypants'
function customizeSidebar(items) {
// Filter out submenus in Your Measurements Sets and Your Patterns
for (const item in items) {
if (items[item].label === 'Account') {
for (const design in items[item].items) {
for (const subpage in items[item].items[design].items) {
if (
items[item].items[design].items[subpage].label === 'Your Measurements Sets' ||
items[item].items[design].items[subpage].label === 'Your Patterns'
) {
items[item].items[design].items[subpage].items = []
}
}
}
}
}
return items
}
const config = { const config = {
title: 'FreeSewing Studio', title: 'FreeSewing Studio',
tagline: 'FreeSewing for Designers', tagline: 'FreeSewing for Designers',
@ -28,6 +47,10 @@ const config = {
docs: { docs: {
routeBasePath: '/', routeBasePath: '/',
sidebarPath: './sidebars.js', sidebarPath: './sidebars.js',
async sidebarItemsGenerator({ defaultSidebarItemsGenerator, ...args }) {
const sidebarItems = await defaultSidebarItemsGenerator(args)
return customizeSidebar(sidebarItems)
},
remarkPlugins: [[smartypants, { dashes: 'oldschool' }]], remarkPlugins: [[smartypants, { dashes: 'oldschool' }]],
}, },
theme: { theme: {

View file

@ -389,7 +389,7 @@ export function measurementAsMm(value, units = 'metric') {
/** convert a millimeter value to a Number value in the given units */ /** convert a millimeter value to a Number value in the given units */
export function measurementAsUnits(mmValue, units = 'metric') { export function measurementAsUnits(mmValue, units = 'metric') {
return round(mmValue / (units === 'imperial' ? 25.4 : 10), 3) return round(mmValue / (units === 'imperial' ? 25.4 : 10), 4)
} }
/* /*

View file

@ -35,7 +35,7 @@ ApikeysController.prototype.list = async (req, res, tools) => {
/* /*
* Read API key * Read API key
* *
* This is the endpoint that handles creation of API keys/tokens * This is the endpoint that handles reading of API keys/tokens
* See: https://freesewing.dev/reference/backend/api/apikey * See: https://freesewing.dev/reference/backend/api/apikey
*/ */
ApikeysController.prototype.read = async (req, res, tools) => { ApikeysController.prototype.read = async (req, res, tools) => {

View file

@ -133,7 +133,7 @@ For example, if you want to put "_Finish with bias tape_" on your pattern, don't
tempted to do this: tempted to do this:
```js ```js
path.seam.attr("data-text", "Finish with bias tape"); path.seam.attr('data-text', 'Finish with bias tape')
``` ```
That (English) string is now hard-coded in your pattern. As FreeSewing supports That (English) string is now hard-coded in your pattern. As FreeSewing supports
@ -142,16 +142,16 @@ translation out of the box, it would be a real shame not to make use of it.
Instead, insert a key to identify the string: Instead, insert a key to identify the string:
```js ```js
path.seam.attr("data-text", "finishWithBiasTape"); path.seam.attr('data-text', 'finishWithBiasTape')
``` ```
This way, different strings for different languages can be associated with This way, different strings for different languages can be associated with
the key, allowing translated text to be used. the key, allowing translated text to be used.
You can find and browse the translations and available translation keys for each design in the design's You can find and browse the translations and available translation keys for each design in the design's
[i18n folder on GitHub][1]. [i18n folder on Codeberg][1].
[1]: https://github.com/freesewing/freesewing/tree/develop/designs/aaron/i18n [1]: https://codeberg.org/freesewing/freesewing/src/branch/develop/designs/aaron/i18n
## Construct paths counter-clockwise ## Construct paths counter-clockwise
@ -189,5 +189,3 @@ Constructing a path counter-clockwise will also ensure that the path offset goes
rather than inwards. rather than inwards.
::: :::

View file

@ -12,7 +12,7 @@ instead, thereby overwriting the value of the attribute.
```js ```js
Part Part.attr( Part Part.attr(
string name, string name,
mixed value, mixed value,
bool overwrite = false bool overwrite = false
@ -31,27 +31,25 @@ This method is chainable as it returns the `Part` object
<Example caption=" Example of the Part.attr() method"> <Example caption=" Example of the Part.attr() method">
```js ```js
;({ part, points, Point, Path, paths }) => {
points.A = new Point(0, 0)
points.B = new Point(0, 40)
points.C = new Point(100, 40)
({ part, points, Point, Path, paths }) => { paths.line = new Path()
.move(points.B)
.line(points.C)
.line(points.A)
.line(points.B)
.close()
.addText('I have been flipped!', 'left')
points.A = new Point(0,0) paths.bbox = new Path().move(new Point(0, -40)).move(new Point(120, 60))
points.B = new Point(0,40)
points.C = new Point(100,40)
paths.line = new Path() part.attr('transform', 'scale(1,-1) translate(0,-40)')
.move(points.B)
.line(points.C)
.line(points.A)
.line(points.B)
.close()
.addText('I have been flipped!', 'left')
part.attr('transform', 'scale(1,-1) translate(0,-40)') return part
return part
} }
``` ```
</Example> </Example>

View file

@ -13,35 +13,36 @@ function draft(props)
The draft method receives a single parameter, an object which you can _destructure_ to The draft method receives a single parameter, an object which you can _destructure_ to
access the following properties: access the following properties:
| Property | Description | | Property | Description |
| --------:|:----------- | | ----------------: | :------------------------------------------------------------------------------------------------------------ |
|| **_Content constructors_** | | | **_Content constructors_** |
| `Path` | A [Path constructor](/reference/api/path) to create new paths | | `Path` | A [Path constructor](/reference/api/path) to create new paths |
| `Point` | A [Point constructor](/reference/api/point) to create new points | | `Point` | A [Point constructor](/reference/api/point) to create new points |
| `Snippet` | A [Snippet constructor](/reference/api/snippet) to create new snippets | | `Snippet` | A [Snippet constructor](/reference/api/snippet) to create new snippets |
|| **_Content containers_** | | | **_Content containers_** |
| `paths` | Add a Path to your part by adding it to this object | | `paths` | Add a Path to your part by adding it to this object |
| `points` | Add a Points to your part by adding it to this object | | `points` | Add a Points to your part by adding it to this object |
| `snippets` | Add a Snippet to your part by adding it to this object | | `snippets` | Add a Snippet to your part by adding it to this object |
|| **_Access to settings_** | | | **_Access to settings_** |
| `absoluteOptions` | Access to `settings.absoluteOptions` | | `absoluteOptions` | Access to `settings.absoluteOptions` |
| `complete` | Access to `settings.complete` | | `complete` | Access to `settings.complete` |
| `measurements` | Access to `settings.measurements` | | `expand` | Access to `settings.expand` |
| `options` | Access to `settings.options` | | `measurements` | Access to `settings.measurements` |
| `paperless` | Access to `settings.paperless` | | `options` | Access to `settings.options` |
| `sa` | Access to `settings.sa` | | `paperless` | Access to `settings.paperless` |
| `scale` | Access to `settings.scale` | | `sa` | Access to `settings.sa` |
|| **_Access to utilities_** | | `scale` | Access to `settings.scale` |
| `context` | Allows access to the pattern object and other things higher in the tree | | | **_Access to utilities_** |
| `getId` | See [the getId documentation](/reference/api/part/getid) | | `context` | Allows access to the pattern object and other things higher in the tree |
| `log` | See [the Store Methods documentation](/reference/store-methods#store-methods-we-maintain) | | `getId` | See [the getId documentation](/reference/api/part/getid) |
| `macro` | See [the macros documentation](/reference/macros/) | | `log` | See [the Store Methods documentation](/reference/store-methods#store-methods-we-maintain) |
| `store` | See [the store documentation](/reference/api/store) | | `macro` | See [the macros documentation](/reference/macros/) |
| `units` | A version of [`utils.units()`](/reference/api/utils/units) that is preconfigured with the user's chosen units | | `store` | See [the store documentation](/reference/api/store) |
| `utils` | See [the utils documentation](/reference/api/utils) | | `units` | A version of [`utils.units()`](/reference/api/utils/units) that is preconfigured with the user's chosen units |
| `Bezier` | The [bezier-js](https://pomax.github.io/bezierjs/) library's `Bezier` named export | | `utils` | See [the utils documentation](/reference/api/utils) |
|| **_Return value_** | | `Bezier` | The [bezier-js](https://pomax.github.io/bezierjs/) library's `Bezier` named export |
| `part` | Your draft method **must** return this | | | **_Return value_** |
| `part` | Your draft method **must** return this |
:::tip :::tip

View file

@ -40,15 +40,17 @@ snippets.point = new Snippet("notch", points.testPoint)
let angle = paths.demo.angleAt(points.testPoint) let angle = paths.demo.angleAt(points.testPoint)
//draw a tangent path //draw a tangent path
paths.tangent = new Path() paths.tangent = new Path()
.move(points.testPoint.shift(angle, -30)) .move(points.testPoint.shift(angle, -30))
.line(points.testPoint.shift(angle, 30)) .line(points.testPoint.shift(angle, 30))
.attr("class", "lining dashed") .attr("class", "lining dashed")
return part return part
} }
``` ```
</Example> </Example>
## Notes ## Notes
Keep in mind that calculations with Bézier curves are often approximations. Keep in mind that calculations with Bézier curves are often approximations.
```

View file

@ -12,33 +12,37 @@ Number path.roughLength()
## Example ## Example
<Example caption="Example of the Path.attr() method"> <Example caption="Example of the Path.roughLength() method">
```js ```js
({ Point, points, Path, paths, macro, units, part }) => { ({ Point, points, Path, paths, macro, units, part }) => {
points.B = new Point(10, 30) points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20) points.BCp2 = new Point(40, 20)
points.C = new Point(120, 30) points.C = new Point(120, 30)
points.CCp1 = new Point(50, -30) points.CCp1 = new Point(50, -30)
paths.example = new Path() paths.example = new Path()
.move(points.B) .move(points.B)
.curve(points.BCp2, points.CCp1, points.C) .curve(points.BCp2, points.CCp1, points.C)
macro("pd", { macro("pd", {
path: paths.example, path: paths.example,
d: -10, id: 'macro1',
text: `Path.roughLength() = ${units(paths.example.roughLength())}` d: -10,
}) force: true,
macro("pd", { text: `Path.roughLength() = ${units(paths.example.roughLength())}`
path: paths.example, })
d: 10, macro("pd", {
text: `Path.length() = ${units(paths.example.length())}` path: paths.example,
}) id: 'macro2',
d: 10,
force: true,
text: `Path.length() = ${units(paths.example.length())}`
})
return part
return part
} }
``` ```
</Example> </Example>
@ -48,3 +52,4 @@ The `Path.roughLength()` is not intended to give an estimate that is accurate, b
It calculates the length without *walking the (cubic) Bézier curve* making it very fast and very inaccurate (for curves). It calculates the length without *walking the (cubic) Bézier curve* making it very fast and very inaccurate (for curves).
It is typically used to determine how much precision to apply when walking a curve. It is typically used to determine how much precision to apply when walking a curve.
```

View file

@ -14,7 +14,7 @@ Path path.smurve_(Point cp2, Point end)
``` ```
:::tip :::tip
This method is chainable as it returns the `Path` object This method is chainable as it returns the `Path` objecti
::: :::
## Example ## Example

View file

@ -18,33 +18,39 @@ Path path.translate(float deltaX, float deltaY)
```js ```js
({ Point, points, Path, paths, part, macro }) => { ({ Point, points, Path, paths, part, macro }) => {
points.A = new Point(45, 60) points.A = new Point(45, 60)
points.B = new Point(10, 30) points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20) points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30) points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30) points.CCp1 = new Point(50, -30)
paths.A = new Path() paths.A = new Path()
.move(points.A) .move(points.A)
.line(points.B) .line(points.B)
.curve(points.BCp2, points.CCp1, points.C) .curve(points.BCp2, points.CCp1, points.C)
paths.B = paths.A.translate(60, 30) paths.B = paths.A.translate(60, 30)
points.step1 = points.B.shift(0, 60) points.step1 = points.B.shift(0, 60)
points.step2 = points.step1.shift(-90, 30) points.step2 = points.step1.shift(-90, 30)
macro("ld", { macro("ld", {
from: points.B, from: points.B,
to: points.step1, to: points.step1,
noStartMarker: true noStartMarker: true,
}) id: 'macro1',
macro("ld", { force: true,
from: points.step1, })
to: points.step2, macro("ld", {
noStartMarker: true from: points.step1,
}) to: points.step2,
noStartMarker: true,
id: 'macro2',
force: true,
})
return part return part
} }
``` ```
</Example> </Example>
```

View file

@ -3,7 +3,7 @@ title: Point.flipY()
--- ---
The `Point.flipY()` method returns a new `Point` that mirrors the original The `Point.flipY()` method returns a new `Point` that mirrors the original
point around the Y-value of the point you pass it. If you do not pass in a point around the Y-value of the point you pass it. If you do not pass in a
point, it will default to mirroring around an Y-value of zero. point, it will default to mirroring around an Y-value of zero.
## Signature ## Signature
@ -18,55 +18,77 @@ Point point.flipY(Point mirror = false)
```js ```js
({ Point, points, Path, paths, part }) => { ({ Point, points, Path, paths, part }) => {
points.start = new Point(0, 50) points.start = new Point(0, 50)
points.churchTowerWallLeft = new Point(10, 50) points.churchTowerWallLeft = new Point(10, 50)
points.churchTowerRoofLeft = new Point(10, 30) points.churchTowerRoofLeft = new Point(10, 30)
points.churchTowerTop = new Point(15, 10) points.churchTowerTop = new Point(15, 10)
points.churchTowerRoofRight = new Point(20, 30) points.churchTowerRoofRight = new Point(20, 30)
points.churchRoofRight = new Point(50, 30) points.churchRoofRight = new Point(50, 30)
points.churchWallRight = new Point(50, 50) points.churchWallRight = new Point(50, 50)
points.houseWallLeft = new Point(65, 50) points.houseWallLeft = new Point(65, 50)
points.houseRoofLeft = new Point(65, 35) points.houseRoofLeft = new Point(65, 35)
points.houseRoofTop = new Point(75, 25) points.houseRoofTop = new Point(75, 25)
points.houseRoofRight = new Point(85, 35) points.houseRoofRight = new Point(85, 35)
points.houseWallRight = new Point(85, 50) points.houseWallRight = new Point(85, 50)
points.end = new Point(95, 50) points.end = new Point(95, 50)
points.mirror = new Point(0, 60) points.mirror = new Point(0, 60)
points.mirrorLineEnd = new Point(95, 60) points.mirrorLineEnd = new Point(95, 60)
points._start = points.start.flipY(points.mirror) points.\_start = points.start.flipY(points.mirror)
points._churchTowerWallLeft = points.churchTowerWallLeft.flipY(points.mirror) points.\_churchTowerWallLeft = points.churchTowerWallLeft.flipY(points.mirror)
points._churchTowerRoofLeft = points.churchTowerRoofLeft.flipY(points.mirror) points.\_churchTowerRoofLeft = points.churchTowerRoofLeft.flipY(points.mirror)
points._churchTowerTop = points.churchTowerTop.flipY(points.mirror) points.\_churchTowerTop = points.churchTowerTop.flipY(points.mirror)
points._churchTowerRoofRight = points.churchTowerRoofRight.flipY( points.\_churchTowerRoofRight = points.churchTowerRoofRight.flipY(
points.mirror points.mirror
) )
points._churchRoofRight = points.churchRoofRight.flipY(points.mirror) points.\_churchRoofRight = points.churchRoofRight.flipY(points.mirror)
points._churchWallRight = points.churchWallRight.flipY(points.mirror) points.\_churchWallRight = points.churchWallRight.flipY(points.mirror)
points._houseWallLeft = points.houseWallLeft.flipY(points.mirror) points.\_houseWallLeft = points.houseWallLeft.flipY(points.mirror)
points._houseRoofLeft = points.houseRoofLeft.flipY(points.mirror) points.\_houseRoofLeft = points.houseRoofLeft.flipY(points.mirror)
points._houseRoofTop = points.houseRoofTop.flipY(points.mirror) points.\_houseRoofTop = points.houseRoofTop.flipY(points.mirror)
points._houseRoofRight = points.houseRoofRight.flipY(points.mirror) points.\_houseRoofRight = points.houseRoofRight.flipY(points.mirror)
points._houseWallRight = points.houseWallRight.flipY(points.mirror) points.\_houseWallRight = points.houseWallRight.flipY(points.mirror)
points._end = points.end.flipY(points.mirror) points.\_end = points.end.flipY(points.mirror)
paths.skylineTop = new Path() paths.skylineTop = new Path()
.move(points.start) .move(points.start)
.line(points.churchTowerWallLeft) .line(points.churchTowerWallLeft)
.line(points.churchTowerRoofLeft) .line(points.churchTowerRoofLeft)
.line(points.churchTowerTop) .line(points.churchTowerTop)
.line(points.churchTowerRoofRight) .line(points.churchTowerRoofRight)
.line(points.churchRoofRight) .line(points.churchRoofRight)
.line(points.churchWallRight) .line(points.churchWallRight)
.line(points.houseWallLeft) .line(points.houseWallLeft)
.line(points.houseRoofLeft) .line(points.houseRoofLeft)
.line(points.houseRoofTop) .line(points.houseRoofTop)
.line(points.houseRoofRight) .line(points.houseRoofRight)
.line(points.houseWallRight) .line(points.houseWallRight)
.line(points.end) .line(points.end)
return part paths.skylineBot = new Path()
.move(points.\_start)
.line(points.\_churchTowerWallLeft)
.line(points.\_churchTowerRoofLeft)
.line(points.\_churchTowerTop)
.line(points.\_churchTowerRoofRight)
.line(points.\_churchRoofRight)
.line(points.\_churchWallRight)
.line(points.\_houseWallLeft)
.line(points.\_houseRoofLeft)
.line(points.\_houseRoofTop)
.line(points.\_houseRoofRight)
.line(points.\_houseWallRight)
.line(points.\_end)
paths.mirror = new Path()
.move(points.mirror)
.line(points.mirrorLineEnd)
.setClass("note dashed")
return part
} }
``` ```
</Example> </Example>
```

View file

@ -3,7 +3,7 @@ title: Point.shift()
--- ---
The `Point.shift()` method returns a new `Point` that is `distance` (mm) away The `Point.shift()` method returns a new `Point` that is `distance` (mm) away
in the direction of `angle` (degrees). An angle of 0° points to the right, and in the direction of `angle` (degrees). An angle of 0° points to the right, and
the angle increases counterclockwise. the angle increases counterclockwise.
## Signature ## Signature
@ -18,19 +18,23 @@ Point point.shift(float angle, float distance)
```js ```js
({ Point, points, macro, part }) => { ({ Point, points, macro, part }) => {
points.A = new Point(90, 40) points.A = new Point(90, 40)
.setText("Point A", "right text-sm") .setText("Point A", "right text-sm")
points.B = points.A.shift(155, 70) points.B = points.A.shift(155, 70)
.setText("Point B is point A shifted 7 cm\nat a 155 degree angle", "text-sm") .setText("Point B is point A shifted 7 cm\nat a 155 degree angle", "text-sm")
.attr("data-text-lineheight", 6) .attr("data-text-lineheight", 6)
macro("ld", { macro("ld", {
from: points.B, from: points.B,
to: points.A, to: points.A,
d: -10 d: -10,
}) id: 'macro1',
force: true,
})
return part return part
} }
``` ```
</Example> </Example>
```

View file

@ -15,40 +15,45 @@ negative values to shift the point in the opposite direction.
Point point.shiftFractionTowards(Point target, float fraction) Point point.shiftFractionTowards(Point target, float fraction)
``` ```
## Point.shiftFractionTowards() example ## Example
<Example caption="An example of the Point.shiftFractionTowards() method"> <Example caption="An example of the Point.shiftFractionTowards() method">
```js ```js
({ Point, points, Path, paths, macro, part }) => { ({ Point, points, Path, paths, macro, part }) => {
points.A = new Point(90, 70).setText("Point A", "text-sm") points.A = new Point(90, 70).setText("Point A", "text-sm")
points.B = new Point(10, 10).setText("Point B", "text-sm") points.B = new Point(10, 10).setText("Point B", "text-sm")
points.C = points.A.shiftFractionTowards(points.B, 0.5) points.C = points.A.shiftFractionTowards(points.B, 0.5)
.setText( .setText(
"Point C is point A shifted 50%\nin the direction of point B", "Point C is point A shifted 50%\nin the direction of point B",
"center text-sm" "center text-sm"
) )
.attr("data-text-lineheight", 6) .attr("data-text-lineheight", 6)
paths.direction = new Path() paths.direction = new Path()
.move(points.A) .move(points.A)
.line(points.B) .line(points.B)
.setClass("note dashed") .setClass("note dashed")
macro("ld", { macro("ld", {
from: points.C, from: points.C,
to: points.A, to: points.A,
d: -10 d: -10,
}) id: 'macro1',
force: true,
})
macro("ld", { macro("ld", {
from: points.B, from: points.B,
to: points.A, to: points.A,
d: 20 d: 20,
}) id: 'macro2',
force: true,
})
return part return part
} }
``` ```
</Example> </Example>
@ -58,3 +63,4 @@ Point point.shiftFractionTowards(Point target, float fraction)
If you need to move a point by a specific distance instead of a percentage, use If you need to move a point by a specific distance instead of a percentage, use
[`Point.shiftTowards()`](/reference/api/point/shifttowards/) or [`Point.shiftTowards()`](/reference/api/point/shifttowards/) or
[`Point.shiftOutwards()`](/reference/api/point/shiftoutwards/) instead. [`Point.shiftOutwards()`](/reference/api/point/shiftoutwards/) instead.
```

View file

@ -17,24 +17,27 @@ Point point.shiftOutwards(Point target, float distance)
```js ```js
({ Point, points, Path, paths, macro, part }) => { ({ Point, points, Path, paths, macro, part }) => {
points.A = new Point(90, 70).setText("Point A", "text-sm right") points.A = new Point(90, 70).setText("Point A", "text-sm right")
points.B = new Point(10, 10).setText("Point B", "text-sm") points.B = new Point(10, 10).setText("Point B", "text-sm")
points.C = points.A.shiftOutwards(points.B, 30) points.C = points.A.shiftOutwards(points.B, 30)
.setText("Point C is point A shifted 3 cm\nbeyond point B", "text-sm") .setText("Point C is point A shifted 3 cm\nbeyond point B", "text-sm")
.attr("data-text-lineheight", 6) .attr("data-text-lineheight", 6)
paths.direction = new Path() paths.direction = new Path()
.move(points.A) .move(points.A)
.line(points.C) .line(points.C)
.addClass("note dashed") .addClass("note dashed")
macro("ld", { macro("ld", {
from: points.C, from: points.C,
to: points.B, to: points.B,
d: -10 d: -10,
}) force: true,
})
return part return part
} }
``` ```
</Example> </Example>
```

View file

@ -17,25 +17,32 @@ Point point.shiftTowards(Point target, float distance)
```js ```js
({ Point, points, Path, paths, macro, part }) => { ({ Point, points, Path, paths, macro, part }) => {
points.A = new Point(90, 70).setText("Point A", "right text-sm") points.A = new Point(90, 70).setText("Point A", "right text-sm")
points.B = new Point(10, 10).setText("Point B", "text-sm") points.B = new Point(10, 10).setText("Point B", "text-sm")
points.C = points.A.shiftTowards(points.B, 35) points.C = points.A.shiftTowards(points.B, 35)
.setText("Point C is point A shifted 3.5 cm\nin the direction of point B", "center, text-sm") .setText("Point C is point A shifted 3.5 cm\nin the direction of point B", "center, text-sm")
.attr("data-text-lineheight", 6) .attr("data-text-lineheight", 6)
paths.direction = new Path() paths.direction = new Path()
.move(points.A) .move(points.A)
.line(points.B) .line(points.B)
.addClass("note dashed") .addClass("note dashed")
macro("ld", { macro("ld", {
from: points.C, from: points.C,
to: points.A, to: points.A,
d: -10 d: -10,
}) force: true,
})
return part // Boundary box
paths.bbox = new Path()
.move(new Point(10,10))
.move(new Point(125,70))
return part
} }
``` ```
</Example> </Example>
@ -45,3 +52,4 @@ Point point.shiftTowards(Point target, float distance)
If you need to move a point a percentage instead of a specific distance, use If you need to move a point a percentage instead of a specific distance, use
[`Point.shiftFractionTowards()`](/reference/api/point/shiftfractiontowards/) [`Point.shiftFractionTowards()`](/reference/api/point/shiftfractiontowards/)
instead. instead.
```

View file

@ -2,14 +2,12 @@
title: Point.slope() title: Point.slope()
--- ---
The `Point.slope()` method returns the slope (dy/dx) of a line between two Points. The `Point.slope()` method returns the slope (dy/dx) of a line between two Points.
## Signature ## Signature
```js ```js
point.slope(otherPoint) point.slope(otherPoint)
``` ```
## Example ## Example
@ -17,33 +15,35 @@ point.slope(otherPoint)
<Example caption="An example of the Point.slope() method"> <Example caption="An example of the Point.slope() method">
```js ```js
;({ Point, points, Path, paths, Snippet, snippets, part, macro }) => {
points.A = new Point(0, 0)
points.B = new Point(200, 150)
({ Point, points, Path, paths, Snippet, snippets, part, macro }) => { const slope = points.A.slope(points.B)
points.A = new Point(0,0) macro('hd', {
points.B = new Point(200,150) from: points.A,
to: points.B,
y: points.B.y,
id: 'macro1',
force: true,
})
macro('vd', {
to: points.B,
from: points.A,
x: 0,
id: 'macro2',
force: true,
})
const slope = points.A.slope(points.B) paths.line = new Path()
.move(points.A)
macro('hd', { .line(points.B)
from: points.A, .attr('class', 'canvas')
to: points.B, .setText('Slope: ' + slope, 'center text-lg')
y: points.B.y,
})
macro('vd', {
to: points.B,
from: points.A,
x: 0,
})
paths.line = new Path()
.move(points.A)
.line(points.B)
.attr("class", "canvas")
.setText("Slope: " + slope, "center text-lg")
return part return part
} }
``` ```
</Example> </Example>

View file

@ -24,28 +24,36 @@ Positive values for `deltaY` will move the point downwards.
<Example caption="An example of the Point.translate() method"> <Example caption="An example of the Point.translate() method">
```js ```js
({ Point, points, Snippet, snippets, macro, part }) => { ({ Point, points, Path, paths, Snippet, snippets, macro, part }) => {
points.A = new Point(10, 10).setText("Point A", "text-sm") points.A = new Point(10, 10).setText("Point A", "text-sm")
points.B = points.A.translate(120, 60) points.B = points.A.translate(120, 60)
.setText( .setText(
"Point B is point A with a\ntranslate(120, 60)\ntransform applied", "Point B is point A with a\ntranslate(120, 60)\ntransform applied",
"right text-sm" "right text-sm"
) )
.attr("data-text-dy", -6) .attr("data-text-dy", -6)
.attr("data-text-lineheight", 6) .attr("data-text-lineheight", 6)
snippets.A = new Snippet("x", points.A) snippets.A = new Snippet("x", points.A)
snippets.B = new Snippet("x", points.B) snippets.B = new Snippet("x", points.B)
macro("ld", { macro("ld", {
from: points.A, from: points.A,
to: points.B, to: points.B,
text: "translate(120,60)", text: "translate(120,60)",
noStartMarker: true noStartMarker: true,
}) force: true,
})
return part // Bounding box
paths.bbox = new Path()
.move(new Point(10, 10))
.move(new Point(130, 80))
return part
} }
``` ```
</Example> </Example>
```

View file

@ -17,7 +17,7 @@ This method is chainable as it returns the `Snippet` object
## Example ## Example
<Example caption="An example of the Snippet.clone() method"> <Example caption="An example of the Snippet.scale() method">
```js ```js
({ Point, Path, paths, Snippet, snippets, part }) => { ({ Point, Path, paths, Snippet, snippets, part }) => {

View file

@ -9,8 +9,22 @@ SVG document.
## Signature ## Signature
```svg ```js
Defs svg.defs = {
list = {
buttonhole: `<g id="buttonhole" transform="scale(1)">
<path class="mark" d="M -1,-5 L 1,-5 L 1,5 L -1,5 z"></path>
</g>`,
},
}
```
Result:
```
<defs> <defs>
/* svg.defs will be inserted */ <g id="buttonhole" transform="scale(1)">
<path class="mark" d="M -1,-5 L 1,-5 L 1,5 L -1,5 z"></path>
</g>
</defs> </defs>
``` ```

View file

@ -7,9 +7,17 @@ section of the SVG document.
## Signature ## Signature
```svg ```js
String svg.style += 'circle {stroke:black;}'
```
Result:
```
<style type="text/css"> <style type="text/css">
/* svg.style will be inserted */ circle {
stroke: black;
}
</style> </style>
``` ```

View file

@ -4,6 +4,15 @@ title: hem
The `hem` macro drafts a hem allowance with fold lines. The `hem` macro drafts a hem allowance with fold lines.
:::note
##### Not a core-plugins macro
The `hem` macro is not provided by the [core plugins](/reference/plugins/core),
so you need to load the [path-utils plugin](/reference/plugins/path-utils)
explicitly if you want to use it.
:::
## Signature ## Signature
```js ```js
@ -68,7 +77,7 @@ Path macro('hem', {
| `prefix` | `'hemMacro'` | `number` | The name prefix used for paths created by this macro. | | `prefix` | `'hemMacro'` | `number` | The name prefix used for paths created by this macro. |
| `cssClass` | `'fabric'` | `number` | The CSS class added to the fold lines and the hem outline. Should usually match the CSS class for the part outline | | `cssClass` | `'fabric'` | `number` | The CSS class added to the fold lines and the hem outline. Should usually match the CSS class for the part outline |
## Detailed Description ## Notes
This macro will create the following paths (assuming the default `'hemMacro'` prefix is used): This macro will create the following paths (assuming the default `'hemMacro'` prefix is used):

View file

@ -4,31 +4,20 @@ title: join
The `join` macro joins multiple paths together. The `join` macro joins multiple paths together.
Unlike the core `path.join(...)` function, the `join` macro can extend line ends to meet in a sharp corner and will automatically Unlike the core [`Path.join()`](/reference/api/path/join) function,
trim useless path ends when adjacent paths in the array are intersecting. the `join` macro can extend line ends to meet in a sharp corner and
will automatically trim useless path ends when adjacent paths in the
array are intersecting.
The join function accepts an array of `Path` objects :::note
(either names in the `paths` array or direct references to `Path` objects).
This array can contain `null` values or hidden paths (created with `path.hide()`) to create gaps.
:::warning ##### Not a core-plugins macro
Since hidden paths will create gaps in the resulting paths, make sure that all the paths you want to
include in the join are visible before calling the macro.
You can hide them afterwards again, if needed.
The `join` macro is not provided by the [core plugins](/reference/plugins/core),
so you need to load the [path-utils plugin](/reference/plugins/path-utils)
explicitly if you want to use it.
::: :::
By default, the `join` macro will join the paths in a circular fashion, joining the end
of the last path in the array to the start of the first path, creating a full outline.
If this is not desired, insert a `null` element between paths where you want the gap
(or at the end of the `paths` parameter).
Note that a `null` value will create a basic gap in the output,
if you instead include a hidden path, this method will still create sharp corners as if the path were present,
but the actual path will be skipped in the output.
## Signature ## Signature
```js ```js
@ -72,5 +61,29 @@ Path macro('join', {
| Property | Default | Type | Description | | Property | Default | Type | Description |
| -------: | ---------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | -------: | ---------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `paths` | | `array` | An array of pathnames, the names of Paths in the `paths` array to join (you can also reference `Path` objects directly, or insert `null` elements to create gaps) | | `paths` | | `array` | An array of pathnames, the names of Paths in the `paths` array to join (you can also reference `Path` objects directly, or insert `null` elements to create gaps) |
| `mode` | `'corner'` | `string` | Mode for joining paths. Either `'corner'` or `'cut'`. `'cut'` will join the paths directly without extending them (like `path.join(...)`). | | `mode` | `'corner'` | `string` | Mode for joining paths. Either `'corner'` or `'cut'`. `'cut'` will join the paths directly without extending them (like `Path.join()`). |
| `limit` | `null` | `number` | Allows limiting the length of corners in `'corner'` mode. Prevents overly long joins on very sharp angles. Ignored if `null` or `false`. | | `limit` | `null` | `number` | Allows limiting the length of corners in `'corner'` mode. Prevents overly long joins on very sharp angles. Ignored if `null` or `false`. |
## Notes
The `join` macro accepts an array of `Path` objects
(either names in the `paths` array or direct references to `Path` objects).
This array can contain `null` values or hidden paths (created with `Path.hide()`) to create gaps.
:::warning
Since hidden paths will create gaps in the resulting paths, make sure that all the paths you want to
include in the join are visible before calling the macro.
You can hide them afterwards again, if needed.
:::
By default, the `join` macro will join the paths in a circular fashion, joining the end
of the last path in the array to the start of the first path, creating a full outline.
If this is not desired, insert a `null` element between paths where you want the gap
(or at the end of the `paths` parameter).
Note that a `null` value will create a basic gap in the output.
If you instead include a hidden path, this method will still create sharp corners as if the path were present,
but the actual path will be skipped in the output.

View file

@ -4,25 +4,18 @@ title: offset
The `offset` macro will offset and join paths. The `offset` macro will offset and join paths.
Unlike the core `path.join(...)` and `path.offset(...)` functions, the `offset` macro can extend line ends to meet in a sharp corner and will automatically Unlike the core [`Path.join()`](/reference/api/path/join) and
[`Path.offset()`](/reference/api/path/offset) functions, the `offset`
macro can extend line ends to meet in a sharp corner and will automatically
trim useless path ends when adjacent paths in the array are intersecting. trim useless path ends when adjacent paths in the array are intersecting.
The offset macro accepts an array of `Path` objects with their offset, like `{p: 'somePath', offset: 30}` or `{p: ['path1', 'path2'], offset: sa * 3, hidden: true}`.
For the paths in the `p` attribute, you can reference either names in the `paths` array or insert direct references to `Path` objects.
The array can contain `null` values to create gaps (e.g., for cuts on the fold or for other sections that don't need seam allowance).
By default, the `offset` macro will offset the paths in a circular fashion, joining the end
of the last path in the array to the start of the first path, creating a full outline.
If this is not desired, insert `null` elements where you want to create gaps.
You can use `offset: 0` to include paths which don't need an additional offset.
You can use `hidden: true` to omit path segments from the output, but still build corner joins as if they were there.
This can be used for cut-on-fold lines, where no seam allowance is needed.
:::note :::note
To create a seam allowance, prefer the `sa` macro instead of the `offset` macro. It does pretty much the same,
but the `sa` macro defaults to offsetting paths by the user defined seam allowance. ##### Not a core-plugins macro
The `offset` macro is not provided by the [core plugins](/reference/plugins/core),
so you need to load the [path-utils plugin](/reference/plugins/path-utils)
explicitly if you want to use it.
::: :::
## Signature ## Signature
@ -79,6 +72,27 @@ Path macro('offset', {
| Property | Default | Type | Description | | Property | Default | Type | Description |
| -------: | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | -------: | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `paths` | | `array` | An array of paths with their offset and visibility. You can use path names in the `paths` array or reference `Path` objects directly, or insert `null` elements to create gaps. | | `paths` | | `array` | An array of paths with their offset and visibility. You can use path names in the `paths` array or reference `Path` objects directly, or insert `null` elements to create gaps. |
| `mode` | `'corner'` | `string` | Mode for joining paths. Either `'corner'` or `'cut'`. `'cut'` will join the paths directly (like `path.join(...)`) without extending the corners. | | `mode` | `'corner'` | `string` | Mode for joining paths. Either `'corner'` or `'cut'`. `'cut'` will join the paths directly (like `Path.join()`) without extending the corners. |
| `limit` | `null` | `number` | Allows limiting the length of extended path corners in `'corner'` mode. Prevents overly long joins on very sharp angles. Ignored if `null` or `false`. | | `limit` | `null` | `number` | Allows limiting the length of extended path corners in `'corner'` mode. Prevents overly long joins on very sharp angles. Ignored if `null` or `false`. |
| `class` | `'offset'` | `string` | CSS class that is automatically applied to the resulting path. | | `class` | `'offset'` | `string` | CSS class that is automatically applied to the resulting path. |
## Notes
The `offset` macro accepts an array of `Path` objects with their offset, like `{p: 'somePath', offset: 30}` or `{p: ['path1', 'path2'], offset: sa * 3, hidden: true}`.
For the paths in the `p` attribute, you can reference either names in the `paths` array or insert direct references to `Path` objects.
The array can contain `null` values to create gaps (e.g., for cuts on the fold or for other sections that don't need seam allowance).
By default, the `offset` macro will offset the paths in a circular fashion, joining the end
of the last path in the array to the start of the first path, creating a full outline.
If this is not desired, insert `null` elements where you want to create gaps.
You can use `offset: 0` to include paths which don't need an additional offset.
You can use `hidden: true` to omit path segments from the output, but still build corner joins as if they were there.
This can be used for cut-on-fold lines, where no seam allowance is needed.
:::note
To create a seam allowance, prefer the [`sa`](/reference/macros/sa) macro
instead of the `offset` macro. It does pretty much the same,
but the `sa` macro defaults to offsetting paths by the user defined seam allowance.
:::

View file

@ -4,24 +4,20 @@ title: sa
The `sa` macro will create seam allowance paths. The `sa` macro will create seam allowance paths.
Unlike the core `path.join(...)` and `path.offset(...)` functions, the `sa` macro can extend line ends to meet in a sharp corner and will automatically Unlike the core [`Path.join()`](/reference/api/path/join) and
trim useless path ends when adjacent paths in the array are intersecting. [`Path.offset()`](/reference/api/path/offset) functions, the
`sa` macro can extend line ends to meet in a sharp corner and
will automatically trim useless path ends when adjacent paths
in the array are intersecting.
The sa macro accepts an array of `Path` objects :::note
(either names in the `paths` array or direct references to `Path` objects).
This array can contain `null` values to create gaps (e.g. for cuts on the fold or for other sections that don't need seam allowance).
By default, the `sa` macro will sa the paths in a circular fashion, joining the end ##### Not a core-plugins macro
of the last path in the array to the start of the first path, creating a full outline.
If this is not desired, insert `null` elements where you want to create gaps.
You can optionally override the offset and invisibility for individual paths. The `sa` macro is not provided by the [core plugins](/reference/plugins/core),
To do so, insert an object literal like `{p: 'somePath', offset: 30}` or `{p: ['path1', 'path2'], offset: sa * 3, hidden: true}` into the `paths` array. so you need to load the [path-utils plugin](/reference/plugins/path-utils)
explicitly if you want to use it.
You can use `offset: 0` to include paths which have already build-in the seam allowance (e.g. the result of the `hem` macro). :::
You can use `hidden: true` to hide path segments from the output, but still build corner joins as if they were there.
This can be used for cut-on-fold lines, where no seam allowance is needed.
## Signature ## Signature
@ -76,7 +72,25 @@ macro('sa', {
| Property | Default | Type | Description | | Property | Default | Type | Description |
| -------: | ---------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | -------: | ---------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `paths` | | `array` | An array of paths. You can use path names in the `paths` array or reference `Path` objects directly, or insert `null` elements to create gaps. You can also override offset and visibility for individual paths in the same way as with the `offset` macro. | | `paths` | | `array` | An array of paths. You can use path names in the `paths` array or reference `Path` objects directly, or insert `null` elements to create gaps. You can also override offset and visibility for individual paths in the same way as with the `offset` macro. |
| `mode` | `'corner'` | `string` | Mode for joining paths. Either `'corner'` or `'cut'`. `'cut'` will join the paths directly (like `path.join(...)`) without extending the corners. | | `mode` | `'corner'` | `string` | Mode for joining paths. Either `'corner'` or `'cut'`. `'cut'` will join the paths directly (like `Path.join()`) without extending the corners. |
| `limit` | `null` | `number` | Allows limiting the length of extended path corners in `'corner'` mode. Prevents overly long joins on very sharp angles. Ignored if `null` or `false`. | | `limit` | `null` | `number` | Allows limiting the length of extended path corners in `'corner'` mode. Prevents overly long joins on very sharp angles. Ignored if `null` or `false`. |
| `sa` | (seam allowance) | `number` | Allows you to override the seam allowance used for this macro. Defaults to the standard seam allowance (`sa` parameter in draft function). | | `sa` | (seam allowance) | `number` | Allows you to override the seam allowance used for this macro. Defaults to the standard seam allowance (`sa` parameter in draft function). |
| `class` | `'sa'` | `string` | CSS class that is automatically applied to the resulting path. | | `class` | `'sa'` | `string` | CSS class that is automatically applied to the resulting path. |
## Notes
The `sa` macro accepts an array of `Path` objects
(either names in the `paths` array or direct references to `Path` objects).
This array can contain `null` values to create gaps (e.g. for cuts on the fold or for other sections that don't need seam allowance).
By default, the `sa` macro will sa the paths in a circular fashion, joining the end
of the last path in the array to the start of the first path, creating a full outline.
If this is not desired, insert `null` elements where you want to create gaps.
You can optionally override the offset and invisibility for individual paths.
To do so, insert an object literal like `{p: 'somePath', offset: 30}` or `{p: ['path1', 'path2'], offset: sa * 3, hidden: true}` into the `paths` array.
You can use `offset: 0` to include paths which have already build-in the seam allowance (e.g. the result of the `hem` macro).
You can use `hidden: true` to hide path segments from the output, but still build corner joins as if they were there.
This can be used for cut-on-fold lines, where no seam allowance is needed.

View file

@ -2,7 +2,9 @@
title: plugin-path-utils title: plugin-path-utils
--- ---
Published as [@freesewing/plugin-path-utils][1], this plugin provides the [hem](/reference/macros/hem), [sa](/reference/macros/sa), [offset](/reference/macros/offset) and [sa](/reference/macros/join) macros, Published as [@freesewing/plugin-path-utils][1], this plugin provides the
[hem](/reference/macros/hem), [join](/reference/macros/join),
[offset](/reference/macros/offset), and [sa](/reference/macros/sa) macros,
whose main purpose is to make it easier to construct seam allowance paths. whose main purpose is to make it easier to construct seam allowance paths.
## Installation ## Installation
@ -25,10 +27,4 @@ import { pathUtilsPlugin } from '@freesewing/plugin-path-utils'
import { pluginPathUtils } from '@freesewing/plugin-path-utils' import { pluginPathUtils } from '@freesewing/plugin-path-utils'
``` ```
## Notes
This plugin is part of the [core-plugins bundle](/reference/plugins/core),
so there is no need to install or import it manually unless you wish to forego
loading of core plugins yet still want to load this plugin.
[1]: https://www.npmjs.com/package/@freesewing/plugin-path-utils [1]: https://www.npmjs.com/package/@freesewing/plugin-path-utils

View file

@ -0,0 +1,44 @@
---
title: expand
---
The `expand` setting controls whether all parts should be fully
drawn in a pattern.
Set `expand` to `false` when the pattern should instead omit parts and/or
draw abbreviated parts.
Omitting parts and using abbreviated parts saves space and paper
in printed patterns.
Typically, this is done for parts that are simple shapes like
rectangles or that can be cut on the fold.
## Signature
```js
const settings = {
Boolean expand=true
}
```
The default `expand` setting is `true`.
Set this to `false` to draft a pattern with omitted or abbreviated parts,
rather than with fully-drawn parts.
## Example
```js
import { Aaron } from '@freesewing/aaron'
const pattern = new Aaron({
expand: false,
})
```
## Notes
The `expand` setting does not automatically cause pattern parts to
be omitted or abbreviated.
Instead, it is up to the pattern designer to have the design
check for the `expand` setting,
include all, full parts if set to `true`,
and omit or abbreviate relevant parts if set to `false`.

View file

@ -3,7 +3,7 @@ title: Settings
--- ---
FreeSewing is all about parametric design, and the settings are the parameters FreeSewing is all about parametric design, and the settings are the parameters
we pass to a pattern when drafting it. Perhaps the most important of all we pass to a pattern when drafting it. Perhaps the most important of all
settings are the measurements, but there are other settings too. settings are the measurements, but there are other settings too.
## Signature ## Signature
@ -13,6 +13,7 @@ Object settings = {
Object absoluteOptions, Object absoluteOptions,
Boolean complete=true, Boolean complete=true,
Boolean embed=false, Boolean embed=false,
Boolean expand=true,
String idPrefix='fs-', String idPrefix='fs-',
Object|Boolean layout=true, Object|Boolean layout=true,
String locale='en', String locale='en',
@ -43,12 +44,11 @@ objects in an array to the pattern constructor:
```js ```js
new pattern([ new pattern([
{ {
// settings // settings
}, },
{ {
// different settings // different settings
}, },
]) ])
``` ```

View file

@ -11,7 +11,6 @@ However, in our own UI on FreeSewing.org, we use this mechanism to allow
designer to flag information to the user, and even suggest changes to the designer to flag information to the user, and even suggest changes to the
pattern configuration. pattern configuration.
## Signature ## Signature
```js ```js
@ -23,4 +22,8 @@ undefined Store.flag.error({
Since these methods are not part of FreeSewing's core API, what you pass to this method does depend on your own implementation. Since these methods are not part of FreeSewing's core API, what you pass to this method does depend on your own implementation.
For a more detailed example of how we use this, see [flag.info()](/reference/store-methods/flag.info). :::note RELATED
The above Signature contains abbreviated information.
For full details about this method's Signature, Configuration, and
Example usage, see [flag.info()](/reference/store-methods/flag.info).
:::

View file

@ -11,7 +11,6 @@ However, in our own UI on FreeSewing.org, we use this mechanism to allow
designer to flag information to the user, and even suggest changes to the designer to flag information to the user, and even suggest changes to the
pattern configuration. pattern configuration.
## Signature ## Signature
```js ```js
@ -23,4 +22,8 @@ undefined Store.flag.fixme({
Since these methods are not part of FreeSewing's core API, what you pass to this method does depend on your own implementation. Since these methods are not part of FreeSewing's core API, what you pass to this method does depend on your own implementation.
For a more detailed example of how we use this, see [flag.info()](/reference/store-methods/flag.info). :::note RELATED
The above Signature contains abbreviated information.
For full details about this method's Signature, Configuration, and
Example usage, see [flag.info()](/reference/store-methods/flag.info).
:::

View file

@ -11,17 +11,28 @@ However, in our own UI on FreeSewing.org, we use this mechanism to allow
designer to flag information to the user, and even suggest changes to the designer to flag information to the user, and even suggest changes to the
pattern configuration. pattern configuration.
:::tip
The Signature, Configuration, and Example information below applies to the
`flag.error()`, `flag.fixme()`, `flag.info()`, `flag.note()`,
`flag.tip()`, and `flag.warn()` methods.
:::
## Signature ## Signature
```js ```js
undefined Store.flag.info({ undefined Store.flag.info({
id: 'id_string',
title: 'flag:expandIsOn.t', title: 'flag:expandIsOn.t',
desc: 'flag:expandIsOn.d', desc: 'flag:expandIsOn.d',
msg: 'flag:expandIsOn',
notes: [ notes: [
'sorcha:moreInfo1', 'sorcha:moreInfo1',
'sorcha:moreInfo2', 'sorcha:moreInfo2',
], ],
replace: {
key1: //code for replacement value,
key2: //code for replacement value,
},
suggest: { suggest: {
text: 'flag:disable', text: 'flag:disable',
icon: 'expand', icon: 'expand',
@ -37,29 +48,41 @@ The example above is from our implementation, which uses the following propertie
## Configuration ## Configuration
| Property | Type | Description | | Property | Type | Description |
| ----------:| ------------------- | ----------- | | ------------------------: | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | String | An ID for this flag message. If none is provided, `title` will be used | | `id` | String | An ID for this flag message. If none is provided, `title` will be used |
| `title` | String | The title of the message | | `title` | String | The translation key for the title of the message |
| `desc` | String | The description of the message | | `desc` | String | The translation key for the description of the message |
| `notes` | String or Array of Strings | More information/notes (see [Notes](#notes))| | `msg` | String | The translation key for the message |
| `suggest.text` | String | Text to go on the button to implement the suggested configuration change | | `notes` | String or Array of Strings | Translation keys for more information/notes (see [Notes](#notes)) |
| `suggest.icon` | String | Icon name to go on the button to implement the suggested configuration change. (see [suggest.icon](#suggesticon)) | | `replace` | Object | Key/values for text replacements (see [Replacement Values](#replacement-values)) |
| `suggest.update.settings` | Array | An array describing the changes to apply to the `settings` if the user accepts the suggestion. (see [suggest.update](#suggestupdate)) | | `suggest.text` | String | Text to go on the button to implement the suggested configuration change |
| `suggest.update.ui` | Array | An array describing the changes to apply to the `ui` if the user accepts the suggestion. (see [suggest.update](#suggestupdate)) | | `suggest.icon` | String | Icon name to go on the button to implement the suggested configuration change. (see [suggest.icon](#suggesticon)) |
| `suggest.update.settings` | Array | An array describing the changes to apply to the `settings` if the user accepts the suggestion. (see [suggest.update](#suggestupdate)) |
| `suggest.update.ui` | Array | An array describing the changes to apply to the `ui` if the user accepts the suggestion. (see [suggest.update](#suggestupdate)) |
### Notes ### Notes
Notes are optional, but allow you to add more text/content to the flag message. Notes are optional, but allow you to add more text/content to the flag message.
Unlike `desc` which can only hold a string, `notes` can hold either a string or an array of strings. Unlike `title` or `desc` which can only hold a string, `notes` can hold either a string or an array of strings.
Both `desc` and `notes` will be rendered as markdown. `notes` are also translation keys, and the translation strings will be
rendered as markdown.
### Replacement Values
The translation strings for `title`, `desc`, and `notes` can contain
variables that allow calculated values to be inserted into messages.
The optional `replace` object holds key/value pair properties where
keys are variable names and values contain code that generates
the replacement text for that variable.
### suggest.icon ### suggest.icon
An optional name of an icon. Or leave it out to not render and icon. An optional name of an icon. Or leave it out to not render an icon.
The idea is that the icon helps convey the message, the following icon names are supported: The idea is that the icon helps convey the message, with the following icon names supported:
- `note` - `note`
- `info` - `info`
@ -77,15 +100,15 @@ Any other name will be ignored.
Note that the `suggest` object is optional. Without it, it will merely display a message to the user. Note that the `suggest` object is optional. Without it, it will merely display a message to the user.
However, when a suggest key is present, a button will be created that the user can click to accept the suggested changes. However, when a suggest key is present, a button will be created that the user can click to accept the suggested changes.
The `suggest.update` object has only two possible top-level keys: The `suggest.update` object has only two possible top-level keys:
- `settings` - `settings`
- `ui` - `ui`
They both take the same parameter, an array with two elements: They both take the same parameter, an array with two elements:
```mjs ```mjs
Array [`path`, `value`] Array[(`path`, `value`)]
``` ```
This will be used to update the `settings` of the pattern, or the `ui` settings on FreeSewing.org. This will be used to update the `settings` of the pattern, or the `ui` settings on FreeSewing.org.
@ -109,7 +132,7 @@ So to set the `waistEase` option to `0.2`, it should look like this:
## Example ## Example
```js ```js
({ store, part }) => { ;({ store, part }) => {
store.flag.info({ store.flag.info({
msg: `aaron:cutNeckBinding`, msg: `aaron:cutNeckBinding`,
notes: ['flag:saUnused', 'flag:partHiddenByExpand'], notes: ['flag:saUnused', 'flag:partHiddenByExpand'],
@ -129,4 +152,3 @@ So to set the `waistEase` option to `0.2`, it should look like this:
return part return part
} }
``` ```

View file

@ -11,7 +11,6 @@ However, in our own UI on FreeSewing.org, we use this mechanism to allow
designer to flag information to the user, and even suggest changes to the designer to flag information to the user, and even suggest changes to the
pattern configuration. pattern configuration.
## Signature ## Signature
```js ```js
@ -23,4 +22,8 @@ undefined Store.flag.note({
Since these methods are not part of FreeSewing's core API, what you pass to this method does depend on your own implementation. Since these methods are not part of FreeSewing's core API, what you pass to this method does depend on your own implementation.
For a more detailed example of how we use this, see [flag.info()](/reference/store-methods/flag.info). :::note RELATED
The above Signature contains abbreviated information.
For full details about this method's Signature, Configuration, and
Example usage, see [flag.info()](/reference/store-methods/flag.info).
:::

View file

@ -22,20 +22,16 @@ The example above is from our implementation, which uses the following propertie
## Configuration ## Configuration
| Property | Type | Description | | Property | Type | Description |
| ----------:| ------------------- | ----------- | | -------: | ------ | ---------------------------- |
| `preset` | String | The ID of an existing preset | | `preset` | String | The ID of an existing preset |
## Example ## Example
```js ```js
({ store, expand, part }) => { ;({ store, expand, part }) => {
store.flag.preset(expand store.flag.preset(expand ? 'expandIsOn' : 'expandIsOff')
? 'expandIsOn'
: 'expandIsOff'
)
return part return part
} }
``` ```

View file

@ -11,7 +11,6 @@ However, in our own UI on FreeSewing.org, we use this mechanism to allow
designer to flag information to the user, and even suggest changes to the designer to flag information to the user, and even suggest changes to the
pattern configuration. pattern configuration.
## Signature ## Signature
```js ```js
@ -23,4 +22,8 @@ undefined Store.flag.tip({
Since these methods are not part of FreeSewing's core API, what you pass to this method does depend on your own implementation. Since these methods are not part of FreeSewing's core API, what you pass to this method does depend on your own implementation.
For a more detailed example of how we use this, see [flag.info()](/reference/store-methods/flag.info). :::note RELATED
The above Signature contains abbreviated information.
For full details about this method's Signature, Configuration, and
Example usage, see [flag.info()](/reference/store-methods/flag.info).
:::

View file

@ -11,7 +11,6 @@ However, in our own UI on FreeSewing.org, we use this mechanism to allow
designer to flag information to the user, and even suggest changes to the designer to flag information to the user, and even suggest changes to the
pattern configuration. pattern configuration.
## Signature ## Signature
```js ```js
@ -23,4 +22,8 @@ undefined Store.flag.warn({
Since these methods are not part of FreeSewing's core API, what you pass to this method does depend on your own implementation. Since these methods are not part of FreeSewing's core API, what you pass to this method does depend on your own implementation.
For a more detailed example of how we use this, see [flag.info()](/reference/store-methods/flag.info). :::note RELATED
The above Signature contains abbreviated information.
For full details about this method's Signature, Configuration, and
Example usage, see [flag.info()](/reference/store-methods/flag.info).
:::

View file

@ -16,9 +16,6 @@ via a web browser.
The files and computer programs are hosted on and run from a remote The files and computer programs are hosted on and run from a remote
server run by GitHub. server run by GitHub.
[gh]: https://github.com
[ghcs]: https://github.com/features/codespaces
For FreeSewing, you can use Codespaces to edit our repository files to For FreeSewing, you can use Codespaces to edit our repository files to
modify existing designs, add new files to create new designs, and run modify existing designs, add new files to create new designs, and run
the FreeSewing lab website so you can test designs. the FreeSewing lab website so you can test designs.
@ -55,7 +52,7 @@ The Codespaces app will open in the browser window.
The Codespaces app is basically the [Visual Studio Code][vs] app with The Codespaces app is basically the [Visual Studio Code][vs] app with
Codespaces and GitHub integration built in. 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 'Wait 45 seconds or so for the Codespace app to clone the repository from
GitHub to the codespace repository and start.' GitHub to the codespace repository and start.'
## Editing files ## Editing files
@ -234,8 +231,14 @@ To delete a codespace:
and select "Delete". and select "Delete".
:::note RELATED :::note RELATED
For more information, please see: For more information, please see:
- [GitHub Codebases documentation](https://docs.github.com/en/codespaces). - [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) - [About billing for GitHub Codespaces](https://docs.github.com/en/billing/managing-billing-for-github-codespaces/about-billing-for-github-codespaces)
:::
:::
[gh]: https://github.com
[ghcs]: https://github.com/features/codespaces
[vs]: https://code.visualstudio.com

View file

@ -3,17 +3,16 @@ title: Setting up the FreeSewing development environment
sidebar_position: 40 sidebar_position: 40
--- ---
FreeSewing provides a development environment to help you design and develop FreeSewing provides the studio, a development environment to help you design and develop patterns.
patterns.
There are two ways to run this development environment: There are two ways to run this studio:
- [**Monorepo development**](#monorepo-development): Use this if you intend to - [**Repository studio**](#monorepo-studio): Use this if you intend to
contribute your work to FreeSewing contribute your work to FreeSewing
- [**Stand-alone development**](#stand-alone-development): Use this if you want - [**Stand-alone studio**](#stand-alone-studio): Use this if you want
to do your own thing, and not contribute to FreeSewing to do your own thing, and not contribute to FreeSewing
## Monorepo development ## Repository studio
:::note :::note
This is the recommended way for (aspiring) FreeSewing contributors This is the recommended way for (aspiring) FreeSewing contributors
@ -22,34 +21,28 @@ This is the recommended way for (aspiring) FreeSewing contributors
### TL;DR ### TL;DR
```bash ```bash
git clone https://github.com/freesewing/freesewing git clone https://codeberg.org/freesewing/freesewing.git
cd freesewing cd freesewing
yarn kickstart npm run kickstart
``` ```
:::tip :::tip
Even better: [clone your own Even better: [clone your own
fork](https://github.com/freesewing/freesewing/fork) fork](https://codeberg.org/freesewing/freesewing/fork)
```bash ```bash
git clone https://github.com/your-username/freesewing git clone https://codeberg.org/your-username/freesewing.git
cd freesewing cd freesewing
yarn kickstart npm run kickstart
``` ```
::: :::
This sets up the monorepo. If you would like to create a new design, run the This sets up the monorepo. If you would like to create a new design or plugin, run the
following command: following command:
```sh ```sh
yarn new design npm run add
```
If you'd like to create a new plugin, run this variant instead:
```sh
yarn new plugin
``` ```
### Step by step ### Step by step
@ -59,20 +52,10 @@ These docs assume you have git installed.
But if you're running Linux, you have git, right? But if you're running Linux, you have git, right?
::: :::
#### Install yarn
Our repository uses yarn workspaces. So you'll need `yarn` to work with it.
To install it run:
```bash
npm install yarn --global
```
#### Fork our repository #### Fork our repository
You'll want to fork our repository. This way you have your own copy where you can make You'll want to fork our repository. This way you have your own copy where you can make
all the changes you want. To do so, visit https://github.com/freesewing/freesewing/fork all the changes you want. To do so, visit https://codeberg.org/freesewing/freesewing/fork
#### Clone the forked repository #### Clone the forked repository
@ -82,96 +65,76 @@ Now that you have your very own fork, it's time to clone it locally.
git clone <url to your fork> git clone <url to your fork>
``` ```
Make sure to use the URL to your own fork, typically `https://github.com/your-username/freesewing` but Make sure to use the URL to your own fork, typically `https://codeberg.org/your-username/freesewing.git` but
obviously with your real username rather than `your-username`. obviously with your real username rather than `your-username`.
#### Install dependencies #### Install dependencies
Enter the directory that was created, and run the `yarn kickstart` command: Enter the directory that was created, and run the `npm run kickstart` command:
```bash ```bash
cd freesewing cd freesewing
yarn kickstart npm run kickstart
``` ```
Now you're ready to [start the development environment](/tutorials/getting-started-linux/dev-start). Now you're ready to [start the development environment](/tutorials/getting-started-linux/dev-start).
:::note ### Creating a new design or plugin
There is another `yarn` command that comes with some Linux distributions, If you would like to create a new design or plugin, run the following command:
installed as part of the `cmdtest` package and used for command line
scenario testing.
If you get an `ERROR: There are no scenarios; must have at least one.`
message when trying to run the `yarn` command, it may be because the wrong
`yarn` is being used.
Possible workarounds for this include uninstalling the `cmdtest` package
or making sure that npm `yarn` is installed and comes first in your `PATH`
environment variable.
:::
## Creating a new design
If you would like to create a new design, run the following command:
```sh ```sh
yarn new design npm run add
``` ```
## Creating a new plugin After you've answered [some questions](#questions), it create a new
sub-folder for your design in the `designs` folder.
If you'd like to create a new plugin, run the following command: Now you're ready to [start the development
environment](/tutorials/getting-started-linux/dev-start).
```sh
yarn new plugin
```
## Stand-alone development ## Stand-alone development
With Node.js installed, all you need to do to setup the stand-alone development environment is run this command: With Node.js installed, all you need to do to setup the stand-alone development environment is run this command:
```bash ```bash
npx @freesewing/new-design npx @freesewing/studio
``` ```
After you've answered [some questions](#questions), it will take a while to set After you enter the folder name to create, it will take a while to set
everything up. When it's done, you will have a new folder with the development everything up. When it's done, you will have a new folder with the development
environment inside. environment inside.
### Creating a new design
If you would like to create a new design, enter the folder that was just created and run the following command:
```sh
npm run add
```
After you've answered [some questions](#questions), it create a new
sub-folder for your design in the `designs` folder.
Now you're ready to [start the development Now you're ready to [start the development
environment](/tutorials/getting-started-linux/dev-start). environment](/tutorials/getting-started-linux/dev-start).
:::tip
The folder will have the name you chose above.
:::
:::note :::note
### Questions ### Questions
#### What design name to use
Please stick to a single word name using \[a-z] to avoid problems.
#### What template to use #### What template to use
Use `From scratch` unless you want to start from our of our blocks: Use `From scratch` unless you want to start from our of our blocks:
- Use `Extend Brian` to start from [Brian](https://freesewing.org/designs/brian) - Use `Extend Brian` to start from [Brian](https://freesewing.eu/designs/brian)
- Use `Extend Bent` to start from [Bent](https://freesewing.org/designs/bent) - Use `Extend Bent` to start from [Bent](https://freesewing.eu/designs/bent)
- Use `Extend Bella` to start from [Bella](https://freesewing.org/designs/bella) - Use `Extend Bella` to start from [Bella](https://freesewing.eu/designs/bella)
- Use `Extend Breanna` to start from [Breanna](https://freesewing.org/designs/breanna) - Use `Extend Breanna` to start from [Breanna](https://freesewing.eu/designs/breanna)
- Use `Extend Titan` to start from [Titan](https://freesewing.org/designs/titan) - Use `Extend Titan` to start from [Titan](https://freesewing.eu/designs/titan)
#### What name to use
This will become the name of your design. Stick to \[a-z] here to avoid problems.
If you're not certain what to pick, just mash some keys, it doesn't matter.
#### What package manager to use
You may wish to choose `yarn` since that is the package manager
that we use when doing work in the monorepo,
and many of our tutorials are written to use `yarn`.
However, it doesn't really matter.
You can choose either `yarn` or `npm` as you wish.
::: :::

View file

@ -3,55 +3,70 @@ title: Start the development environment
sidebar_position: 50 sidebar_position: 50
--- ---
FreeSewing provides a development environment to help you design and develop patterns. FreeSewing provides the studio, a development environment to help you design and develop patterns.
There are two ways to run this development environment: There are two ways to run this studio:
- [**Monorepo development**](#monorepo-development): Use this if you intend to contribute your work to FreeSewing - [**Repository studio**](#repository-studio): Use this if you intend to contribute your work to FreeSewing
- [**Stand-alone development**](#stand-alone-development): Use this if you want to do your own thing, and not contribute to FreeSewing - [**Stand-alone studio**](#stand-alone-studio): Use this if you want to do your own thing, and not contribute to FreeSewing
## Monorepo development ## Repository Studio
Run `yarn lab` to start the development environment: Run `npm run studio` to start the repository studio development environment:
```bash ```bash
yarn lab npm run studio
``` ```
Then point your browser to http://localhost:8000 Then point your browser to http://localhost:3000
Your new design will appear in the Local Designs page.
:::tip :::tip
### Adding a new design ### Adding another new design
This is all you need to work on existing designs. If you'd like to add a new design, run: This is all you need to work on existing designs. If you'd like to add another new design, run:
```bash ```bash
yarn new design npm run add
``` ```
Just make sure to re-start the lab afterwards with `yarn lab` Just make sure to re-start the repository studio afterwards with `npm run studio`
::: :::
## Stand-alone development ## Stand-alone studio
You will have a new folder that has the name you picked for your design. You will have a new folder that contains the stand-alone studio development environment.
If you chose `test`, you will have a folder named `test`. (Within this new folder, the `design` subfolder holds your design's configuration files and source code.
If you chose `banana`, you'll have a folder named `banana`.
(Within this new folder, the `design` subfolder holds your design's configuration file and source code.
You can ignore all other subfolders and files; they are part of the development environment.) You can ignore all other subfolders and files; they are part of the development environment.)
To start the development environment, enter the folder that was created To start the development environment, enter the folder that was created
and run `yarn dev` (or `npm run dev` if you're using npm as a package manager). and run `npm run start`.
Then open your browser and go to http://localhost:8000 ```bash
npm run start
```
Then open your browser and go to http://localhost:3000
:::tip :::tip
The development environment will watch for any changes you make to The development environment will watch for any changes you make to
the pattern's source code or configuration. the pattern's source code or configuration.
When you do, it will update automatically in your browser. When you do, the pattern will update automatically in your browser.
::: :::
### Adding another new design
This is all you need to work on existing designs. If you'd like to add another new design, run:
```bash
npm run add
```
Just make sure to re-start the stand-alone studio afterwards with `npm run start`.
:::note :::note
##### Yay, you're done! ##### Yay, you're done!

View file

@ -6,10 +6,10 @@ sidebar_position: 20
Now we will use `nvm` to install Node.js. Run the following command: Now we will use `nvm` to install Node.js. Run the following command:
```bash ```bash
nvm install lts/hydrogen nvm install lts/iron
``` ```
This will install the so-called LTS version of Node.js 18 on your system. This will install the so-called LTS version of Node.js 20 on your system.
LTS versions -- short for Long Term Support -- are good Node.js versions LTS versions -- short for Long Term Support -- are good Node.js versions
to use because they are stable and supported for a long time. to use because they are stable and supported for a long time.

View file

@ -47,7 +47,7 @@ With multiple Node.js versions installed, `nvm` allows you to switch between dif
versions. Just tell it which version you want to use: versions. Just tell it which version you want to use:
```bash ```bash
nvm use lts/hydrogen nvm use lts/iron
``` ```
If you picked a version that is not installed, `nvm` will simply tell you If you picked a version that is not installed, `nvm` will simply tell you

View file

@ -3,17 +3,16 @@ title: Setting up the FreeSewing development environment
sidebar_position: 40 sidebar_position: 40
--- ---
FreeSewing provides a development environment to help you design and develop FreeSewing provides the studio, a development environment to help you design and develop patterns.
patterns.
There are two ways to run this development environment: There are two ways to run this studio:
- [**Monorepo development**](#monorepo-development): Use this if you intend to - [**Repository studio**](#monorepo-studio): Use this if you intend to
contribute your work to FreeSewing contribute your work to FreeSewing
- [**Stand-alone development**](#stand-alone-development): Use this if you want - [**Stand-alone studio**](#stand-alone-studio): Use this if you want
to do your own thing, and not contribute to FreeSewing to do your own thing, and not contribute to FreeSewing
## Monorepo development ## Repository studio
:::note :::note
This is the recommended way for (aspiring) FreeSewing contributors This is the recommended way for (aspiring) FreeSewing contributors
@ -22,57 +21,41 @@ This is the recommended way for (aspiring) FreeSewing contributors
### TL;DR ### TL;DR
```bash ```bash
git clone https://github.com/freesewing/freesewing git clone https://codeberg.org/freesewing/freesewing.git
cd freesewing cd freesewing
yarn kickstart npm run kickstart
``` ```
:::tip :::tip
Even better: [clone your own Even better: [clone your own
fork](https://github.com/freesewing/freesewing/fork) fork](https://codeberg.org/freesewing/freesewing/fork)
```bash ```bash
git clone https://github.com/your-username/freesewing git clone https://codeberg.org/your-username/freesewing.git
cd freesewing cd freesewing
yarn kickstart npm run kickstart
``` ```
::: :::
This sets up the monorepo. If you would like to create a new design, run the This sets up the monorepo. If you would like to create a new design or plugin, run the
following command: following command:
```sh ```sh
yarn new design npm run add
```
If you'd like to create a new plugin, run this variant instead:
```sh
yarn new plugin
``` ```
### Step by step ### Step by step
:::note :::note
These docs assume you have git installed. These docs assume you have git installed.
But if you're running macOS, you have git, right? But if you're running on a Mac system, you have git, right?
::: :::
#### Install yarn
Our repository uses yarn workspaces. So you'll need `yarn` to work with it.
To install it run:
```bash
npm install yarn --global
```
#### Fork our repository #### Fork our repository
You'll want to fork our repository. This way you have your own copy where you can make You'll want to fork our repository. This way you have your own copy where you can make
all the changes you want. To do so, visit https://github.com/freesewing/freesewing/fork all the changes you want. To do so, visit https://codeberg.org/freesewing/freesewing/fork
#### Clone the forked repository #### Clone the forked repository
@ -82,81 +65,76 @@ Now that you have your very own fork, it's time to clone it locally.
git clone <url to your fork> git clone <url to your fork>
``` ```
Make sure to use the URL to your own fork, typically `https://github.com/your-username/freesewing` but Make sure to use the URL to your own fork, typically `https://codeberg.org/your-username/freesewing.git` but
obviously with your real username rather than `your-username`. obviously with your real username rather than `your-username`.
#### Install dependencies #### Install dependencies
Enter the directory that was created, and run the `yarn kickstart` command: Enter the directory that was created, and run the `npm run kickstart` command:
```bash ```bash
cd freesewing cd freesewing
yarn kickstart npm run kickstart
``` ```
Now you're ready to [start the development environment](/tutorials/getting-started-linux/dev-start). Now you're ready to [start the development environment](/tutorials/getting-started-linux/dev-start).
## Creating a new design ### Creating a new design or plugin
If you would like to create a new design, run the following command: If you would like to create a new design or plugin, run the following command:
```sh ```sh
yarn new design npm run add
``` ```
## Creating a new plugin After you've answered [some questions](#questions), it create a new
sub-folder for your design in the `designs` folder.
If you'd like to create a new plugin, run the following command: Now you're ready to [start the development
environment](/tutorials/getting-started-mac/dev-start).
```sh
yarn new plugin
```
## Stand-alone development ## Stand-alone development
With Node.js installed, all you need to do to setup the stand-alone development environment is run this command: With Node.js installed, all you need to do to setup the stand-alone development environment is run this command:
```bash ```bash
npx @freesewing/new-design npx @freesewing/studio
``` ```
After you've answered [some questions](#questions), it will take a while to set After you enter the folder name to create, it will take a while to set
everything up. When it's done, you will have a new folder with the development everything up. When it's done, you will have a new folder with the development
environment inside. environment inside.
Now you're ready to [start the development ### Creating a new design
environment](/tutorials/getting-started-linux/dev-start).
:::tip If you would like to create a new design, enter the folder that was just created and run the following command:
The folder will have the name you chose above.
::: ```sh
npm run add
```
After you've answered [some questions](#questions), it create a new
sub-folder for your design in the `designs` folder.
Now you're ready to [start the development
environment](/tutorials/getting-started-mac/dev-start).
:::note :::note
### Questions ### Questions
#### What design name to use
Please stick to a single word name using \[a-z] to avoid problems.
#### What template to use #### What template to use
Use `From scratch` unless you want to start from our of our blocks: Use `From scratch` unless you want to start from our of our blocks:
- Use `Extend Brian` to start from [Brian](https://freesewing.org/designs/brian) - Use `Extend Brian` to start from [Brian](https://freesewing.eu/designs/brian)
- Use `Extend Bent` to start from [Bent](https://freesewing.org/designs/bent) - Use `Extend Bent` to start from [Bent](https://freesewing.eu/designs/bent)
- Use `Extend Bella` to start from [Bella](https://freesewing.org/designs/bella) - Use `Extend Bella` to start from [Bella](https://freesewing.eu/designs/bella)
- Use `Extend Breanna` to start from [Breanna](https://freesewing.org/designs/breanna) - Use `Extend Breanna` to start from [Breanna](https://freesewing.eu/designs/breanna)
- Use `Extend Titan` to start from [Titan](https://freesewing.org/designs/titan) - Use `Extend Titan` to start from [Titan](https://freesewing.eu/designs/titan)
#### What name to use
This will become the name of your design. Stick to \[a-z] here to avoid problems.
If you're not certain what to pick, just mash some keys, it doesn't matter.
#### What package manager to use
You may wish to choose `yarn` since that is the package manager
that we use when doing work in the monorepo,
and many of our tutorials are written to use `yarn`.
However, it doesn't really matter.
You can choose either `yarn` or `npm` as you wish.
::: :::

View file

@ -3,55 +3,70 @@ title: Start the development environment
sidebar_position: 50 sidebar_position: 50
--- ---
FreeSewing provides a development environment to help you design and develop patterns. FreeSewing provides the studio, a development environment to help you design and develop patterns.
There are two ways to run this development environment: There are two ways to run this studio:
- [**Monorepo development**](#monorepo-development): Use this if you intend to contribute your work to FreeSewing - [**Repository studio**](#repository-studio): Use this if you intend to contribute your work to FreeSewing
- [**Stand-alone development**](#stand-alone-development): Use this if you want to do your own thing, and not contribute to FreeSewing - [**Stand-alone studio**](#stand-alone-studio): Use this if you want to do your own thing, and not contribute to FreeSewing
## Monorepo development ## Repository Studio
Run `yarn lab` to start the development environment: Run `npm run studio` to start the repository studio development environment:
```bash ```bash
yarn lab npm run studio
``` ```
Then point your browser to http://localhost:8000 Then point your browser to http://localhost:3000
Your new design will appear in the Local Designs page.
:::tip :::tip
### Adding a new design ### Adding another new design
This is all you need to work on existing designs. If you'd like to add a new design, run: This is all you need to work on existing designs. If you'd like to add another new design, run:
```bash ```bash
yarn new design npm run add
``` ```
Just make sure to re-start the lab afterwards with `yarn lab` Just make sure to re-start the repository studio afterwards with `npm run studio`
::: :::
## Stand-alone development ## Stand-alone studio
You will have a new folder that has the name you picked for your design. You will have a new folder that contains the stand-alone studio development environment.
If you chose `test`, you will have a folder named `test`. (Within this new folder, the `design` subfolder holds your design's configuration files and source code.
If you chose `banana`, you'll have a folder named `banana`.
(Within this new folder, the `design` subfolder holds your design's configuration file and source code.
You can ignore all other subfolders and files; they are part of the development environment.) You can ignore all other subfolders and files; they are part of the development environment.)
To start the development environment, enter the folder that was created To start the development environment, enter the folder that was created
and run `yarn dev` (or `npm run dev` if you're using npm as a package manager). and run `npm run start`.
Then open your browser and go to http://localhost:8000 ```bash
npm run start
```
Then open your browser and go to http://localhost:3000
:::tip :::tip
The development environment will watch for any changes you make to The development environment will watch for any changes you make to
the pattern's source code or configuration. the pattern's source code or configuration.
When you do, it will update automatically in your browser. When you do, the pattern will update automatically in your browser.
::: :::
### Adding another new design
This is all you need to work on existing designs. If you'd like to add another new design, run:
```bash
npm run add
```
Just make sure to re-start the stand-alone studio afterwards with `npm run start`.
:::note :::note
##### Yay, you're done! ##### Yay, you're done!

View file

@ -6,10 +6,10 @@ sidebar_position: 20
Now we will use `nvm` to install Node.js. Run the following command: Now we will use `nvm` to install Node.js. Run the following command:
```bash ```bash
nvm install lts/hydrogen nvm install lts/iron
``` ```
This will install the so-called LTS version of Node.js 18 on your system. This will install the so-called LTS version of Node.js 20 on your system.
LTS versions -- short for Long Term Support -- are good Node.js versions LTS versions -- short for Long Term Support -- are good Node.js versions
to use because they are stable and supported for a long time. to use because they are stable and supported for a long time.

View file

@ -47,7 +47,7 @@ With multiple Node.js versions installed, `nvm` allows you to switch between dif
versions. Just tell it which version you want to use: versions. Just tell it which version you want to use:
```bash ```bash
nvm use v10.22.1 nvm use lts/iron
``` ```
If you picked a version that is not installed, `nvm` will simply tell you If you picked a version that is not installed, `nvm` will simply tell you

View file

@ -62,7 +62,7 @@ Free Hobby accounts are limited to 3 Projects per Git repository.
Under each project there will be many, many deployments. 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. built from a specific commit version of a specific branch of the repository.
These deployments can be accessed using a web browser to preview These deployments can be accessed using a web browser to preview
the web app or website. the web app or website.

View file

@ -55,11 +55,11 @@ guide](https://github.com/nvm-sh/nvm#install--update-script). Once installed
you will need to activate NVM by either following the instructions printed to you will need to activate NVM by either following the instructions printed to
the screen or opening a new terminal. the screen or opening a new terminal.
#### Install Node.js (and optionally Yarn) #### Install Node.js
Now that you have NVM installed, you can install Node.js. The latest version can be Now that you have NVM installed, you can install Node.js. The latest version can be
installed using `nvm install default`. You can also install a specific version installed using `nvm install default`. You can also install a specific version
using `nvm install v18.17.0`. For the purposes of debugging it can be useful to using `nvm install v20.19.1`. For the purposes of debugging it can be useful to
have the same version of Node.js installed as the main project uses, which you can have the same version of Node.js installed as the main project uses, which you can
then activate using `nvm use <version>`. You can determine what version the then activate using `nvm use <version>`. You can determine what version the
FreeSewing project uses by checking FreeSewing project uses by checking
@ -68,16 +68,12 @@ FreeSewing project uses by checking
:::warning :::warning
At the time this guide was written the latest version of Node.js/npm has At the time this guide was written the latest version of Node.js/npm has
a bug in the dependency resolution process which causes the freesewing project a bug in the dependency resolution process which causes the freesewing project
to fail to build. Use the latest LTS version (currently 18.17.0) or the specific to fail to build. Use the latest LTS version (currently 20.19.1) or the specific
version used by the main project to avoid this issue. version used by the main project to avoid this issue.
::: :::
Node.js comes with the Node Package Manager (npm) by default which can be used to Node.js comes with the Node Package Manager (npm) by default which is used to
set up the project. The default package manager uses a fairly simplistic approach set up the project.
to dependency resolution which can make builds take a long time. Yarn is an
alternative package manager which makes builds faster, especially for monolithic
projects like FreeSewing. If you'd like to install yarn run `npm install yarn
--global` (optional, but recommended).
#### Install and configure Git (recommended) #### Install and configure Git (recommended)
@ -137,12 +133,12 @@ will take you to the latest release which provides an installer you can download
and run. Once nvm-windows is installed you will be able to continue with the and run. Once nvm-windows is installed you will be able to continue with the
rest of this process. rest of this process.
### Install Node.js (and optionally Yarn) ### Install Node.js
Open a Powershell terminal or command prompt. Run `nvm ls available` to show Open a Powershell terminal or command prompt. Run `nvm ls available` to show
versions that can be installed. Choose the appropriate version (you should use versions that can be installed. Choose the appropriate version (you should use
the same version as the freesewing project or latest LTS version) then run `nvm the same version as the freesewing project or latest LTS version) then run `nvm
install 18.17.0` and `nvm use 18.17.0` (where `18.17.0` is the full version install 20.19.1` and `nvm use 20.19.1` (where `20.19.1` is the full version
string of the version you wish to use) to activate the newly installed version. string of the version you wish to use) to activate the newly installed version.
You will receive a prompt for elevated permissions and will need to accept it in You will receive a prompt for elevated permissions and will need to accept it in
order to activate the new version of Node.js. order to activate the new version of Node.js.
@ -150,20 +146,16 @@ order to activate the new version of Node.js.
:::warning :::warning
At the time this guide was written the latest version of Node.js/npm has At the time this guide was written the latest version of Node.js/npm has
a bug in the dependency resolution process which causes the freesewing project a bug in the dependency resolution process which causes the freesewing project
to fail to build. Use the latest LTS version (currently 18.17.0) or the specific to fail to build. Use the latest LTS version (currently 20.19.1 or the specific
version used by the main project to avoid this issue. version used by the main project to avoid this issue.
::: :::
Node.js comes with the Node Package Manager (npm) by default which can be used to Node.js comes with the Node Package Manager (npm) by default which is used to
set up the project. The default package manager uses a fairly simplistic approach set up the project.
to dependency resolution which can make builds take a long time. Yarn is an
alternative package manager which makes builds faster, especially for monolithic
projects like FreeSewing. If you'd like to install yarn run (`npm install yarn
-g`) (optional).
## Setting up the FreeSewing development environment ## Setting up the FreeSewing development environment
In VSCode or in a terminal, navigate to the folder you wish to contain your new patterns (e.g. `D:\Documents\my-freesewing-patterns`). Inside this directory run `npx @freesewing/new-design`. In VSCode or in a terminal, navigate to the folder you wish to contain your new patterns (e.g. `D:\Documents\my-freesewing-patterns`). Inside this directory run `npx @freesewing/studio`.
After you've answered [some questions](#questions), it will take a while to set everything up. After you've answered [some questions](#questions), it will take a while to set everything up.
When it's done, you will have a new folder with the development environment inside. When it's done, you will have a new folder with the development environment inside.
@ -192,14 +184,6 @@ This will become the name of your design. Stick to \[a-z] here to avoid problems
If you're not certain what to pick, just mash some keys, it doesn't matter. If you're not certain what to pick, just mash some keys, it doesn't matter.
#### What package manager to use
You may wish to choose `yarn` since that is the package manager
that we use when doing work in the monorepo,
and many of our tutorials are written to use `yarn`.
However, it doesn't really matter.
You can choose either `yarn` or `npm` as you wish.
::: :::
## Start the development environment ## Start the development environment
@ -211,7 +195,7 @@ If you chose `banana`, you'll have a folder named `banana`.
You can ignore all other subfolders and files; they are part of the development environment.) You can ignore all other subfolders and files; they are part of the development environment.)
To start the development environment, navigate to the folder that was created To start the development environment, navigate to the folder that was created
and run `yarn dev` (or `npm run dev` if you're using npm as a package manager). and run `npm run org`.
Then open your browser and go to http://localhost:8000 Then open your browser and go to http://localhost:8000

View file

@ -1,28 +1,22 @@
--- ---
title: Setting up the development environment title: Setting up the FreeSewing development environment
sidebar_position: 20 sidebar_position: 20
--- ---
FreeSewing provides a development environment that visualizes your design for FreeSewing provides the studio, a development environment that visualizes your design for
you. This tutorial is for the stand-alone development environment, not the you. This tutorial is for the stand-alone studio, not the
monorepo development environment (which you may have set up if you followed a repository studio (which you may have set up if you followed a
getting started tutorial). getting started tutorial).
To set it up, I will open a terminal and enter the following command: To set it up, I will open a terminal and enter the following command:
```sh ```sh
npx @freesewing/new-design npx @freesewing/studio
``` ```
It will ask if it is ok to install the development environment in a new folder After you enter the folder name to create, it will take a while to set
named `freesewing`. You can accept the default, or pick a different folder name everything up. When it's done, you will have a new folder with the development
if you prefer. environment inside.
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,
and it will also initialize a git repository for you (if you have git on your system).
:::note :::note
@ -31,29 +25,58 @@ of dependencies that need to be downloaded.
::: :::
When it's ready, you can enter the `freesewing` directory that was just created and run `npm run dev`: You will have a new folder that contains the stand-alone studio development environment.
(Within this new folder, the `design` subfolder holds your design's configuration files and source code.
You can ignore all other subfolders and files; they are part of the development environment.)
```sh ### Creating a new design
cd freesewing
npm run dev If you would like to create a new design, enter the folder that was just
created and run the following command:
```bash
npm run add
``` ```
Or if you want to use yarn as package manager: After you've answered [some questions](#questions), it create a new
sub-folder for your design in the `designs` folder.
```sh :::note
cd freesewing
yarn dev ### Questions
#### What design name to use
Please stick to a single word name using \[a-z] to avoid problems.
#### What template to use
Use `From scratch` unless you want to start from our of our blocks:
- Use `Extend Brian` to start from [Brian](https://freesewing.eu/designs/brian)
- Use `Extend Bent` to start from [Bent](https://freesewing.eu/designs/bent)
- Use `Extend Bella` to start from [Bella](https://freesewing.eu/designs/bella)
- Use `Extend Breanna` to start from [Breanna](https://freesewing.eu/designs/breanna)
- Use `Extend Titan` to start from [Titan](https://freesewing.eu/designs/titan)
:::
### Starting the development environment
To start the development environment, enter the folder containing the
stand-alone studio and run `npm run start`.
```bash
npm run start
``` ```
Now open a browser and go to http://localhost:8000 Then open your browser and go to http://localhost:3000
If all goes well, we'll should see this landing page:
![The FreeSewing development environment](./nd.png)
:::tip :::tip
The development environment will watch for any changes you make to
##### More detailed setup tutorials are available the pattern's source code or configuration.
When you do, the pattern will update automatically in your browser.
:::
This pattern design tutorial contains only an abbreviated overview This pattern design tutorial contains only an abbreviated overview
of the setup process. of the setup process.
@ -69,7 +92,7 @@ For more detailed instructions, please refer to one of our setup tutorials:
##### Need help? ##### Need help?
If you run into any issues, head over to [FreeSewing.org/support](https://next.freesewing.org/support) If you run into any issues, head over to [FreeSewing.eu/support](https://freesewing.eu/support)
which lists the various ways in which you can get help. which lists the various ways in which you can get help.
::: :::

View file

@ -25,7 +25,7 @@ If you don't have NodeJS on your system, you can go to
##### NodeJS versions ##### NodeJS versions
You need Node.js 18 (lts/hydrogen) or higher to use FreeSewing You need Node.js 20 (lts/iron) or higher to use FreeSewing
If you're looking to use different versions, I can recommend using `nvm` which makes this very easy: https://github.com/nvm-sh/nvm If you're looking to use different versions, I can recommend using `nvm` which makes this very easy: https://github.com/nvm-sh/nvm

View file

@ -1,5 +1,5 @@
--- ---
title: "Part 1: Prerequisites" title: 'Part 1: Prerequisites'
--- ---
In this first part, I will get your up and running with the FreeSewing In this first part, I will get your up and running with the FreeSewing
@ -10,24 +10,35 @@ this section. If not, I have good news and bad news (and then some more good
news) for you. news) for you.
The good news is that JavaScript is an easy language to pick up. It is also a The good news is that JavaScript is an easy language to pick up. It is also a
very popular and versatile language and the skills you learn here will serve very popular and versatile language and the skills you learn here will serve
you well. you well.
The bad news is that the JavaScript ecosystem is vast, and unfortunately The bad news is that the JavaScript ecosystem is vast, and unfortunately
somewhat fractured. Most of the problems people need help with are not so much somewhat fractured. Most of the problems people need help with are not so much
in the code itself, but rather getting everything to work together. in the code itself, but rather getting everything to work together.
This is true not just for FreeSewing, but pretty much all modern JavaScript. This is true not just for FreeSewing, but pretty much all modern JavaScript.
But, no need to despair, FreeSewing provides a development environment that But, no need to despair, FreeSewing provides a development environment that
will take care of all of this for you. So you can focus on designing patterns. will take care of all of this for you. So you can focus on designing patterns.
If you have NodeJS on your system, getting that development environment up If you have NodeJS on your system, getting that development environment up
and running takes only a single command: and running takes only a single command:
```sh ```sh
npx @freesewing/new-design npx @freesewing/studio
``` ```
If you don't have NodeJS on your system --- or if you're not sure what If you don't have NodeJS on your system --- or if you're not sure what
NodeJS is to begin with --- read on to learn how to install it. NodeJS is to begin with --- read on to learn how to install it.
:::danger[Windows users also need to install `git`]
The `git` command line utility is also a requirement for installing the
FreeSewing development environment. Linux and Mac systems come with
`git` pre-installed, but Windows users will need to install it manually.
Please see the
[Getting started on Windows](/tutorials/getting-started-windows/)
tutorial for instructions on how to install `git` on a Windows system.
:::

View file

@ -3,7 +3,7 @@ title: The FreeSewing development environment
sidebar_position: 30 sidebar_position: 30
--- ---
If you have been to FreeSewing.org the FreeSewing development environment will look familiar. If you have been to FreeSewing.eu the FreeSewing development environment will look familiar.
That's because under the hood, it re-uses the same building blocks. That's because under the hood, it re-uses the same building blocks.
At the top of the page is the header with a row of icons that lay out what is available to you. At the top of the page is the header with a row of icons that lay out what is available to you.

View file

@ -60,27 +60,28 @@ function draftBib({
tweak _ measurements.head / 12 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
)
paths.quarterNeck = new Path() points.rightCp1 = points.right.shift(
.move(points.right) 90,
.curve( points.bottom.dy(points.right) / 2
points.rightCp1, )
points.bottomCp2, points.bottomCp2 = points.bottom.shift(
points.bottom 0,
) points.bottom.dx(points.right) / 2
.hide() )
delta = paths.quarterNeck.length() - target paths.quarterNeck = new Path()
if (delta > 0) tweak = tweak _ 0.99 .move(points.right)
else tweak = tweak _ 1.02 .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) } while (Math.abs(delta) > 1)

View file

@ -9,22 +9,22 @@ how to design a bespoke sewing pattern, start to finish.
This tutorial is divided into three parts, allowing you to speedrun or entirely This tutorial is divided into three parts, allowing you to speedrun or entirely
skip certain parts depending on your interests or prior experience: skip certain parts depending on your interests or prior experience:
## Part 1: Prerequisites ## Part 1: Prerequisites
The first sections of this tutorial, [Part 1](/tutorials/pattern-design/part1), The first sections of this tutorial, [Part 1](/tutorials/pattern-design/part1),
deals with the prerequisites. Installing node, setting up the FreeSewing deals with the prerequisites. Installing node, setting up the FreeSewing
development environment on your system, and so on. If you are familiar with development environment on your system, and so on. If you are familiar with
the JavaScript ecosystem, I can summarize that entire section in this one-liner the JavaScript ecosystem, I can summarize that entire section in this one-liner
that sets up the FreeSewing development environment on your system: that sets up the FreeSewing development environment on your system:
```sh ```sh
npx @freesewing/new-design npx @freesewing/studio
``` ```
## Part 2: Parametric design ## Part 2: Parametric design
In [Part 2](/tutorials/pattern-design/part2) I will show you how to design a In [Part 2](/tutorials/pattern-design/part2) I will show you how to design a
parametric sewing pattern with FreeSewing. We'll create a part, add a bunch of parametric sewing pattern with FreeSewing. We'll create a part, add a bunch of
points, draw lines and curves, and so on. points, draw lines and curves, and so on.
All the basic skills required to create a sewing pattern in code. All the basic skills required to create a sewing pattern in code.
@ -39,10 +39,9 @@ There is more to FreeSewing patterns than meets the eye, and in [Part
further value to your designs. further value to your designs.
This includes things like translation, supporting laser cutters, avoiding the This includes things like translation, supporting laser cutters, avoiding the
need to printing with so-called *paperless patterns*, as well as how you can need to printing with so-called _paperless patterns_, as well as how you can
configure your pattern to integrate with FreeSewing.org, or your own configure your pattern to integrate with FreeSewing.org, or your own
frontend. frontend.
You can follow this tutorial start to finish, or skip ahead and back, the You can follow this tutorial start to finish, or skip ahead and back, the
choice is yours. choice is yours.

View file

@ -3,6 +3,7 @@ import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem' import TabItem from '@theme/TabItem'
import { pluginFlip } from '@freesewing/plugin-flip' import { pluginFlip } from '@freesewing/plugin-flip'
import { pluginGore } from '@freesewing/plugin-gore' import { pluginGore } from '@freesewing/plugin-gore'
import { pluginPathUtils } from '@freesewing/plugin-path-utils'
import { pluginRingsector } from '@freesewing/plugin-ringsector' import { pluginRingsector } from '@freesewing/plugin-ringsector'
import { Design } from '@freesewing/core' import { Design } from '@freesewing/core'
import yaml from 'js-yaml' import yaml from 'js-yaml'
@ -49,7 +50,7 @@ const buildPattern = (children, settings = { margin: 5 }, tutorial = false, pape
lengthRatio: { pct: 75, min: 55, max: 85, menu: 'style' }, lengthRatio: { pct: 75, min: 55, max: 85, menu: 'style' },
} }
: {}, : {},
plugins: [pluginFlip, pluginGore, pluginRingsector], plugins: [pluginFlip, pluginGore, pluginPathUtils, pluginRingsector],
} }
const design = new Design({ const design = new Design({
parts: [part], parts: [part],

View file

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1000.0001"
height="570"
viewBox="0 0 264.58335 150.8125"
version="1.1"
id="svg562"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="collarease.svg">
<defs
id="defs556">
<marker
style="overflow:visible"
id="Arrow1MstartQR"
refX="0"
refY="0"
orient="auto">
<path
inkscape:connector-curvature="0"
transform="matrix(0.4,0,0,0.4,4,0)"
style="fill:#ff5b77;fill-rule:evenodd;stroke:#ff5b77;stroke-width:1.00000003pt;marker-start:none"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path5084" />
</marker>
<marker
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend3"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path5087"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#ff5b77;fill-rule:evenodd;stroke:#ff5b77;stroke-width:1.00000003pt;marker-start:none"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
style="overflow:visible"
id="marker53"
refX="0"
refY="0"
orient="auto">
<path
inkscape:connector-curvature="0"
transform="matrix(0.4,0,0,0.4,4,0)"
style="fill:#ff5b77;fill-rule:evenodd;stroke:#ff5b77;stroke-width:1.00000003pt;marker-start:none"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path51" />
</marker>
<marker
orient="auto"
refY="0"
refX="0"
id="marker57"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path55"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#ff5b77;fill-rule:evenodd;stroke:#ff5b77;stroke-width:1.00000003pt;marker-start:none"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.8101934"
inkscape:cx="643.91418"
inkscape:cy="184.47717"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1043"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-global="false"
showguides="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata559">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-47.625)">
<path
sodipodi:nodetypes="cccccccc"
style="fill:#808080;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
d="M 54.1423,55.951664 41.911976,57.89133 41.479965,72.528929 88.376337,70.286566 90.362996,57.874273 78.243784,55.951664 c -2.227828,2.394111 -6.970559,2.235652 -12.047084,2.235652 -5.836933,0.03682 -9.857971,0.124718 -12.0544,-2.235652 z"
id="inside"
inkscape:connector-curvature="0" />
<path
id="primary1"
style="fill:#404040;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
d="m 54.365069,55.951664 -24.994326,3.963971 c 0.172338,1.737552 3.199375,16.991043 3.199375,24.085049 0.437121,18.491896 -1.180108,20.477076 -2.081846,24.079736 2.059602,15.42627 -0.19137,23.36576 -0.19859,37.43095 0,15.45988 -2.473168,37.24966 -2.818075,41.08324 l 77.888383,0.1366 c -0.34465,-3.83357 -1.77452,-24.63877 -1.77452,-40.09865 0,-21.75303 -1.51285,-35.46062 -1.51285,-40.89932 -2.227137,-3.85782 -3.165523,-15.233969 -2.405677,-24.110942 0,-7.094007 3.614267,-19.969111 3.786587,-21.706663 L 78.466544,55.951664 C 76.238715,58.345775 79.8041,67.655702 66.41946,67.700859 53.034819,67.746011 56.561498,58.312034 54.365069,55.951664 Z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccsccccsc" />
<path
id="primary2"
style="fill:#404040;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
d="m 29.976678,59.796959 c 4.038677,5.842073 7.195097,34.945535 2.259218,43.712951 -3.300515,29.05614 -11.777082,63.63539 -14.767163,87.13685 -7.17153,0.38249 -9.8107924,0.34584 -14.4503455,-1.16564 C 3.325709,152.0588 11.092508,115.43576 19.69795,80.932692 22.337969,70.842832 26.664555,59.796959 29.976678,59.796959 Z m 73.645182,0 c -4.038678,5.842073 -7.195093,34.945535 -2.25922,43.712951 3.30052,29.05614 11.77708,63.63539 14.76716,87.13685 7.17153,0.38249 9.8108,0.34584 14.45035,-1.16564 -0.30732,-37.42232 -8.07412,-74.04536 -16.67956,-108.548428 -2.64002,-10.08986 -6.96661,-21.135733 -10.27873,-21.135733 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccc" />
<path
inkscape:connector-curvature="0"
id="path973"
d="m 182.5973,56.623458 -9.28477,1.267872 -0.43201,14.637599 46.89637,-2.242363 1.98666,-12.412293 -8.70857,-1.560874 c -2.22783,2.394111 -10.3812,1.873917 -15.45772,1.873917 -5.83694,0.03682 -12.80353,0.796512 -14.99996,-1.563858 z"
style="fill:#808080;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
sodipodi:nodetypes="cccccccc" />
<path
sodipodi:nodetypes="cccccccsccccsc"
inkscape:connector-curvature="0"
d="m 182.35499,56.623458 -21.58369,3.292177 c 0.17234,1.737552 3.19938,16.991043 3.19938,24.085049 0.43712,18.491896 -1.18011,20.477076 -2.08185,24.079736 2.0596,15.42627 -0.19137,23.36576 -0.19859,37.43095 0,15.45988 -2.47317,37.24966 -2.81808,41.08324 l 77.88839,0.1366 c -0.34465,-3.83357 -1.77452,-24.63877 -1.77452,-40.09865 0,-21.75303 -1.51285,-35.46062 -1.51285,-40.89932 -2.22714,-3.85782 -3.16553,-15.233969 -2.40568,-24.110942 0,-7.094007 3.61427,-19.969111 3.78659,-21.706663 l -22.04143,-3.600996 c -2.22783,2.394111 -1.608,12.997189 -14.99264,13.042346 -13.38464,0.04515 -13.2686,-10.373157 -15.46503,-12.733527 z"
style="fill:#404040;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
id="path975" />
<path
sodipodi:nodetypes="cccccccccccc"
inkscape:connector-curvature="0"
d="m 161.37724,59.796959 c 4.03867,5.842073 7.19509,34.945535 2.25921,43.712951 -3.30051,29.05614 -11.77708,63.63539 -14.76716,87.13685 -7.17153,0.38249 -9.81079,0.34584 -14.45034,-1.16564 0.30732,-37.42232 8.07412,-74.04536 16.67956,-108.548428 2.64002,-10.08986 6.9666,-21.135733 10.27873,-21.135733 z m 73.64518,0 c -4.03868,5.842073 -7.1951,34.945535 -2.25922,43.712951 3.30052,29.05614 11.77708,63.63539 14.76716,87.13685 7.17153,0.38249 9.8108,0.34584 14.45035,-1.16564 -0.30732,-37.42232 -8.07412,-74.04536 -16.67956,-108.548428 -2.64002,-10.08986 -6.96661,-21.135733 -10.27873,-21.135733 z"
style="fill:#404040;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
id="path977" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.8 KiB

View file

@ -0,0 +1,12 @@
---
title: 'Collar ease'
---
![Collar ease](collarease.svg)
Controls the amount of ease at your collar/neck and the size
of the neck opening..
Hugo uses the neck circumference measurement rather than head
circumference to produce the neck opening size when generating patterns.
Use this option to adjust the neck opening size if needed.

View file

@ -10,6 +10,7 @@ import Armholedepth from '@site/docs/docs/designs/hugo/options/armholedepth/read
import Backneckcutout from '@site/docs/docs/designs/hugo/options/backneckcutout/readme.mdx' import Backneckcutout from '@site/docs/docs/designs/hugo/options/backneckcutout/readme.mdx'
import Bicepsease from '@site/docs/docs/designs/hugo/options/bicepsease/readme.mdx' import Bicepsease from '@site/docs/docs/designs/hugo/options/bicepsease/readme.mdx'
import Chestease from '@site/docs/docs/designs/hugo/options/chestease/readme.mdx' import Chestease from '@site/docs/docs/designs/hugo/options/chestease/readme.mdx'
import Collarease from '@site/docs/docs/designs/hugo/options/collarease/readme.mdx'
import Cuffease from '@site/docs/docs/designs/hugo/options/cuffease/readme.mdx' import Cuffease from '@site/docs/docs/designs/hugo/options/cuffease/readme.mdx'
import Draftforhighbust from '@site/docs/docs/designs/hugo/options/draftforhighbust/readme.mdx' import Draftforhighbust from '@site/docs/docs/designs/hugo/options/draftforhighbust/readme.mdx'
import Hipsease from '@site/docs/docs/designs/hugo/options/hipsease/readme.mdx' import Hipsease from '@site/docs/docs/designs/hugo/options/hipsease/readme.mdx'
@ -52,6 +53,16 @@ import Sleevelengthbonus from '@site/docs/docs/designs/hugo/options/sleevelength
<Chestease /> <Chestease />
### Collar ease {#collarease}
**The amount of ease around your neck**
- Type: **Percentage**
- Default: **15%**
- Minimum: **0%**
- Maximum: **40%**
<Collarease />
### Cuff ease {#cuffease} ### Cuff ease {#cuffease}
**The amount of ease at your wrist.** **The amount of ease at your wrist.**

View file

@ -0,0 +1,21 @@
---
title: 'Legacy waist and hips calculations'
---
This option allows you to use the legacy way of calculating the waist
and hips.
The legacy (v3) way used the chest circumference to set the intial waist
and hips points.
It then used waist and hips measurements to attempt to move the waist
and hips points towards the actual measurement values.
The new, v4 way instead uses the actual waist and hips measurements to
set the waist and hips points.
It also contains corrections to the front waist dart and side seam
adjustments to maintain the correct width and ease at the waist and hips.
It also adjusts the armhole point after the dart rotation to maintain
the correct width and ease at the armhole.
If you enable this option, Wahid will revert to the v3 way of calculating
waist and hips points.

View file

@ -21,6 +21,7 @@ import Hemradius from '@site/docs/docs/designs/wahid/options/hemradius/readme.md
import Hemstyle from '@site/docs/docs/designs/wahid/options/hemstyle/readme.mdx' import Hemstyle from '@site/docs/docs/designs/wahid/options/hemstyle/readme.mdx'
import Hipsease from '@site/docs/docs/designs/wahid/options/hipsease/readme.mdx' import Hipsease from '@site/docs/docs/designs/wahid/options/hipsease/readme.mdx'
import Legacyarmholedepth from '@site/docs/docs/designs/wahid/options/legacyarmholedepth/readme.mdx' import Legacyarmholedepth from '@site/docs/docs/designs/wahid/options/legacyarmholedepth/readme.mdx'
import Legacywaisthips from '@site/docs/docs/designs/wahid/options/legacywaisthips/readme.mdx'
import Lengthbonus from '@site/docs/docs/designs/wahid/options/lengthbonus/readme.mdx' import Lengthbonus from '@site/docs/docs/designs/wahid/options/lengthbonus/readme.mdx'
import Neckinset from '@site/docs/docs/designs/wahid/options/neckinset/readme.mdx' import Neckinset from '@site/docs/docs/designs/wahid/options/neckinset/readme.mdx'
import Necklinedrop from '@site/docs/docs/designs/wahid/options/necklinedrop/readme.mdx' import Necklinedrop from '@site/docs/docs/designs/wahid/options/necklinedrop/readme.mdx'
@ -255,6 +256,14 @@ import Weltheight from '@site/docs/docs/designs/wahid/options/weltheight/readme.
<Legacyarmholedepth /> <Legacyarmholedepth />
### Legacy waist and hips widths {#legacywaisthips}
**Enable this option to use the legacy (v3) way to calculate the waist and hips widths (using chest circumference) rather than the new way (using the waist and hips measurements).**
- Type: **Boolean**
- Default: **false**
<Legacywaisthips />
### Neck inset {#neckinset} ### Neck inset {#neckinset}
**How much the shoulder seam is cut inwards at the neck** **How much the shoulder seam is cut inwards at the neck**

View file

@ -25,6 +25,22 @@ function customizeSidebar(items) {
} }
} }
// Filter out submenus in Your Measurements Sets and Your Patterns
for (const item in items) {
if (items[item].label === 'Account') {
for (const design in items[item].items) {
for (const subpage in items[item].items[design].items) {
if (
items[item].items[design].items[subpage].label === 'Your Measurements Sets' ||
items[item].items[design].items[subpage].label === 'Your Patterns'
) {
items[item].items[design].items[subpage].items = []
}
}
}
}
}
return items return items
} }

View file

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1000.0001"
height="570"
viewBox="0 0 264.58335 150.8125"
version="1.1"
id="svg562"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="collarease.svg">
<defs
id="defs556">
<marker
style="overflow:visible"
id="Arrow1MstartQR"
refX="0"
refY="0"
orient="auto">
<path
inkscape:connector-curvature="0"
transform="matrix(0.4,0,0,0.4,4,0)"
style="fill:#ff5b77;fill-rule:evenodd;stroke:#ff5b77;stroke-width:1.00000003pt;marker-start:none"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path5084" />
</marker>
<marker
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend3"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path5087"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#ff5b77;fill-rule:evenodd;stroke:#ff5b77;stroke-width:1.00000003pt;marker-start:none"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
style="overflow:visible"
id="marker53"
refX="0"
refY="0"
orient="auto">
<path
inkscape:connector-curvature="0"
transform="matrix(0.4,0,0,0.4,4,0)"
style="fill:#ff5b77;fill-rule:evenodd;stroke:#ff5b77;stroke-width:1.00000003pt;marker-start:none"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path51" />
</marker>
<marker
orient="auto"
refY="0"
refX="0"
id="marker57"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path55"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#ff5b77;fill-rule:evenodd;stroke:#ff5b77;stroke-width:1.00000003pt;marker-start:none"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.8101934"
inkscape:cx="643.91418"
inkscape:cy="184.47717"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1043"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-global="false"
showguides="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata559">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-47.625)">
<path
sodipodi:nodetypes="cccccccc"
style="fill:#808080;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
d="M 54.1423,55.951664 41.911976,57.89133 41.479965,72.528929 88.376337,70.286566 90.362996,57.874273 78.243784,55.951664 c -2.227828,2.394111 -6.970559,2.235652 -12.047084,2.235652 -5.836933,0.03682 -9.857971,0.124718 -12.0544,-2.235652 z"
id="inside"
inkscape:connector-curvature="0" />
<path
id="primary1"
style="fill:#404040;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
d="m 54.365069,55.951664 -24.994326,3.963971 c 0.172338,1.737552 3.199375,16.991043 3.199375,24.085049 0.437121,18.491896 -1.180108,20.477076 -2.081846,24.079736 2.059602,15.42627 -0.19137,23.36576 -0.19859,37.43095 0,15.45988 -2.473168,37.24966 -2.818075,41.08324 l 77.888383,0.1366 c -0.34465,-3.83357 -1.77452,-24.63877 -1.77452,-40.09865 0,-21.75303 -1.51285,-35.46062 -1.51285,-40.89932 -2.227137,-3.85782 -3.165523,-15.233969 -2.405677,-24.110942 0,-7.094007 3.614267,-19.969111 3.786587,-21.706663 L 78.466544,55.951664 C 76.238715,58.345775 79.8041,67.655702 66.41946,67.700859 53.034819,67.746011 56.561498,58.312034 54.365069,55.951664 Z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccsccccsc" />
<path
id="primary2"
style="fill:#404040;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
d="m 29.976678,59.796959 c 4.038677,5.842073 7.195097,34.945535 2.259218,43.712951 -3.300515,29.05614 -11.777082,63.63539 -14.767163,87.13685 -7.17153,0.38249 -9.8107924,0.34584 -14.4503455,-1.16564 C 3.325709,152.0588 11.092508,115.43576 19.69795,80.932692 22.337969,70.842832 26.664555,59.796959 29.976678,59.796959 Z m 73.645182,0 c -4.038678,5.842073 -7.195093,34.945535 -2.25922,43.712951 3.30052,29.05614 11.77708,63.63539 14.76716,87.13685 7.17153,0.38249 9.8108,0.34584 14.45035,-1.16564 -0.30732,-37.42232 -8.07412,-74.04536 -16.67956,-108.548428 -2.64002,-10.08986 -6.96661,-21.135733 -10.27873,-21.135733 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccc" />
<path
inkscape:connector-curvature="0"
id="path973"
d="m 182.5973,56.623458 -9.28477,1.267872 -0.43201,14.637599 46.89637,-2.242363 1.98666,-12.412293 -8.70857,-1.560874 c -2.22783,2.394111 -10.3812,1.873917 -15.45772,1.873917 -5.83694,0.03682 -12.80353,0.796512 -14.99996,-1.563858 z"
style="fill:#808080;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
sodipodi:nodetypes="cccccccc" />
<path
sodipodi:nodetypes="cccccccsccccsc"
inkscape:connector-curvature="0"
d="m 182.35499,56.623458 -21.58369,3.292177 c 0.17234,1.737552 3.19938,16.991043 3.19938,24.085049 0.43712,18.491896 -1.18011,20.477076 -2.08185,24.079736 2.0596,15.42627 -0.19137,23.36576 -0.19859,37.43095 0,15.45988 -2.47317,37.24966 -2.81808,41.08324 l 77.88839,0.1366 c -0.34465,-3.83357 -1.77452,-24.63877 -1.77452,-40.09865 0,-21.75303 -1.51285,-35.46062 -1.51285,-40.89932 -2.22714,-3.85782 -3.16553,-15.233969 -2.40568,-24.110942 0,-7.094007 3.61427,-19.969111 3.78659,-21.706663 l -22.04143,-3.600996 c -2.22783,2.394111 -1.608,12.997189 -14.99264,13.042346 -13.38464,0.04515 -13.2686,-10.373157 -15.46503,-12.733527 z"
style="fill:#404040;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
id="path975" />
<path
sodipodi:nodetypes="cccccccccccc"
inkscape:connector-curvature="0"
d="m 161.37724,59.796959 c 4.03867,5.842073 7.19509,34.945535 2.25921,43.712951 -3.30051,29.05614 -11.77708,63.63539 -14.76716,87.13685 -7.17153,0.38249 -9.81079,0.34584 -14.45034,-1.16564 0.30732,-37.42232 8.07412,-74.04536 16.67956,-108.548428 2.64002,-10.08986 6.9666,-21.135733 10.27873,-21.135733 z m 73.64518,0 c -4.03868,5.842073 -7.1951,34.945535 -2.25922,43.712951 3.30052,29.05614 11.77708,63.63539 14.76716,87.13685 7.17153,0.38249 9.8108,0.34584 14.45035,-1.16564 -0.30732,-37.42232 -8.07412,-74.04536 -16.67956,-108.548428 -2.64002,-10.08986 -6.96661,-21.135733 -10.27873,-21.135733 z"
style="fill:#404040;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
id="path977" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.8 KiB

View file

@ -0,0 +1,12 @@
---
title: 'Collar ease'
---
![Collar ease](collarease.svg)
Controls the amount of ease at your collar/neck and the size
of the neck opening..
Hugo uses the neck circumference measurement rather than head
circumference to produce the neck opening size when generating patterns.
Use this option to adjust the neck opening size if needed.

View file

@ -2,6 +2,25 @@ import { themes as prismThemes } from 'prism-react-renderer'
import { docusaurusPlugins } from './plugins/index.mjs' import { docusaurusPlugins } from './plugins/index.mjs'
import smartypants from 'remark-smartypants' import smartypants from 'remark-smartypants'
function customizeSidebar(items) {
// Filter out submenus in Your Measurements Sets and Your Patterns
for (const item in items) {
if (items[item].label === 'Account') {
for (const design in items[item].items) {
for (const subpage in items[item].items[design].items) {
if (
items[item].items[design].items[subpage].label === 'Your Measurements Sets' ||
items[item].items[design].items[subpage].label === 'Your Patterns'
) {
items[item].items[design].items[subpage].items = []
}
}
}
}
}
return items
}
const config = { const config = {
title: 'FreeSewing Studio', title: 'FreeSewing Studio',
tagline: 'FreeSewing for Designers', tagline: 'FreeSewing for Designers',
@ -25,6 +44,10 @@ const config = {
docs: { docs: {
routeBasePath: '/', routeBasePath: '/',
sidebarPath: './sidebars.js', sidebarPath: './sidebars.js',
async sidebarItemsGenerator({ defaultSidebarItemsGenerator, ...args }) {
const sidebarItems = await defaultSidebarItemsGenerator(args)
return customizeSidebar(sidebarItems)
},
remarkPlugins: [[smartypants, { dashes: 'oldschool' }]], remarkPlugins: [[smartypants, { dashes: 'oldschool' }]],
}, },
theme: { theme: {