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,7 +1,7 @@
# Contributing to FreeSewing
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! ❤️
The following is a set of guidelines for contributing to FreeSewing.
@ -11,23 +11,24 @@ Use your best judgment, and feel free to propose changes to this document in a p
## Code of Conduct
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).
<Tip>
:::tip
##### 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!
Please don't file an issue to ask a question.
You'll get faster results by contacting us on Discord.
You can get to our Discord server via https://discord.freesewing.org/.
You'll get faster results by contacting us on Discord or the Forum.
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.
So what's daytime for you might be the middle of the night for others.
@ -35,16 +36,16 @@ Please be patient. Sooner or later, somebody will answer.
## 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:
- [`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
### 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?
@ -55,7 +56,4 @@ This file is mostly geared towards code contributors, but there's plenty of othe
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).
Don't be afraid to take on an issue. If you get stuck, [we'll help you out](https://discord.freesewing.org/).
<ReadMore />
Don't be afraid to take on an issue. If you get stuck, [we'll help you out](https://discord.freesewing.eu/).

View file

@ -23,7 +23,9 @@
"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.",
"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": {
"ribbingHeight": {

View file

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

View file

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

View file

@ -1,4 +1,3 @@
export const collarEase = 0.05
export const armholeDepthFactor = 0.5
export const shoulderEase = 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 ribbingHeight = { pct: 10, min: 4, max: 20, 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,
measurements,
macro,
log,
units,
part,
}) {
// Top of raglan sleeve
@ -233,6 +235,46 @@ function hugoSleeve({
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
}

View file

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

View file

@ -117,6 +117,18 @@
"centerBackDart": {
"t": "Center back dart",
"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 { 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'
function wahidBack({
@ -20,6 +27,13 @@ function wahidBack({
for (let i of Object.keys(paths)) delete paths[i]
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
let shoulderLen = points.shoulder.dist(points.neck)
let backInset = shoulderLen * options.backInset
@ -279,6 +293,7 @@ export const back = {
neckInset,
centerBackDart,
backScyeDart,
legacyWaistHips,
},
draft: wahidBack,
}

View file

@ -30,6 +30,7 @@ import {
s3Armhole,
shoulderSlopeReduction,
backNeckCutout,
legacyWaistHips,
} from './options.mjs'
function wahidFront({
@ -51,6 +52,14 @@ function wahidFront({
// Cleanup from Brian
for (let i of Object.keys(paths)) delete paths[i]
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
points.closureTop = new Point(
measurements.chest * options.frontOverlap * -1,
@ -176,15 +185,31 @@ function wahidFront({
points.dartHipLeft,
points.pocketTopMid.y + pwvh
)
points.pocketTopLeft = points.pocketTopMidLeft.shift(180 + options.pocketAngle, pw / 2)
points.pocketBottomLeft = points.pocketTopLeft.shift(options.pocketAngle - 90, pwh)
// The pocket can start out offset from the horizontal/vertical.
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.pocketBottomMidRight = points.pocketBottomMidLeft.flipX(points.pocketTopMid)
points.pocketTopRight = points.pocketTopMidRight.shift(options.pocketAngle, pw / 2)
points.pocketBottomRight = points.pocketTopRight.shift(options.pocketAngle - 90, pwh)
points.pocketTopRight = points.pocketTopMidRight.shift(
options.pocketAngle - startingAngleOffset,
pw / 2
)
points.pocketBottomRight = points.pocketTopRight.shift(
options.pocketAngle - 90 - startingAngleOffset,
pwh
)
// Store pocket bag length
store.set('pocketBagLength', points.pocketTopMid.dy(points.cfHem) * 0.75)
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
points._dartWidth = points.dartTop.shiftFractionTowards(
points.armholeHollow.rotate(options.frontScyeDart, points.dartTop),
@ -230,6 +255,10 @@ function wahidFront({
if (typeof points[p] !== 'undefined')
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)
}
// Facing/Lining boundary (flb)
@ -314,6 +343,7 @@ function wahidFront({
*/
// Cutlist
store.cutlist.setCut({ cut: 2, from: 'fabric' })
store.cutlist.addCut({ cut: 2, from: 'interfacing' })
// Buttons
points.button1 = new Point(0, points.closureTop.y + 10)
@ -561,6 +591,7 @@ export const front = {
s3Armhole,
shoulderSlopeReduction,
backNeckCutout,
legacyWaistHips,
},
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 pocketAngle = { deg: 5, min: 0, max: 5, menu: 'advanced' }
export const shoulderSlopeReduction = { pct: 0, min: 0, max: 80, menu: 'advanced' }
export const legacyWaistHips = { bool: false, menu: 'advanced' }
// Hide inherited options
export const bicepsEase = 0.15

View file

@ -16,6 +16,22 @@ export const constructMainDart = (part) => {
store.set('wr12', wr12)
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.dartWaistRight = points.dartWaistCenter.shift(0, 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}`}
onClick={() =>
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 }} />
</ModalWrapper>
)
@ -292,13 +297,12 @@ const CreateBookmark = ({ type, title, slug }) => {
const { setLoadingStatus } = useContext(LoadingStatusContext)
const { setModal } = useContext(ModalContext)
const url = slug.toLowerCase().slice(0,4) === 'http'
? slug
: `/${slug}`
const url = slug.toLowerCase().slice(0, 4) === 'http' ? slug : `/${slug}`
const bookmark = async (evt) => {
evt.stopPropagation()
setLoadingStatus([true, 'Contacting backend'])
let title = name
const [status] = await backend.createBookmark({ type, title, url })
if (status === 201) {
setLoadingStatus([true, 'Bookmark created', true, true])

View file

@ -118,7 +118,7 @@ export const Pattern = ({ id, Link }) => {
delete data.data
delete data.userId
delete data.img
data.settings = JSON.parse(data.settings)
data.name += ' (clone)'
const [status, body] = await backend.createPattern(data)
if (status === 201 && body.result === 'created') {
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 bg = { aspectRatio: '1/1.4' }
if (!lineDrawing) {
bg.backgroundImage = `url(${exampleImageUrl}`
bg.backgroundImage = `url(${exampleImageUrl})`
bg.backgroundSize = 'cover'
bg.backgroundPosition = 'center center'
}
const inner = (
<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
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`}
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`}
style={bg}
>
<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'}`}
>
<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>
</h5>
{lineDrawing ? (
<div className="tw:grow tw:w-full tw:h-auto tw:square tw:text-center">
<LineDrawing className="tw:w-5/6 tw:m-auto tw:my-0 tw:text-base-content" />
<div className="tw:flex-auto tw:flex tw:justify-center">
<LineDrawing className="tw:w-5/6 tw:text-base-content" />
</div>
) : (
<span />
)}
<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`}`}
>
<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 {
AsideIcon,
CompactIcon,
DetailIcon,
ExpandIcon,
ExportIcon,
@ -241,7 +242,8 @@ export const HeaderMenuDraftViewUiPreferences = (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 (
<HeaderMenuDropdown
@ -253,7 +255,12 @@ export const HeaderMenuDraftViewFlags = (props) => {
<HeaderMenuIcon name="flag" extraClasses="tw:text-secondary" />
<span className="tw:hidden tw:lg:inline">
Flags
<span>({count})</span>
<NumberBadge
value={count}
color={
Object.values(flatFlags).some((it) => it.type === 'error') ? 'error' : 'secondary'
}
/>
</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) => {
const { update, state } = props
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)"
>
<UnitsIcon
className={`${size} ${settings.units === accountUnits ? style.dflt : style.custom}`}
className={`${size} ${settings.units === accountUnits || typeof settings.units === 'undefined' ? style.dflt : style.custom}`}
/>
</Button>
) : null}
@ -336,9 +354,11 @@ export const HeaderMenuDraftViewIcons = (props) => {
}
tooltip="Turns Expand on or off (see Core Settings)"
>
<ExpandIcon
className={`${size} ${[false, 0, '0'].includes(settings.expand) ? style.custom : style.dflt}`}
/>
{[false, 0, '0'].includes(settings.expand) ? (
<CompactIcon className={`${size} ${style.custom}`} />
) : (
<ExpandIcon className={`${size} ${style.dflt}`} />
)}
</Button>
) : null}
<HeaderMenuIconSpacer />

View file

@ -130,10 +130,15 @@ export const BookmarkedSetPicker = ({
const [status, body] = await backend.getBookmarks()
const loadedSets = {}
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')) {
let set
const match = bookmark.url.match(setsRE)
if (match) unique_ids.add(match[2])
}
for (const id of unique_ids) {
try {
const [status, body] = await backend.getSet(bookmark.url.slice(6))
const [status, body] = await backend.getSet(id)
if (status === 200 && body.result === 'success') {
const [hasMeasies] = hasRequiredMeasurements(Design, body.set.measies)
loadedSets[body.set.id] = { ...body.set, hasMeasies }

View file

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

View file

@ -149,7 +149,9 @@ export const MenuListInput = ({
: '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 : (
<div
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.
*
* Note that we could try to use something like 1.5875 for imperial to move in steps of 1/16 inches,
* but we round the mm values to two digits in the options, which would accumulate rounding errors.
*
* Because of this, we use 10ths of inches instead of 16ths of inches.
* Imperial:
* mm, inch, decimal digits precision needed:
* 1.27 mm, 0.1 in, 2 digits
* 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 (
<MenuSliderInput

View file

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

View file

@ -79,7 +79,7 @@ export const MenuListValue = ({ current, config, changed }) => {
else if (val) key = <BoolYesIcon />
else key = <BoolNoIcon />
const translated = config.doNotTranslate || key
const translated = config.doNotTranslate ? val : key
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
*/
export const MenuPctOptionValue = ({ config, current, settings, changed, patternConfig }) => {
const val = changed ? current : config.pct / 100
const val = changed ? current : config.dflt
return (
<MenuHighlightValue changed={changed}>

View file

@ -1,5 +1,5 @@
// Dependencies
import { linkClasses } from '@freesewing/utils'
import { linkClasses, capitalize } from '@freesewing/utils'
// Hooks
import React from 'react'
// Components
@ -20,7 +20,22 @@ export const DocsView = ({ 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">
<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>
<H5>Understanding the FreeSewing Pattern Editor</H5>
<p className="tw:text-lg">
@ -29,8 +44,8 @@ export const DocsView = ({ state, config, update }) => {
<b>
<a
className={linkClasses}
href="https://freesewing.org/docs/about/site/editor"
>{`FreeSewing.org/docs/about/editor`}</a>
href="https://freesewing.eu/docs/about/editor"
>{`FreeSewing.eu/docs/about/editor`}</a>
</b>
</p>
</Popout>

View file

@ -43,7 +43,7 @@ export const TestView = ({ Design, state, update, config }) => {
if (missingMeasurements(state)) return <Null />
const { settings } = state
if (settings.sample) {
if (settings?.sample) {
/*
* 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

View file

@ -158,7 +158,9 @@ export const handleExport = async ({
workerArgs.strings.setName = settings?.metadata?.setName
? settings.metadata.setName
: '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 : ''
const notes = 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 string = ''
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)
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', ' ')
desc = he.decode(desc)

View file

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

View file

@ -333,6 +333,25 @@ export const CodeIcon = (props) => (
</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
*
@ -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" />
</IconWrapper>
)

View file

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

View file

@ -60,7 +60,7 @@ export const PointXray = ({
: null
}
></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 x={point.x + 3} dy={5}>
{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 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 = {
title: 'FreeSewing Studio',
tagline: 'FreeSewing for Designers',
@ -28,6 +47,10 @@ const config = {
docs: {
routeBasePath: '/',
sidebarPath: './sidebars.js',
async sidebarItemsGenerator({ defaultSidebarItemsGenerator, ...args }) {
const sidebarItems = await defaultSidebarItemsGenerator(args)
return customizeSidebar(sidebarItems)
},
remarkPlugins: [[smartypants, { dashes: 'oldschool' }]],
},
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 */
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
*
* 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
*/
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:
```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
@ -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:
```js
path.seam.attr("data-text", "finishWithBiasTape");
path.seam.attr('data-text', 'finishWithBiasTape')
```
This way, different strings for different languages can be associated with
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
[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
@ -189,5 +189,3 @@ Constructing a path counter-clockwise will also ensure that the path offset goes
rather than inwards.
:::

View file

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

View file

@ -14,7 +14,7 @@ The draft method receives a single parameter, an object which you can _destructu
access the following properties:
| Property | Description |
| --------:|:----------- |
| ----------------: | :------------------------------------------------------------------------------------------------------------ |
| | **_Content constructors_** |
| `Path` | A [Path constructor](/reference/api/path) to create new paths |
| `Point` | A [Point constructor](/reference/api/point) to create new points |
@ -26,6 +26,7 @@ access the following properties:
| | **_Access to settings_** |
| `absoluteOptions` | Access to `settings.absoluteOptions` |
| `complete` | Access to `settings.complete` |
| `expand` | Access to `settings.expand` |
| `measurements` | Access to `settings.measurements` |
| `options` | Access to `settings.options` |
| `paperless` | Access to `settings.paperless` |

View file

@ -46,9 +46,11 @@ paths.tangent = new Path()
return part
}
```
</Example>
## Notes
Keep in mind that calculations with Bézier curves are often approximations.
```

View file

@ -12,7 +12,7 @@ Number path.roughLength()
## Example
<Example caption="Example of the Path.attr() method">
<Example caption="Example of the Path.roughLength() method">
```js
({ Point, points, Path, paths, macro, units, part }) => {
@ -27,18 +27,22 @@ Number path.roughLength()
macro("pd", {
path: paths.example,
id: 'macro1',
d: -10,
force: true,
text: `Path.roughLength() = ${units(paths.example.roughLength())}`
})
macro("pd", {
path: paths.example,
id: 'macro2',
d: 10,
force: true,
text: `Path.length() = ${units(paths.example.length())}`
})
return part
}
```
</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 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
This method is chainable as it returns the `Path` object
This method is chainable as it returns the `Path` objecti
:::
## Example

View file

@ -36,15 +36,21 @@ Path path.translate(float deltaX, float deltaY)
macro("ld", {
from: points.B,
to: points.step1,
noStartMarker: true
noStartMarker: true,
id: 'macro1',
force: true,
})
macro("ld", {
from: points.step1,
to: points.step2,
noStartMarker: true
noStartMarker: true,
id: 'macro2',
force: true,
})
return part
}
```
</Example>
```

View file

@ -35,21 +35,21 @@ Point point.flipY(Point mirror = false)
points.mirror = new Point(0, 60)
points.mirrorLineEnd = new Point(95, 60)
points._start = points.start.flipY(points.mirror)
points._churchTowerWallLeft = points.churchTowerWallLeft.flipY(points.mirror)
points._churchTowerRoofLeft = points.churchTowerRoofLeft.flipY(points.mirror)
points._churchTowerTop = points.churchTowerTop.flipY(points.mirror)
points._churchTowerRoofRight = points.churchTowerRoofRight.flipY(
points.\_start = points.start.flipY(points.mirror)
points.\_churchTowerWallLeft = points.churchTowerWallLeft.flipY(points.mirror)
points.\_churchTowerRoofLeft = points.churchTowerRoofLeft.flipY(points.mirror)
points.\_churchTowerTop = points.churchTowerTop.flipY(points.mirror)
points.\_churchTowerRoofRight = points.churchTowerRoofRight.flipY(
points.mirror
)
points._churchRoofRight = points.churchRoofRight.flipY(points.mirror)
points._churchWallRight = points.churchWallRight.flipY(points.mirror)
points._houseWallLeft = points.houseWallLeft.flipY(points.mirror)
points._houseRoofLeft = points.houseRoofLeft.flipY(points.mirror)
points._houseRoofTop = points.houseRoofTop.flipY(points.mirror)
points._houseRoofRight = points.houseRoofRight.flipY(points.mirror)
points._houseWallRight = points.houseWallRight.flipY(points.mirror)
points._end = points.end.flipY(points.mirror)
points.\_churchRoofRight = points.churchRoofRight.flipY(points.mirror)
points.\_churchWallRight = points.churchWallRight.flipY(points.mirror)
points.\_houseWallLeft = points.houseWallLeft.flipY(points.mirror)
points.\_houseRoofLeft = points.houseRoofLeft.flipY(points.mirror)
points.\_houseRoofTop = points.houseRoofTop.flipY(points.mirror)
points.\_houseRoofRight = points.houseRoofRight.flipY(points.mirror)
points.\_houseWallRight = points.houseWallRight.flipY(points.mirror)
points.\_end = points.end.flipY(points.mirror)
paths.skylineTop = new Path()
.move(points.start)
@ -66,7 +66,29 @@ Point point.flipY(Point mirror = false)
.line(points.houseWallRight)
.line(points.end)
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>
```

View file

@ -27,10 +27,14 @@ Point point.shift(float angle, float distance)
macro("ld", {
from: points.B,
to: points.A,
d: -10
d: -10,
id: 'macro1',
force: true,
})
return part
}
```
</Example>
```

View file

@ -15,7 +15,7 @@ negative values to shift the point in the opposite direction.
Point point.shiftFractionTowards(Point target, float fraction)
```
## Point.shiftFractionTowards() example
## Example
<Example caption="An example of the Point.shiftFractionTowards() method">
```js
@ -38,17 +38,22 @@ Point point.shiftFractionTowards(Point target, float fraction)
macro("ld", {
from: points.C,
to: points.A,
d: -10
d: -10,
id: 'macro1',
force: true,
})
macro("ld", {
from: points.B,
to: points.A,
d: 20
d: 20,
id: 'macro2',
force: true,
})
return part
}
```
</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
[`Point.shiftTowards()`](/reference/api/point/shifttowards/) or
[`Point.shiftOutwards()`](/reference/api/point/shiftoutwards/) instead.
```

View file

@ -31,10 +31,13 @@ Point point.shiftOutwards(Point target, float distance)
macro("ld", {
from: points.C,
to: points.B,
d: -10
d: -10,
force: true,
})
return part
}
```
</Example>
```

View file

@ -31,11 +31,18 @@ Point point.shiftTowards(Point target, float distance)
macro("ld", {
from: points.C,
to: points.A,
d: -10
d: -10,
force: true,
})
// Boundary box
paths.bbox = new Path()
.move(new Point(10,10))
.move(new Point(125,70))
return part
}
```
</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
[`Point.shiftFractionTowards()`](/reference/api/point/shiftfractiontowards/)
instead.
```

View file

@ -7,9 +7,7 @@ The `Point.slope()` method returns the slope (dy/dx) of a line between two Point
## Signature
```js
point.slope(otherPoint)
```
## Example
@ -17,9 +15,7 @@ point.slope(otherPoint)
<Example caption="An example of the Point.slope() method">
```js
({ Point, points, Path, paths, Snippet, snippets, part, macro }) => {
;({ Point, points, Path, paths, Snippet, snippets, part, macro }) => {
points.A = new Point(0, 0)
points.B = new Point(200, 150)
@ -29,21 +25,25 @@ point.slope(otherPoint)
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,
})
paths.line = new Path()
.move(points.A)
.line(points.B)
.attr("class", "canvas")
.setText("Slope: " + slope, "center text-lg")
.attr('class', 'canvas')
.setText('Slope: ' + slope, 'center text-lg')
return part
}
```
</Example>

View file

@ -24,7 +24,7 @@ Positive values for `deltaY` will move the point downwards.
<Example caption="An example of the Point.translate() method">
```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.B = points.A.translate(120, 60)
@ -42,10 +42,18 @@ Positive values for `deltaY` will move the point downwards.
from: points.A,
to: points.B,
text: "translate(120,60)",
noStartMarker: true
noStartMarker: true,
force: true,
})
// Bounding box
paths.bbox = new Path()
.move(new Point(10, 10))
.move(new Point(130, 80))
return part
}
```
</Example>
```

View file

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

View file

@ -9,8 +9,22 @@ SVG document.
## 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>
/* 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>
```

View file

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

View file

@ -4,6 +4,15 @@ title: hem
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
```js
@ -68,7 +77,7 @@ Path macro('hem', {
| `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 |
## Detailed Description
## Notes
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.
Unlike the core `path.join(...)` function, 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.
Unlike the core [`Path.join()`](/reference/api/path/join) function,
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
(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.
:::note
:::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.
##### Not a core-plugins macro
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
```js
@ -72,5 +61,29 @@ Path macro('join', {
| 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) |
| `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`. |
## 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.
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.
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` 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
@ -79,6 +72,27 @@ Path macro('offset', {
| 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. |
| `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`. |
| `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.
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
trim useless path ends when adjacent paths in the array are intersecting.
Unlike the core [`Path.join()`](/reference/api/path/join) and
[`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
(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).
:::note
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.
##### Not a core-plugins macro
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.
The `sa` 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
@ -76,7 +72,25 @@ macro('sa', {
| 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. |
| `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`. |
| `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. |
## 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
---
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.
## Installation
@ -25,10 +27,4 @@ import { pathUtilsPlugin } 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

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

@ -13,6 +13,7 @@ Object settings = {
Object absoluteOptions,
Boolean complete=true,
Boolean embed=false,
Boolean expand=true,
String idPrefix='fs-',
Object|Boolean layout=true,
String locale='en',
@ -51,4 +52,3 @@ new pattern([
},
])
```

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
pattern configuration.
## Signature
```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.
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
pattern configuration.
## Signature
```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.
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
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
```js
undefined Store.flag.info({
id: 'id_string',
title: 'flag:expandIsOn.t',
desc: 'flag:expandIsOn.d',
msg: 'flag:expandIsOn',
notes: [
'sorcha:moreInfo1',
'sorcha:moreInfo2',
],
replace: {
key1: //code for replacement value,
key2: //code for replacement value,
},
suggest: {
text: 'flag:disable',
icon: 'expand',
@ -38,11 +49,13 @@ The example above is from our implementation, which uses the following propertie
## Configuration
| Property | Type | Description |
| ----------:| ------------------- | ----------- |
| ------------------------: | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | String | An ID for this flag message. If none is provided, `title` will be used |
| `title` | String | The title of the message |
| `desc` | String | The description of the message |
| `notes` | String or Array of Strings | More information/notes (see [Notes](#notes))|
| `title` | String | The translation key for the title of the message |
| `desc` | String | The translation key for the description of the message |
| `msg` | String | The translation key for the message |
| `notes` | String or Array of Strings | Translation keys for more information/notes (see [Notes](#notes)) |
| `replace` | Object | Key/values for text replacements (see [Replacement Values](#replacement-values)) |
| `suggest.text` | String | Text to go on the button to implement the suggested configuration change |
| `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)) |
@ -52,14 +65,24 @@ The example above is from our implementation, which uses the following propertie
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
An optional name of an icon. Or leave it out to not render and icon.
The idea is that the icon helps convey the message, the following icon names are supported:
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, with the following icon names supported:
- `note`
- `info`
@ -85,7 +108,7 @@ The `suggest.update` object has only two possible top-level keys:
They both take the same parameter, an array with two elements:
```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.
@ -109,7 +132,7 @@ So to set the `waistEase` option to `0.2`, it should look like this:
## Example
```js
({ store, part }) => {
;({ store, part }) => {
store.flag.info({
msg: `aaron:cutNeckBinding`,
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
}
```

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
pattern configuration.
## Signature
```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.
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

@ -23,19 +23,15 @@ The example above is from our implementation, which uses the following propertie
## Configuration
| Property | Type | Description |
| ----------:| ------------------- | ----------- |
| -------: | ------ | ---------------------------- |
| `preset` | String | The ID of an existing preset |
## Example
```js
({ store, expand, part }) => {
store.flag.preset(expand
? 'expandIsOn'
: 'expandIsOff'
)
;({ store, expand, part }) => {
store.flag.preset(expand ? 'expandIsOn' : 'expandIsOff')
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
pattern configuration.
## Signature
```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.
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
pattern configuration.
## Signature
```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.
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
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
modify existing designs, add new files to create new designs, and run
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
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.'
## Editing files
@ -234,8 +231,14 @@ To delete a codespace:
and select "Delete".
:::note RELATED
For more information, please see:
- [GitHub Codebases documentation](https://docs.github.com/en/codespaces).
- [About billing for GitHub Codespaces](https://docs.github.com/en/billing/managing-billing-for-github-codespaces/about-billing-for-github-codespaces)
:::
[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
---
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
- [**Repository studio**](#monorepo-studio): Use this if you intend to
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
## Monorepo development
## Repository studio
:::note
This is the recommended way for (aspiring) FreeSewing contributors
@ -22,34 +21,28 @@ This is the recommended way for (aspiring) FreeSewing contributors
### TL;DR
```bash
git clone https://github.com/freesewing/freesewing
git clone https://codeberg.org/freesewing/freesewing.git
cd freesewing
yarn kickstart
npm run kickstart
```
:::tip
Even better: [clone your own
fork](https://github.com/freesewing/freesewing/fork)
fork](https://codeberg.org/freesewing/freesewing/fork)
```bash
git clone https://github.com/your-username/freesewing
git clone https://codeberg.org/your-username/freesewing.git
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:
```sh
yarn new design
```
If you'd like to create a new plugin, run this variant instead:
```sh
yarn new plugin
npm run add
```
### Step by step
@ -59,20 +52,10 @@ These docs assume you have git installed.
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
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
@ -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>
```
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`.
#### 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
cd freesewing
yarn kickstart
npm run kickstart
```
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,
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:
If you would like to create a new design or plugin, run the following command:
```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:
```sh
yarn new plugin
```
Now you're ready to [start the development
environment](/tutorials/getting-started-linux/dev-start).
## Stand-alone development
With Node.js installed, all you need to do to setup the stand-alone development environment is run this command:
```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
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
environment](/tutorials/getting-started-linux/dev-start).
:::tip
The folder will have the name you chose above.
:::
:::note
### 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.org/designs/brian)
- Use `Extend Bent` to start from [Bent](https://freesewing.org/designs/bent)
- Use `Extend Bella` to start from [Bella](https://freesewing.org/designs/bella)
- Use `Extend Breanna` to start from [Breanna](https://freesewing.org/designs/breanna)
- Use `Extend Titan` to start from [Titan](https://freesewing.org/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.
- 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)
:::

View file

@ -3,55 +3,70 @@ title: Start the development environment
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
- [**Stand-alone development**](#stand-alone-development): Use this if you want to do your own thing, and not contribute to FreeSewing
- [**Repository studio**](#repository-studio): Use this if you intend to contribute your work 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
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
### 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
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.
If you chose `test`, you will have a folder named `test`.
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 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.)
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
The development environment will watch for any changes you make to
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
##### 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:
```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
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:
```bash
nvm use lts/hydrogen
nvm use lts/iron
```
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
---
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
- [**Repository studio**](#monorepo-studio): Use this if you intend to
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
## Monorepo development
## Repository studio
:::note
This is the recommended way for (aspiring) FreeSewing contributors
@ -22,57 +21,41 @@ This is the recommended way for (aspiring) FreeSewing contributors
### TL;DR
```bash
git clone https://github.com/freesewing/freesewing
git clone https://codeberg.org/freesewing/freesewing.git
cd freesewing
yarn kickstart
npm run kickstart
```
:::tip
Even better: [clone your own
fork](https://github.com/freesewing/freesewing/fork)
fork](https://codeberg.org/freesewing/freesewing/fork)
```bash
git clone https://github.com/your-username/freesewing
git clone https://codeberg.org/your-username/freesewing.git
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:
```sh
yarn new design
```
If you'd like to create a new plugin, run this variant instead:
```sh
yarn new plugin
npm run add
```
### Step by step
:::note
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
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
@ -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>
```
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`.
#### 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
cd freesewing
yarn kickstart
npm run kickstart
```
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
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:
```sh
yarn new plugin
```
Now you're ready to [start the development
environment](/tutorials/getting-started-mac/dev-start).
## Stand-alone development
With Node.js installed, all you need to do to setup the stand-alone development environment is run this command:
```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
environment inside.
Now you're ready to [start the development
environment](/tutorials/getting-started-linux/dev-start).
### Creating a new design
:::tip
The folder will have the name you chose above.
:::
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
environment](/tutorials/getting-started-mac/dev-start).
:::note
### 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.org/designs/brian)
- Use `Extend Bent` to start from [Bent](https://freesewing.org/designs/bent)
- Use `Extend Bella` to start from [Bella](https://freesewing.org/designs/bella)
- Use `Extend Breanna` to start from [Breanna](https://freesewing.org/designs/breanna)
- Use `Extend Titan` to start from [Titan](https://freesewing.org/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.
- 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)
:::

View file

@ -3,55 +3,70 @@ title: Start the development environment
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
- [**Stand-alone development**](#stand-alone-development): Use this if you want to do your own thing, and not contribute to FreeSewing
- [**Repository studio**](#repository-studio): Use this if you intend to contribute your work 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
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
### 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
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.
If you chose `test`, you will have a folder named `test`.
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 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.)
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
The development environment will watch for any changes you make to
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
##### 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:
```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
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:
```bash
nvm use v10.22.1
nvm use lts/iron
```
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.
\_Deployments are simply builds, an instance of a website/app
_Deployments_ are simply builds, an instance of a website/app
built from a specific commit version of a specific branch of the repository.
These deployments can be accessed using a web browser to preview
the web app or website.

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
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
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
then activate using `nvm use <version>`. You can determine what version the
FreeSewing project uses by checking
@ -68,16 +68,12 @@ FreeSewing project uses by checking
:::warning
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
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.
:::
Node.js comes with the Node Package Manager (npm) by default which can be used to
set up the project. The default package manager uses a fairly simplistic approach
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).
Node.js comes with the Node Package Manager (npm) by default which is used to
set up the project.
#### 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
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
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
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.
You will receive a prompt for elevated permissions and will need to accept it in
order to activate the new version of Node.js.
@ -150,20 +146,16 @@ order to activate the new version of Node.js.
:::warning
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
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.
:::
Node.js comes with the Node Package Manager (npm) by default which can be used to
set up the project. The default package manager uses a fairly simplistic approach
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).
Node.js comes with the Node Package Manager (npm) by default which is used to
set up the project.
## 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.
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.
#### 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
@ -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.)
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

View file

@ -1,28 +1,22 @@
---
title: Setting up the development environment
title: Setting up the FreeSewing development environment
sidebar_position: 20
---
FreeSewing provides a development environment that visualizes your design for
you. This tutorial is for the stand-alone development environment, not the
monorepo development environment (which you may have set up if you followed a
FreeSewing provides the studio, a development environment that visualizes your design for
you. This tutorial is for the stand-alone studio, not the
repository studio (which you may have set up if you followed a
getting started tutorial).
To set it up, I will open a terminal and enter the following command:
```sh
npx @freesewing/new-design
npx @freesewing/studio
```
It will ask if it is ok to install the development environment in a new folder
named `freesewing`. You can accept the default, or pick a different folder name
if you prefer.
It will also ask what package manager you would like to use.
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).
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
environment inside.
:::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
cd freesewing
npm run dev
### 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:
```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
cd freesewing
yarn dev
:::note
### 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
If all goes well, we'll should see this landing page:
![The FreeSewing development environment](./nd.png)
Then open your browser and go to http://localhost:3000
:::tip
##### More detailed setup tutorials are available
The development environment will watch for any changes you make to
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
of the setup process.
@ -69,7 +92,7 @@ For more detailed instructions, please refer to one of our setup tutorials:
##### 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.
:::

View file

@ -25,7 +25,7 @@ If you don't have NodeJS on your system, you can go to
##### 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

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
@ -25,9 +25,20 @@ If you have NodeJS on your system, getting that development environment up
and running takes only a single command:
```sh
npx @freesewing/new-design
npx @freesewing/studio
```
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.
:::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
---
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.
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,6 +60,7 @@ function draftBib({
tweak _ measurements.head / 12
)
points.rightCp1 = points.right.shift(
90,
points.bottom.dy(points.right) / 2
@ -79,8 +80,8 @@ function draftBib({
.hide()
delta = paths.quarterNeck.length() - target
if (delta > 0) tweak = tweak _ 0.99
else tweak = tweak _ 1.02
if (delta > 0) tweak = tweak * 0.99
else tweak = tweak * 1.02
} while (Math.abs(delta) > 1)

View file

@ -18,7 +18,7 @@ the JavaScript ecosystem, I can summarize that entire section in this one-liner
that sets up the FreeSewing development environment on your system:
```sh
npx @freesewing/new-design
npx @freesewing/studio
```
## Part 2: Parametric design
@ -39,10 +39,9 @@ There is more to FreeSewing patterns than meets the eye, and in [Part
further value to your designs.
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
frontend.
You can follow this tutorial start to finish, or skip ahead and back, the
choice is yours.

View file

@ -3,6 +3,7 @@ import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
import { pluginFlip } from '@freesewing/plugin-flip'
import { pluginGore } from '@freesewing/plugin-gore'
import { pluginPathUtils } from '@freesewing/plugin-path-utils'
import { pluginRingsector } from '@freesewing/plugin-ringsector'
import { Design } from '@freesewing/core'
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' },
}
: {},
plugins: [pluginFlip, pluginGore, pluginRingsector],
plugins: [pluginFlip, pluginGore, pluginPathUtils, pluginRingsector],
}
const design = new Design({
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 Bicepsease from '@site/docs/docs/designs/hugo/options/bicepsease/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 Draftforhighbust from '@site/docs/docs/designs/hugo/options/draftforhighbust/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 />
### Collar ease {#collarease}
**The amount of ease around your neck**
- Type: **Percentage**
- Default: **15%**
- Minimum: **0%**
- Maximum: **40%**
<Collarease />
### Cuff ease {#cuffease}
**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 Hipsease from '@site/docs/docs/designs/wahid/options/hipsease/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 Neckinset from '@site/docs/docs/designs/wahid/options/neckinset/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 />
### 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}
**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
}

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 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 = {
title: 'FreeSewing Studio',
tagline: 'FreeSewing for Designers',
@ -25,6 +44,10 @@ const config = {
docs: {
routeBasePath: '/',
sidebarPath: './sidebars.js',
async sidebarItemsGenerator({ defaultSidebarItemsGenerator, ...args }) {
const sidebarItems = await defaultSidebarItemsGenerator(args)
return customizeSidebar(sidebarItems)
},
remarkPlugins: [[smartypants, { dashes: 'oldschool' }]],
},
theme: {