1
0
Fork 0

add(bibi): Add Bibi, a FreeSewing pattern for a knit top body block

This commit is contained in:
Jonathan Haas 2024-04-19 12:04:46 +02:00
parent 9f593d2eb7
commit 44bb09e4c3
No known key found for this signature in database
GPG key ID: 0E7873037C5924E8
25 changed files with 1957 additions and 2 deletions

View file

@ -103,6 +103,25 @@
"setSleeve" "setSleeve"
] ]
}, },
"bibi": {
"code": "Jonathan Haas",
"description": "A FreeSewing pattern for a knit top body block",
"design": [
"Jonathan Haas"
],
"difficulty": 2,
"lab": true,
"org": true,
"tags": [
"blocks",
"tops"
],
"techniques": [
"curvedSeam",
"hem",
"flatSleeve"
]
},
"bob": { "bob": {
"code": "Joost De Cock", "code": "Joost De Cock",
"description": "A FreeSewing pattern for a bib", "description": "A FreeSewing pattern for a bib",

View file

@ -0,0 +1 @@
config/config.js

17
designs/bibi/CHANGELOG.md Normal file
View file

@ -0,0 +1,17 @@
# Change log for: @freesewing/bibi
## 3.0.0 (2023-09-30)
### Changed
- All FreeSewing packages are now ESM only.
- All FreeSewing packages now use named exports.
- Dropped support for NodeJS 14. NodeJS 18 (LTS/hydrogen) or more recent is now required.
This is the **initial release**, and the start of this change log.
> Prior to version 2, FreeSewing was not a JavaScript project.
> As such, that history is out of scope for this change log.

162
designs/bibi/README.md Normal file
View file

@ -0,0 +1,162 @@
<p align='center'><a
href="https://www.npmjs.com/package/@freesewing/bibi"
title="@freesewing/bibi on NPM"
><img src="https://img.shields.io/npm/v/@freesewing/bibi.svg"
alt="@freesewing/bibi on NPM"/>
</a><a
href="https://opensource.org/licenses/MIT"
title="License: MIT"
><img src="https://img.shields.io/npm/l/@freesewing/bibi.svg?label=License"
alt="License: MIT"/>
</a><a
href="https://deepscan.io/dashboard#view=project&tid=2114&pid=2993&bid=23256"
title="Code quality on DeepScan"
><img src="https://deepscan.io/api/teams/2114/projects/2993/branches/23256/badge/grade.svg"
alt="Code quality on DeepScan"/>
</a><a
href="https://github.com/freesewing/freesewing/issues?q=is%3Aissue+is%3Aopen+label%3Apkg%3Atina"
title="Open issues tagged pkg:bibi"
><img src="https://img.shields.io/github/issues/freesewing/freesewing/pkg:bibi.svg?label=Issues"
alt="Open issues tagged pkg:bibi"/>
</a><a
href="#contributors-"
title="All Contributors"
><img src="https://img.shields.io/badge/all_contributors-127-pink.svg"
alt="All Contributors"/>
</a></p><p align='center'><a
href="https://twitter.com/freesewing_org"
title="Follow @freesewing_org on Twitter"
><img src="https://img.shields.io/badge/%F3%A0%80%A0-Follow%20us-blue.svg?logo=twitter&logoColor=white&logoWidth=15"
alt="Follow @freesewing_org on Twitter"/>
</a><a
href="https://chat.freesewing.org"
title="Chat with us on Discord"
><img src="https://img.shields.io/discord/698854858052075530?label=Chat%20on%20Discord"
alt="Chat with us on Discord"/>
</a><a
href="https://freesewing.org/patrons/join"
title="Become a FreeSewing Patron"
><img src="https://img.shields.io/badge/%F3%A0%80%A0-Support%20us-blueviolet.svg?logo=cash-app&logoColor=white&logoWidth=15"
alt="Become a FreeSewing Patron"/>
</a><a
href="https://instagram.com/freesewing_org"
title="Follow @freesewing_org on Twitter"
><img src="https://img.shields.io/badge/%F3%A0%80%A0-Follow%20us-E4405F.svg?logo=instagram&logoColor=white&logoWidth=15"
alt="Follow @freesewing_org on Twitter"/>
</a></p>
# @freesewing/bibi
A FreeSewing pattern for overlap top
# FreeSewing
> [!TIP]
>#### Support FreeSewing: Become a patron, or make a one-time donation 🥰
>
> FreeSewing is an open source project maintained by Joost De Cock and financially supported by the FreeSewing patrons.
>
> If you feel FreeSewing is worthwhile, and you can spend a few coins without
hardship, then you should [join us and become a patron](https://freesewing.org/community/join).
## What am I looking at? 🤔
This repository is the FreeSewing *monorepo* holding all FreeSewing's websites, documentation, designs, plugins, and other NPM packages.
This folder holds: @freesewing/bibi
If you're not entirely sure what to do or how to start, type this command:
```
npm run tips
```
> [!NOTE]
> If you don't want to set up a dev environment, you can run it in your browser:
>
> [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/freesewing/freesewing)
>
> We recommend that you fork our repository and then
> put `gitpod.io/#<entire-url-of-your-fork` into a browser
> to start up a browser-based dev environment of your own.
## About FreeSewing 💀
Where the world of makers and developers collide, that's where you'll find FreeSewing.
If you're a maker, checkout [freesewing.org](https://freesewing.org/) where you can generate
sewing patterns adapted to your measurements.
If you're a developer, the FreeSewing documentation lives at [freesewing.dev](https://freesewing.dev/).
The FreeSewing [core library](https://freesewing.dev/reference/api/) is a *batteries-included* toolbox
for parametric design of sewing patterns. But FreeSewing also provides a range
of [plugins](https://freesewing.dev/reference/plugins/) that further extend the
functionality of the platform.
If you have NodeJS installed, you can try it right now by running:
```bash
npx @freesewing/new-design
```
Getting started guides are available for:
- [Linux](https://freesewing.dev/tutorials/getting-started-linux/)
- [MacOS](https://freesewing.dev/tutorials/getting-started-mac/)
- [Windows](https://freesewing.dev/tutorials/getting-started-windows/)
The [pattern design tutorial](https://freesewing.dev/tutorials/pattern-design/) will
show you how to create your first parametric design.
## Getting started ⚡
To get started with FreeSewing, you can spin up our development environment with:
```bash
npx @freesewing/new-design
```
To work with FreeSewing's monorepo, you'll need [NodeJS v18](https://nodejs.org), [lerna](https://lerna.js.org/) and [yarn](https://yarnpkg.com/) on your system.
Once you have those, clone (or fork) this repo and run `yarn kickstart`:
```bash
git clone git@github.com:freesewing/freesewing.git
cd freesewing
yarn kickstart
```
## Links 👩‍💻
**Official channels**
- 💻 Makers website: [FreeSewing.org](https://freesewing.org)
- 💻 Developers website: [FreeSewing.dev](https://freesewing.dev)
- ✅ [Support](https://github.com/freesewing/freesewing/issues/new/choose),
[Issues](https://github.com/freesewing/freesewing/issues) &
[Discussions](https://github.com/freesewing/freesewing/discussions) on
[GitHub](https://github.com/freesewing/freesewing)
**Social media**
- 🐦 Twitter: [@freesewing_org](https://twitter.com/freesewing_org)
- 📷 Instagram: [@freesewing_org](https://instagram.com/freesewing_org)
**Places the FreeSewing community hangs out**
- 💬 [Discord](https://discord.freesewing.org/)
- 💬 [Facebook](https://www.facebook.com/groups/627769821272714/)
- 💬 [Reddit](https://www.reddit.com/r/freesewing/)
## License: MIT 🤓
© [Joost De Cock](https://github.com/joostdecock).
See [the license file](https://github.com/freesewing/freesewing/blob/develop/LICENSE) for details.
## Where to get help 🤯
For [Support](https://github.com/freesewing/freesewing/issues/new/choose),
please use the [Issues](https://github.com/freesewing/freesewing/issues) &
[Discussions](https://github.com/freesewing/freesewing/discussions) on
[GitHub](https://github.com/freesewing/freesewing).

35
designs/bibi/build.mjs Normal file
View file

@ -0,0 +1,35 @@
/* This script will build the package with esbuild */
import esbuild from 'esbuild'
import pkg from './package.json' assert { type: 'json' }
// Create banner based on package info
const banner = `/**
* ${pkg.name} | v${pkg.version}
* ${pkg.description}
* (c) ${new Date().getFullYear()} ${pkg.author}
* @license ${pkg.license}
*/`
// Shared esbuild options
const options = {
banner: { js: banner },
bundle: true,
entryPoints: ['src/index.mjs'],
format: 'esm',
outfile: 'dist/index.mjs',
external: ['@freesewing'],
metafile: process.env.VERBOSE ? true : false,
minify: process.env.NO_MINIFY ? false : true,
sourcemap: true,
}
// Let esbuild generate the build
const build = async () => {
const result = await esbuild.build(options).catch(() => process.exit(1))
if (process.env.VERBOSE) {
const info = await esbuild.analyzeMetafile(result.metafile)
console.log(info)
}
}
build()

4
designs/bibi/data.mjs Normal file
View file

@ -0,0 +1,4 @@
// This file is auto-generated | All changes you make will be overwritten.
export const name = '@freesewing/bibi'
export const version = '3.2.0'
export const data = { name, version }

50
designs/bibi/i18n/de.json Normal file
View file

@ -0,0 +1,50 @@
{
"t": "Teagan, das T-Shirt",
"d": "Teagan ist ein Schnittmuster für ein passgenaues T-Shirt.",
"p": {
"back": "Rückseite",
"front": "Vorderseite",
"sleeve": "Ärmel"
},
"s": {
"fullLengthFromHps": "Volle Länge (vom höchsten Schulterpunkt)"
},
"o": {
"draftForHighBust": {
"t": "Entwurf für hohe Büste",
"d": "Zeichnen Sie das Muster für die hohe Büstenmessung (falls vorhanden) statt der (vollen) Truhe. Dies wird zu einem besser angepassten Kleidungsstück für Brustkleidung führen."
},
"sleeveEase": {
"t": "Bequemlichkeitszugabe Ärmel",
"d": "Größe der Bequemlichkeitszugabe an den Ärmeln"
},
"sleeveLength": {
"t": "Ärmellänge",
"d": "Steuert die Länge deiner Ärmel"
},
"necklineBend": {
"t": "Krümmung Halsausschnitt",
"d": "Steuert die Krümmung des Halsausschnitts."
},
"necklineDepth": {
"t": "Ausschnitttiefe",
"d": "Steuert, wie tief der Halsausschnitt fällt."
},
"necklineWidth": {
"t": "Ausschnittbreite",
"d": "Steuert die Breite des Halsausschnitts."
},
"curveToWaist": {
"t": "Fit the waist",
"d": "Whether or not to fit the waist or rahter only fit chest and hips."
},
"curvedWaistEase": {
"t": "Waist ease",
"d": "Ease at the waist (only applies when the waist is fitted)."
},
"hipsEase": {
"t": "Hips ease",
"d": "Ease at the hips."
}
}
}

109
designs/bibi/i18n/en.json Normal file
View file

@ -0,0 +1,109 @@
{
"t": "Bibi block body",
"d": "Bibi is a multifunctional building block for tops",
"p": {
"back": "Back",
"front": "Front",
"sleeve": "Sleeve"
},
"s": {
"dartNo.t": "No bust dart",
"dartNo.d": "Gather fabric instead",
"dartYes.t": "Create a bust dart",
"dartYes.d": "If necessary, draft a bust dart to match side seam lengths",
"sleevesNo.t": "Sleeveless",
"sleevesNo.d": "Draft the pattern without sleeves",
"sleevesYes.t": "With sleeves",
"sleevesYes.d": "Draft the pattern with sleeves",
"fitWaistNo.t": "Don't fit the waist",
"fitWaistNo.d": "Create a loose fit and a more rectangular shape",
"fitWaistYes.t": "Fit the waist",
"fitWaistYes.d": "Create a more body-fitting shape",
"length.underbust.t": "Underbust",
"length.underbust.d": "Create a bralette top that only covers the bust.",
"length.waist.t": "Waist",
"length.waist.d": "Create a crop top.",
"length.hips.t": "Hips",
"length.hips.d": "Create a hip-length top.",
"length.seat.t": "Seat",
"length.seat.d": "Create a seat-length top.",
"length.knee.t": "Knee",
"length.knee.d": "Create a knee-length dress.",
"length.floor.t": "Floor",
"length.floor.d": "Create a floor-length dress. Use a negative length bonus to prevent the garment from actually dragging on the floor."
},
"o": {
"draftForHighBust": {
"t": "Bust adjustment",
"d": "If the pattern should be drafted with a basic full bust adjustment (FBA). This will result in a more fitted garment for people with breasts."
},
"draftForHighBustYes": {
"t": "Draft with bust adjustment",
"d": "Suggested for most people with breasts. This option only has an effect if the optional bust-related measurements are available."
},
"draftForHighBustNo": {
"t": "Draft without bust adjustment",
"d": "Drafts a pattern using the chest measurement only."
},
"cuffEase": {
"t": "Sleeve fullness",
"d": "Controls how wide the sleeves are."
},
"sleeveLength": {
"t": "Sleeve length",
"d": "Controls the length of your sleeves"
},
"necklineBend": {
"t": "Neckline curvature",
"d": "Controls the curvature of the neckline."
},
"necklineDepth": {
"t": "Neckline depth",
"d": "Controls how deep the neck opening plunges down."
},
"necklineWidth": {
"t": "Neckline width",
"d": "Controls the width of the neck opening."
},
"fitWaist": {
"t": "Fit the waist",
"d": "Whether or not to fit the waist."
},
"waistEase": {
"t": "Waist ease",
"d": "Ease at the waist (only applies when the waist is fitted)."
},
"hipsEase": {
"t": "Hips ease",
"d": "Ease at the hips."
},
"seatEase": {
"t": "Seat ease",
"d": "Ease at the seat."
},
"bustEase": {
"t": "Bust ease",
"d": "Ease at the bust. Only applies when bust adjustment is enabled."
},
"flare": {
"t": "Flare",
"d": "How much fabric to use at the bottom hem of the design. Only applies when the design goes below the seat."
},
"dart": {
"t": "Allow bust dart",
"d": "Allow creating a bust dart if this is necessary"
},
"sleeves": {
"t": "Sleeves",
"d": "Draft the pattern with sleeves"
},
"strapWidth": {
"t": "Strap width",
"d": "How wide the shoulder straps are. Only applies when sleeves are disabled."
},
"length": {
"t": "Length",
"d": "Which measurement line to use for the bottom hem. You can do fine adjustments using the bonus length option."
}
}
}

50
designs/bibi/i18n/es.json Normal file
View file

@ -0,0 +1,50 @@
{
"t": "Teagan, camiseta",
"d": "Teagan es un patrón de camiseta entallada.",
"p": {
"back": "Atrás",
"front": "Frente",
"sleeve": "Manga"
},
"s": {
"fullLengthFromHps": "Longitud completa (de HPS)"
},
"o": {
"draftForHighBust": {
"t": "Borrador para alta caída",
"d": "Borra el patrón para la medición alta del polvo (si está disponible) en lugar del cofre (completo). Esto dará lugar a una prenda más ajustada para las personas con senos."
},
"sleeveEase": {
"t": "Manga fácil",
"d": "Cantidad de facilidad de sus mangas"
},
"sleeveLength": {
"t": "Longitud de la manga",
"d": "Controla la longitud de las mangas"
},
"necklineBend": {
"t": "Curvatura neckline",
"d": "Controla la curvatura del cuello."
},
"necklineDepth": {
"t": "Profundidad del cuello",
"d": "Controla la profundidad de la abertura del cuello."
},
"necklineWidth": {
"t": "Neckline width",
"d": "Controla el ancho de la abertura del cuello."
},
"curveToWaist": {
"t": "Fit the waist",
"d": "Whether or not to fit the waist or rahter only fit chest and hips."
},
"curvedWaistEase": {
"t": "Waist ease",
"d": "Ease at the waist (only applies when the waist is fitted)."
},
"hipsEase": {
"t": "Hips ease",
"d": "Ease at the hips."
}
}
}

50
designs/bibi/i18n/fr.json Normal file
View file

@ -0,0 +1,50 @@
{
"t": "T-shirt Teagan",
"d": "Teagan est un T-shirt ajusté.",
"p": {
"back": "Retour",
"front": "Avant",
"sleeve": "Manche"
},
"s": {
"fullLengthFromHps": "Longueur complète (à partir du haut de l'épaule)"
},
"o": {
"draftForHighBust": {
"t": "Tracé pour le buste supérieur",
"d": "Tracer le patron pour la mesure de tour de poitrine supérieure (si disponible) plutôt que la poitrine (pleine). Il en résultera un vêtement plus ajusté pour les personnes qui ont des seins."
},
"sleeveEase": {
"t": "Aisance des manches",
"d": "Quantité d'aisance de vos manches"
},
"sleeveLength": {
"t": "Longueur des manches",
"d": "Contrôle la longueur de vos manches"
},
"necklineBend": {
"t": "Courbure de l'encolure",
"d": "Contrôle la courbure de l'encolure."
},
"necklineDepth": {
"t": "Profondeur de l'encolure",
"d": "Contrôle la profondeur de l'encolure."
},
"necklineWidth": {
"t": "Largeur d'encolure",
"d": "Contrôle la largeur de l'encolure."
},
"curveToWaist": {
"t": "Fit the waist",
"d": "Whether or not to fit the waist or rahter only fit chest and hips."
},
"curvedWaistEase": {
"t": "Waist ease",
"d": "Ease at the waist (only applies when the waist is fitted)."
},
"hipsEase": {
"t": "Hips ease",
"d": "Ease at the hips."
}
}
}

View file

@ -0,0 +1,8 @@
import en from './en.json' assert { type: 'json' }
import de from './de.json' assert { type: 'json' }
import es from './es.json' assert { type: 'json' }
import fr from './fr.json' assert { type: 'json' }
import nl from './nl.json' assert { type: 'json' }
import uk from './uk.json' assert { type: 'json' }
export const i18n = { en, de, es, fr, nl, uk }

50
designs/bibi/i18n/nl.json Normal file
View file

@ -0,0 +1,50 @@
{
"t": "Teagan T-shirt",
"d": "Teagan is een patroon voor een aansluitend t-shirt.",
"p": {
"back": "Achterzijde",
"front": "Voorzijde",
"sleeve": "Mouw"
},
"s": {
"fullLengthFromHps": "Afgewerkte lengte (vanaf HPS)"
},
"o": {
"draftForHighBust": {
"t": "Teken voor hoge buste",
"d": "Teken het patroon voor de hoge bustemaat (indien beschikbaar) in plaats van de volle borstomtrek. Dit heeft een aansluitender kledingstuk als resultaat voor mensen met borsten."
},
"sleeveEase": {
"t": "Overwijdte mouw",
"d": "De hoeveelheid extra ruimte in je mouwen"
},
"sleeveLength": {
"t": "Mouwlengte",
"d": "Bepaalt de lengte van je mouwen"
},
"necklineBend": {
"t": "Curve halslijn",
"d": "Bepaalt de curve van de halsuitsnijding."
},
"necklineDepth": {
"t": "Diepte halsuitsnijding",
"d": "Bepaalt hoe diep de halsopening is."
},
"necklineWidth": {
"t": "Breedte halsuitsnijding",
"d": "Bepaalt hoe breed de halsopening is."
},
"curveToWaist": {
"t": "Fit the waist",
"d": "Whether or not to fit the waist or rahter only fit chest and hips."
},
"curvedWaistEase": {
"t": "Waist ease",
"d": "Ease at the waist (only applies when the waist is fitted)."
},
"hipsEase": {
"t": "Hips ease",
"d": "Ease at the hips."
}
}
}

50
designs/bibi/i18n/uk.json Normal file
View file

@ -0,0 +1,50 @@
{
"t": "Teagan T-shirt",
"d": "Teagan is a fitted T-shirt pattern.",
"p": {
"back": "Back",
"front": "Front",
"sleeve": "Sleeve"
},
"s": {
"fullLengthFromHps": "Full length (from HPS)"
},
"o": {
"draftForHighBust": {
"t": "Draft for high bust",
"d": "Draft the pattern for the high bust measurement (if available) rather than the (full) chest. This will result in a more fitted garment for people with breasts."
},
"sleeveEase": {
"t": "Sleeve ease",
"d": "Amount of ease of your sleeves"
},
"sleeveLength": {
"t": "Sleeve length",
"d": "Controls the length of your sleeves"
},
"necklineBend": {
"t": "Neckline curvature",
"d": "Controls the curvature of the neckline."
},
"necklineDepth": {
"t": "Neckline depth",
"d": "Controls how deep the neck opening plunges down."
},
"necklineWidth": {
"t": "Neckline width",
"d": "Controls the width of the neck opening."
},
"curveToWaist": {
"t": "Fit the waist",
"d": "Whether or not to fit the waist or rahter only fit chest and hips."
},
"curvedWaistEase": {
"t": "Waist ease",
"d": "Ease at the waist (only applies when the waist is fitted)."
},
"hipsEase": {
"t": "Hips ease",
"d": "Ease at the hips."
}
}
}

72
designs/bibi/package.json Normal file
View file

@ -0,0 +1,72 @@
{
"name": "@freesewing/bibi",
"version": "3.2.0",
"description": "A FreeSewing pattern for overlap top",
"author": "Joost De Cock <joost@joost.at> (https://github.com/joostdecock)",
"homepage": "https://freesewing.org/",
"repository": "github:freesewing/freesewing",
"license": "MIT",
"bugs": {
"url": "https://github.com/freesewing/freesewing/issues"
},
"funding": {
"type": "individual",
"url": "https://freesewing.org/patrons/join"
},
"keywords": [
"freesewing",
"design",
"diy",
"fashion",
"made to measure",
"parametric design",
"pattern",
"sewing",
"sewing pattern"
],
"type": "module",
"module": "dist/index.mjs",
"exports": {
".": {
"internal": "./src/index.mjs",
"default": "./dist/index.mjs"
}
},
"scripts": {
"build": "node build.mjs",
"build:all": "yarn build",
"clean": "rimraf dist",
"mbuild": "NO_MINIFY=1 node build.mjs",
"symlink": "mkdir -p ./node_modules/@freesewing && cd ./node_modules/@freesewing && ln -s -f ../../../* . && cd -",
"test": "npx mocha tests/*.test.mjs",
"vbuild": "VERBOSE=1 node build.mjs",
"lab": "cd ../../sites/lab && yarn start",
"tips": "node ../../scripts/help.mjs",
"lint": "npx eslint 'src/**' 'tests/*.mjs'",
"prettier": "npx prettier --write 'src/*.mjs' 'tests/*.mjs'",
"testci": "NODE_OPTIONS=\"--conditions=internal\" npx mocha tests/*.test.mjs --reporter ../../tests/reporters/terse.js",
"wbuild": "node build.mjs",
"wbuild:all": "yarn wbuild"
},
"peerDependencies": {
"@freesewing/brian": "3.2.0",
"@freesewing/core": "3.2.0"
},
"devDependencies": {
"@freesewing/models": "3.2.0",
"@freesewing/plugin-timing": "3.2.0",
"chai": "5.1.0",
"mocha": "10.3.0"
},
"files": [
"dist/*",
"README.md"
],
"publishConfig": {
"access": "public",
"tag": "latest"
},
"engines": {
"node": ">= 18.17.0 <22"
}
}

267
designs/bibi/src/back.mjs Normal file
View file

@ -0,0 +1,267 @@
import { base } from '@freesewing/brian'
import {
adjustSidePoints,
constructBackHem,
constructBackPoints,
correctArmHole,
createArmHoles,
plotSideLineMeasurements,
} from './shared.mjs'
import { sleeve } from './sleeve.mjs'
export const back = {
name: 'bibi.back',
from: base,
measurements: ['hips', 'waist', 'hpsToWaistBack', 'chest', 'seat', 'seatBack', 'waistToSeat'],
optionalMeasurements: ['bustSpan', 'highBust', 'waistToUnderbust', 'waistToKnee', 'waistToFloor'],
hide: { from: true },
after: sleeve,
options: {
// Brian overrides
s3Collar: 0,
s3Armhole: 0,
brianFitSleeve: true,
brianFitCollar: false,
bicepsEase: 0.05,
collarEase: 0,
shoulderSlopeReduction: 0,
sleeveWidthGuarantee: 0.85,
frontArmholeDeeper: 0.01,
legacyArmholeDepth: false,
shoulderEase: { pct: 0, min: -2, max: 6, menu: 'fit' },
// Note: we reuse Brian's cuff ease as "armhole fullness"
cuffEase: {
pct: 20,
min: 0,
max: 200,
menu: (settings, mergedOptions) =>
mergedOptions.sleeves === false ? false : 'style.sleeves',
},
armholeDepth: {
pct: 2,
min: -10,
max: 50,
menu: (settings, mergedOptions) =>
mergedOptions?.legacyArmholeDepth ? false : 'style.sleeves',
},
lengthBonus: { pct: 0, min: -30, max: 30, menu: 'style.length' },
draftForHighBust: { bool: true, menu: 'fit' },
// Bibi specific
fitWaist: { bool: true, menu: 'fit', order: 'EBA' },
waistEase: {
pct: 1,
min: -10,
max: 20,
menu: (settings, mergedOptions) => (mergedOptions.fitWaist ? 'fit' : false),
order: 'EBB',
},
hipsEase: { pct: 2, min: -5, max: 50, menu: 'fit', order: 'ECA' },
seatEase: { pct: 2, min: -5, max: 50, menu: 'fit', order: 'EDA' },
chestEase: { pct: 2, min: -5, max: 25, menu: 'fit', order: 'EAB' },
length: {
dflt: 'seat',
list: ['underbust', 'waist', 'hips', 'seat', 'knee', 'floor'],
menu: 'style.length',
},
flare: {
pct: 5,
min: 0,
max: 150,
menu: (settings, mergedOptions) =>
(mergedOptions.length === 'seat' && mergedOptions.lengthBonus > 0) ||
mergedOptions.length === 'knee' ||
mergedOptions.length === 'floor'
? 'style.length'
: false,
},
strapWidth: {
pct: 40,
min: 5,
max: 100,
menu: (settings, mergedOptions) => (mergedOptions.sleeves ? false : 'style.sleeves'),
},
sleeves: { bool: true, menu: 'style.sleeves' },
backNeckCutout: { pct: 10, min: 6, max: 30, menu: 'style' },
},
draft: bibiBack,
}
function bibiBack({
store,
sa,
Point,
points,
Path,
paths,
Snippet,
snippets,
options,
measurements,
macro,
complete,
utils,
part,
}) {
// Hide Brian paths
for (let key of Object.keys(paths)) paths[key].hide()
// Re-use points for deeper armhole at the front
points.armholePitchCp1 = points.backArmholePitchCp1
points.armholePitch = points.backArmholePitch
points.armholePitchCp2 = points.backArmholePitchCp2
// clean up some unnecessary points
delete points.frontArmholePitchCp1
delete points.frontArmholePitch
delete points.frontArmholePitchCp2
delete points.backArmholePitchCp1
delete points.backArmholePitch
delete points.backArmholePitchCp2
constructBackPoints(points, Point, measurements, options)
adjustSidePoints(points, options)
correctArmHole(points, paths, Path, options, utils)
//
// points.cfNeck = new Point(0, options.necklineDepth * measurements.hpsToWaistFront)
// points.cfNeckCp1 = points.cfNeck.shift(0, points.neck.x * options.necklineBend * 2)
// points.neck = points.hps.shiftFractionTowards(
// points.shoulder,
// options.necklineWidth * (1 - options.strapWidth)
// )
// points.neckCp2 = points.neck
// .shiftTowards(points.shoulder, points.neck.dy(points.cfNeck) * (0.2 + options.necklineBend))
// .rotate(-90, points.neck)
//
// paths.frontNeck = new Path()
// .move(points.neck)
// .curve(points.neckCp2, points.cfNeckCp1, points.cfNeck)
points.cbHem = new Point(0, points.cbWaist.y + measurements.waistToHips * options.lengthBonus)
constructBackHem(points, measurements, options, Point, paths, Path)
store.set('backSideSeamLength', paths.sideSeam.length())
let strapWidth = options.sleeves ? 0 : options.strapWidth
points.neck = points.hps.shiftFractionTowards(
points.shoulder,
options.necklineWidth * (1 - strapWidth)
)
points.cbNeck = new Point(
0,
points.neck.y + options.backNeckCutout * points.neck.dy(points.armhole)
)
// points.cbNeckCp1 = points.cfNeck.shift(0, points.neck.x * options.necklineBend * 2)
// points.neckCp2 = points.neck
// .shiftTowards(points.shoulder, points.neck.dy(points.cfNeck) * (0.2 + options.necklineBend))
// .rotate(-90, points.neck)
points.neckCp2 = points.neck.shiftTowards(points.shoulder, 10).rotate(-90, points.neck)
points.neckCp2 = utils.beamIntersectsY(points.neck, points.neckCp2, points.cbNeck.y)
points.neckCp1 = new Point(points.neck.x / 3, points.cbNeck.y)
paths.backNeck = new Path()
.move(points.neck)
.curve(points.neckCp2, points.neckCp1, points.cbNeck)
.addClass('fabric')
createArmHoles(options, store, points, paths, Path, snippets, Snippet, strapWidth, 'bnotch')
paths.centerLine = new Path().move(points.cbNeck).line(points.cbHem).addClass('fabric')
store.set(
'gatherAreaStart',
Math.min(points.armhole.dy(points.cbWaist) / 10, paths.sideSeam.length() * 0.1)
)
store.set(
'gatherAreaLength',
Math.min(paths.sideSeam.length() * 0.8, points.armhole.dy(points.cbWaist) * 0.5)
)
const reverse = paths.sideSeam.reverse()
snippets.gatherAreaStart = new Snippet('notch', reverse.shiftAlong(store.get('gatherAreaStart')))
snippets.gatherAreaBack = new Snippet(
'notch',
reverse.shiftAlong(store.get('gatherAreaStart') + store.get('gatherAreaLength'))
)
if (sa) {
paths.sa = new Path()
.move(points.cbHem)
.join(paths.hem.offset(sa * 3))
.join(paths.sideSeam.offset(sa))
.join(paths.armhole.offset(sa))
.join(paths.shoulder.offset(sa))
.join(paths.backNeck.offset(sa))
.line(points.cbNeck)
.attr('class', 'fabric sa')
}
macro('cutonfold', {
from: points.cfNeck,
to: points.cbHem,
grainline: true,
})
points.scaleboxAnchor = points.logo.shift(-90, 10)
macro('scalebox', {
at: points.scaleboxAnchor,
})
snippets.logo = new Snippet('logo', points.logo)
macro('title', { at: points.title, nr: 2, title: 'back' })
if (complete && points.hem.y > points.waist.y)
paths.waist = new Path().move(points.cbWaist).line(points.waist).attr('class', 'help')
macro('pd', {
id: 'pArmhole',
path: paths.armhole.reverse(),
d: -1 * sa - 15,
})
macro('pd', {
id: 'pNeck',
path: paths.backNeck.reverse(),
d: -1 * sa - 15,
})
macro('pd', {
id: 'pShoulder',
path: paths.shoulder.reverse(),
d: -1 * sa - 15,
})
macro('hd', {
id: 'wAtHem',
from: points.cbHem,
to: points.hem,
y: points.cbHem.y + sa * 2.5 + 15,
})
macro('vd', {
id: 'hHemToNeck',
from: points.cbHem,
to: points.cbNeck,
x: points.cbHem.x - sa - 15,
})
macro('vd', {
id: 'hNeckToArmhole',
from: points.neck,
to: points.armhole,
x: points.armhole.x + sa + 15,
})
plotSideLineMeasurements(points, paths.sideSeam, utils, macro)
return part
}

336
designs/bibi/src/front.mjs Normal file
View file

@ -0,0 +1,336 @@
import { base } from '@freesewing/brian'
import { back } from './back.mjs'
import {
adjustSidePoints,
calculateFba,
constructFrontHem,
constructFrontPoints,
correctArmHole,
createArmHoles,
plotSideLineMeasurements,
} from './shared.mjs'
export const front = {
name: 'bibi.front',
from: base,
measurements: [
'hips',
'waist',
'hpsToWaistBack',
'chest',
'hpsToWaistFront',
'seat',
'seatBack',
'waistToSeat',
'hpsToBust',
],
optionalMeasurements: ['bustSpan', 'highBust', 'waistToUnderbust', 'waistToKnee', 'waistToFloor'],
hide: { from: true },
plugins: [],
after: back,
options: {
dart: { bool: false, menu: 'fit' },
bustEase: {
pct: 0,
min: 0,
max: 10,
menu: (settings, mergedOptions) => (mergedOptions.draftForHighBust ? 'fit' : false),
order: 'EAA',
},
necklineDepth: { pct: 25, min: 20, max: 60, menu: 'style' },
necklineWidth: { pct: 50, min: 10, max: 90, menu: 'style' },
necklineBend: { pct: 50, min: 0, max: 70, menu: 'style' },
},
draft: bibiFront,
}
function bibiFront({
store,
sa,
Point,
points,
Path,
paths,
Snippet,
snippets,
options,
measurements,
macro,
complete,
utils,
part,
}) {
// Hide Brian paths
for (let key of Object.keys(paths)) paths[key].hide()
// Re-use points for deeper armhole at the front
points.armholePitchCp1 = points.frontArmholePitchCp1
points.armholePitch = points.frontArmholePitch
points.armholePitchCp2 = points.frontArmholePitchCp2
// clean up some unnecessary points
delete points.frontArmholePitchCp1
delete points.frontArmholePitch
delete points.frontArmholePitchCp2
delete points.backArmholePitchCp1
delete points.backArmholePitch
delete points.backArmholePitchCp2
delete points.cbHem
constructFrontPoints(points, Point, measurements, options)
// FBA
points.originalChest = points.chest
points.originalArmhole = points.armhole
points.originalArmholeCp2 = points.armholeCp2
let fba
if (options.draftForHighBust && measurements.bustSpan && measurements.highBust) {
fba = calculateFba(
points.neck.shiftFractionTowards(points.shoulder, 0.5),
points.bust,
points.armholeHollow,
(measurements.bust * (1 + options.bustEase) - measurements.chest) / 2,
Point
)
points.bustOffset = fba.bustOffset
points.sideOffset0 = fba.sideOffset0
points.sideOffset1 = fba.sideOffset1
points.chest = fba.rotateLower(points.chest)
points.armhole = fba.rotateUpper(points.armhole)
points.armholeCp2 = fba.rotateUpper(points.armholeCp2)
points.armholeHollowCp1 = fba.rotateUpper(points.armholeHollowCp1)
points.armholeHollow = fba.rotateUpper(points.armholeHollow)
points.armholeHollowCp2 = fba.rotateUpper(points.armholeHollowCp2)
points.armholePitchCp1 = fba.rotateUpper(points.armholePitchCp1)
points.armholePitch = fba.rotateUpper(points.armholePitch)
points.armholePitchCp2 = fba.rotateUpper(points.armholePitchCp2)
points.shoulderCp1 = fba.rotateUpper(points.shoulderCp1)
points.shoulder = fba.rotateUpper(points.shoulder)
}
adjustSidePoints(points, options)
correctArmHole(points, paths, Path, options, utils)
points.cfNeck = new Point(0, options.necklineDepth * measurements.hpsToWaistFront)
points.cfNeckCp1 = points.cfNeck.shift(0, points.neck.x * options.necklineBend * 2)
let strapWidth = options.sleeves ? 0 : options.strapWidth
points.neck = points.hps.shiftFractionTowards(
points.shoulder,
options.necklineWidth * (1 - strapWidth)
)
points.neckCp2 = points.neck
.shiftTowards(points.shoulder, points.neck.dy(points.cfNeck) * (0.2 + options.necklineBend))
.rotate(-90, points.neck)
paths.frontNeck = new Path()
.move(points.neck)
.curve(points.neckCp2, points.cfNeckCp1, points.cfNeck)
.addClass('fabric')
constructFrontHem(points, measurements, options, Point, paths, Path)
store.set('frontSideSeamLength', paths.sideSeam.length())
let frontLength = store.get('frontSideSeamLength')
let backLength = store.get('backSideSeamLength') ?? 0
let dartLength = frontLength - backLength
const constructDart = (path, tip, dartLength) => {
let length = path.length()
dartLength = Math.max(0, Math.min(dartLength, length / 2))
let gatherArea = (store.get('gatherAreaLength') ?? 0) + dartLength
let offset = length - (store.get('gatherAreaStart') ?? 0) - gatherArea
let startSplit = path.shiftAlong(offset)
let startDartAlpha = path.shiftAlong(offset + (gatherArea - dartLength) * 0.5)
let endDartAlpha = path.shiftAlong(offset + (gatherArea + dartLength) * 0.5)
let endSplit = path.shiftAlong(offset + gatherArea)
let tmp = path.split(startSplit)
let pathBefore = tmp[0]
tmp = tmp[1].split(endSplit)
let pathGather = tmp[0]
let pathAfter = tmp[1]
let angleBefore = path.angleAt(startSplit)
let angleAfter = path.angleAt(endSplit)
let cpBefore = startSplit.shift(angleBefore, dartLength / 3)
let cpAfter = endSplit.shift(angleAfter, -dartLength / 3)
let dartDist = Math.max(tip.dist(startDartAlpha), tip.dist(endDartAlpha))
let startDart = tip.shiftTowards(startDartAlpha, dartDist)
let endDart = tip.shiftTowards(endDartAlpha, dartDist)
let dartMid = startDart.shiftFractionTowards(endDart, 0.5)
let tipShifted = tip.shiftFractionTowards(dartMid, 0.25)
let dartCpStart = tipShifted
.shiftFractionTowards(dartMid, 0.25)
.shiftFractionTowards(startDart, 0.25)
let dartCpEnd = tipShifted
.shiftFractionTowards(dartMid, 0.25)
.shiftFractionTowards(endDart, 0.25)
const dartAngleMain = startDart.angle(endDart)
const dartAngleBefore = startDartAlpha.angle(endDartAlpha)
const dartAngle = dartAngleBefore * 2 - dartAngleMain
let dartInnerAngle = tipShifted.angle(endDart) - tipShifted.angle(startDart)
if (dartInnerAngle < -180) dartInnerAngle += 360
let cpSplitStart = startDart.shift(dartAngle - dartInnerAngle / 2, -dartLength / 4)
let cpSplitEnd = endDart.shift(dartAngle + dartInnerAngle / 2, dartLength / 4)
return {
beforeDart: pathBefore.clone().curve(cpBefore, cpSplitStart, startDart),
dart: new Path().move(startDart)._curve(dartCpStart, tipShifted).curve_(dartCpEnd, endDart),
afterDart: new Path().move(endDart).curve(cpSplitEnd, cpAfter, endSplit).join(pathAfter),
dartMiddle: new Path().move(dartMid).line(tipShifted),
startGather: startSplit,
endGather: endSplit,
startDart: startDart,
endDart: endDart,
dartTip: tipShifted,
gatherArea: gatherArea,
gatherPath: pathGather,
dartLength: dartLength,
offset: offset,
}
}
const dart = constructDart(paths.sideSeam, points.bust, dartLength)
store.set('dart', dart)
points.startGather = dart.startGather
points.endGather = dart.endGather
points.startDart = dart.startDart
points.endDart = dart.endDart
points.dartTip = dart.dartTip
paths.sideSeamWithDart = paths.originalSideSeam = paths.sideSeam.clone().hide()
if (options.dart && dartLength > dart.gatherArea / 5) {
paths.sideSeam1 = dart.beforeDart.hide()
paths.dart = dart.dart.addClass('fabric')
paths.sideSeam2 = dart.afterDart.hide()
if (complete) {
paths.dartMiddle = dart.dartMiddle.addClass('help')
}
paths.sideSeam = paths.sideSeam1.clone().combine(paths.sideSeam2).addClass('fabric')
paths.sideSeamWithDart = paths.sideSeam1.clone().join(paths.dart).join(paths.sideSeam2).hide()
}
if (complete) {
snippets.startGather = new Snippet('notch', points.startGather)
snippets.endGather = new Snippet('notch', points.endGather)
if (dartLength > dart.gatherArea / 10 && !paths.dart) {
paths.gatherPath = new Path()
.move(points.startGather)
.join(dart.gatherPath.offset(-20))
.line(points.endGather)
.addClass('dashed various')
.addText('gather', 'center various help')
}
}
createArmHoles(options, store, points, paths, Path, snippets, Snippet, strapWidth)
paths.centerLine = new Path().move(points.cfNeck).line(points.cfHem).addClass('fabric')
macro('title', { at: points.title, nr: 1, title: 'front' })
macro('cutonfold', {
from: points.cfNeck,
to: points.cfHem,
grainline: true,
})
if (sa) {
if (paths.sideSeam1)
paths.sa = new Path()
.move(points.cfHem)
.join(paths.hem.offset(sa * 3))
.join(paths.sideSeam1.offset(sa))
.join(paths.sideSeam2.offset(sa))
.join(paths.armhole.offset(sa))
.join(paths.shoulder.offset(sa))
.join(paths.frontNeck.offset(sa))
.line(points.cfNeck)
.attr('class', 'fabric sa')
else
paths.sa = new Path()
.move(points.cfHem)
.join(paths.hem.offset(sa * 3))
.join(paths.sideSeam.offset(sa))
.join(paths.armhole.offset(sa))
.join(paths.shoulder.offset(sa))
.join(paths.frontNeck.offset(sa))
.line(points.cfNeck)
.attr('class', 'fabric sa')
}
// Waist line
if (complete && points.hem.y > points.waist.y)
paths.waist = new Path().move(points.cfWaist).line(points.waist).attr('class', 'help')
macro('pd', {
id: 'pArmhole',
path: paths.armhole.reverse(),
d: -1 * sa - 15,
})
macro('pd', {
id: 'pNeck',
path: paths.frontNeck.reverse(),
d: -1 * sa - 15,
})
macro('pd', {
id: 'pShoulder',
path: paths.shoulder.reverse(),
d: -1 * sa - 15,
})
macro('hd', {
id: 'wAtHem',
from: points.cfHem,
to: points.hem,
y: points.hem.y + sa * 2.5 + 15,
})
macro('vd', {
id: 'hHemToNeck',
from: points.cfHem,
to: points.cfNeck,
x: points.cfHem.x - sa - 15,
})
macro('vd', {
id: 'hNeckToArmhole',
from: points.neck,
to: points.armhole,
x: points.armhole.x + sa + 15,
})
if (paths.dart) {
macro('vd', {
id: 'hDart',
from: points.endDart,
to: points.startDart,
x: points.endDart.x + sa + 15,
})
macro('ld', {
id: 'pDartStart',
from: points.dartTip,
to: points.startDart,
d: -sa - 15,
})
macro('ld', {
id: 'pDartEnd',
from: points.dartTip,
to: points.endDart,
d: sa + 15,
})
snippets.bustPoint = new Snippet('notch', points.bust)
}
plotSideLineMeasurements(points, paths.sideSeamWithDart, utils, macro)
return part
}

View file

@ -0,0 +1,49 @@
import { Design, mergeI18n } from '@freesewing/core'
import { data } from '../data.mjs'
import { i18n as brianI18n } from '@freesewing/brian'
import { i18n as bibiI18n } from '../i18n/index.mjs'
import { front } from './front.mjs'
import { back } from './back.mjs'
import { sleeve } from './sleeve.mjs'
import {
constructFrontPoints,
constructBackPoints,
calculateFba,
correctArmHole,
constructSideSeam,
adjustSidePoints,
constructBackHem,
constructFrontHem,
createArmHoles,
plotSideLineMeasurements,
} from './shared.mjs'
// Setup our new design
const Bibi = new Design({
data,
parts: [sleeve, back, front],
})
// Merge translations
const i18n = mergeI18n([brianI18n, bibiI18n], {
o: { drop: ['sleeveLengthBonus'] },
})
// Named exports
export {
front,
back,
sleeve,
constructFrontPoints,
constructBackPoints,
calculateFba,
correctArmHole,
constructSideSeam,
adjustSidePoints,
constructBackHem,
constructFrontHem,
createArmHoles,
plotSideLineMeasurements,
Bibi,
i18n,
}

454
designs/bibi/src/shared.mjs Normal file
View file

@ -0,0 +1,454 @@
function createLowerPoints(points, measurements, options, prefix) {
// These lengths are typically not critical, so use a rough estimate if not given
// We don't want the user to need these measurements, as this design can also be used for shorter tops
if (typeof measurements.waistToKnee !== 'undefined') {
points[prefix + 'Knee'] = points[prefix + 'Waist'].translate(0, measurements.waistToKnee)
} else {
points[prefix + 'Knee'] = points[prefix + 'Waist'].translate(
0,
measurements.hpsToWaistBack * 1.5
)
}
if (typeof measurements.waistToFloor !== 'undefined') {
points[prefix + 'Floor'] = points[prefix + 'Waist'].translate(0, measurements.waistToFloor)
} else {
points[prefix + 'Floor'] = points[prefix + 'Waist'].translate(
0,
measurements.hpsToWaistBack * 2.5
)
}
points.sideTarget = points[prefix + 'Knee'].translate(points.seatBase.x * (1 + options.flare), 0)
}
export function constructFrontPoints(points, Point, measurements, options) {
points.cfBust = new Point(0, measurements.hpsToBust)
if (measurements.bustSpan) {
points.bust = new Point(measurements.bustSpan / 2, measurements.hpsToBust)
} else {
// Very rough estimate, but only used for potential dart construction
points.bust = new Point(measurements.chest / 8, measurements.hpsToBust)
}
points.chest = new Point(
(measurements.chest * (1 + options.chestEase)) / 4,
measurements.hpsToBust
)
points.cbWaist = new Point(0, measurements.hpsToWaistBack)
points.cfWaist = new Point(0, measurements.hpsToWaistFront)
points.waist = new Point((measurements.waist * (1 + options.waistEase)) / 4, points.cfWaist.y)
points.cfArmhole = new Point(
0,
points.cbWaist.y -
measurements.waistToArmpit * (1 - options.armholeDepth - options.bicepsEase / 2)
)
points.armhole = new Point(points.armhole.x, points.cfArmhole.y)
points.armholeCp2 = points.armhole.shift(180, points._tmp1.dx(points.armhole) / 4)
points.cfUnderbust = new Point(
0,
points.cfWaist.y - (measurements.waistToUnderbust ?? measurements.hpsToWaistBack / 6)
)
points.underbust = new Point(
(measurements.chest * (1 + options.chestEase)) / 4,
points.cfUnderbust.y
)
points.cfHips = new Point(0, points.cfWaist.y + measurements.waistToHips)
points.hips = new Point((measurements.hips * (1 + options.hipsEase)) / 4, points.cfHips.y)
const seatFront = measurements.seat - measurements.seatBack
const seatExtra = (measurements.seatBack - seatFront) / 4
points.cfSeat = new Point(0, points.cfWaist.y + measurements.waistToSeat)
points.seatBase = new Point((measurements.seat * (1 + options.seatEase)) / 4, points.cfSeat.y)
points.seat = seatAdjustment(points.seatBase, points.hips, -seatExtra)
// points.cfSeat = points.cfSeat.shift(-90, -seatExtra * 2)
createLowerPoints(points, measurements, options, 'cf')
}
export function constructBackPoints(points, Point, measurements, options) {
points.chest = new Point(
(measurements.chest * (1 + options.chestEase)) / 4,
measurements.hpsToBust
)
points.cbWaist = new Point(0, measurements.hpsToWaistBack)
points.waist = new Point((measurements.waist * (1 + options.waistEase)) / 4, points.cbWaist.y)
points.cbUnderbust = new Point(
0,
points.cbWaist.y - (measurements.waistToUnderbust ?? measurements.hpsToWaistBack / 6)
)
points.underbust = new Point(
(measurements.chest * (1 + options.chestEase)) / 4,
points.cbUnderbust.y
)
points.cbHips = new Point(0, points.cbWaist.y + measurements.waistToHips)
points.hips = new Point((measurements.hips * (1 + options.hipsEase)) / 4, points.cbHips.y)
points.cbSeat = new Point(0, points.cbWaist.y + measurements.waistToSeat)
const seatFront = measurements.seat - measurements.seatBack
const seatExtra = (measurements.seatBack - seatFront) / 4
points.seatBase = new Point((measurements.seat * (1 + options.seatEase)) / 4, points.cbSeat.y)
points.seat = seatAdjustment(points.seatBase, points.hips, seatExtra)
// points.cbSeat = points.cbSeat.shift(-90, seatExtra * 2)
createLowerPoints(points, measurements, options, 'cb')
}
function seatAdjustment(seatBase, anchor, seatExtra) {
const r = anchor.dist(seatBase)
return seatBase.rotate((seatExtra / r) * 90, anchor)
}
export function calculateFba(anchor, bust, side, dx, Point) {
const r = anchor.dist(bust)
const a0 = Math.asin((bust.x - anchor.x) / r)
let a1 = Math.asin((bust.x - anchor.x + dx) / r)
if (isNaN(a1)) {
a1 = Math.PI / 2
}
const bustOffset = new Point(anchor.x + r * Math.sin(a1), anchor.y + r * Math.cos(a1))
const dy = bustOffset.y - bust.y
const b = a1 - a0
const rotateUpper = (point) => point.rotate((b / Math.PI) * 180, anchor)
const sideOffset0 = rotateUpper(side)
const sideOffset1 = side.translate(dx, dy)
const c = sideOffset1.angle(bust) - sideOffset0.angle(bust)
const rotateLower = (point) => point.translate(dx, dy).rotate(-c, bust)
return {
dx: dx,
dy: dy,
rotateUpper: rotateUpper,
rotateLower: rotateLower,
bustOffset: bustOffset,
sideOffset0: sideOffset0,
sideOffset1: sideOffset1,
}
}
export function correctArmHole(points, paths, Path, options, utils) {
points.armholeCp1 = points.chest
if (!options.sleeves && points.armhole.y > points.chest.y) {
points.armhole = utils.beamIntersectsY(points.chest, points.waist, points.armhole.y)
points.armholeCp2 = points.armhole.shift(180, points._tmp1.dx(points.armhole) / 4)
}
if (points.armhole.y > points.armholeCp1.y * 0.8) {
const frac = Math.min(
1,
(points.armhole.y - points.armholeCp1.y * 0.8) / (0.2 * points.armholeCp1.y)
)
points.armholeCp1 = points.chest.shiftFractionTowards(
points.armholeCp2.rotate(90, points.armhole),
frac
)
}
}
function extendSideLine(points, intersectionY) {
const fraction = (intersectionY - points.seat.y) / points.seat.dy(points.sideTarget)
return points.seat.shiftFractionTowards(points.sideTarget, fraction)
}
export function constructSideSeam(Path, Point, points, height, bottomSmoothness) {
const base = new Path()
.move(points.armhole)
.curve(points.armholeCp1, points.waistCp2, points.waist)
.smurve(points.hipsCp2, points.hips)
.smurve(points.seatCp2, points.seat)
const intersectionY = Math.min(height, points.seat.y) - bottomSmoothness
let bottom = base.intersectsY(height)[0]
if (!bottom) {
// below seat
bottom = extendSideLine(points, height)
}
points.hem = bottom
let intersection = base.intersectsY(intersectionY)[0]
if (!intersection) {
if (intersectionY >= points.seat.y) {
// below seat
intersection = extendSideLine(points, intersectionY)
} else {
//above armhole
intersection = points.armhole
}
}
bottom.x = (bottom.x + intersection.x) / 2 // creates a smoother bottom as the bottom is vertical
points.intersection = intersection
const angle = base.angleAt(intersection)
if (!angle) {
return base
}
const intersectionCp1 = intersection.shift(angle, bottomSmoothness * 0.3)
const intersectionCp2 = new Point(bottom.x, bottom.y - bottomSmoothness * 0.3)
points.intersectionCp1 = intersectionCp1
points.intersectionCp2 = intersectionCp2
let result = base.split(intersection)[0]
if (!result.curve) {
result = new Path().move(points.armhole)
}
return result.curve(intersectionCp1, intersectionCp2, bottom).reverse()
}
export function adjustSidePoints(points, options) {
// Remove waist fitting if option is disabled
if (!options.fitWaist || points.waist.x > points.armhole.x) {
if (points.waist.x < points.armhole.x) {
points.waist.x = points.armhole.x
}
points.waistCp2 = points.waist.shiftFractionTowards(points.armhole, 0.2)
if (points.hips.x < points.waist.x) {
points.hips.x = points.waist.x
}
}
// prevent barrel shape
if (points.hips.x < points.waist.x) {
points.hips.x = points.waist.x
}
// prevent excessive hips narrowing
if (points.hips.x < (points.waist.x + points.seat.x) / 2) {
points.hips.x = (points.waist.x + points.seat.x) / 2
}
// prevent smaller seat than hips
if (points.seat.x < points.hips.x) {
points.seat.x = points.hips.x
}
// prevent excessive waist narrowing
if (points.waist.x < 2 * points.hips.x - points.seat.x) {
points.waist.x = 2 * points.hips.x - points.seat.x
}
// curve points
points.waistCp2 = points.waist.shift(90, points.armhole.dy(points.waist) * 0.2)
points.seatCp2 = points.seat.shift(90, points.hips.dy(points.seat) * 0.3)
points.hipsCp2 = points.waist.shiftFractionTowards(points.hips, 0.6)
}
function getBottomSmoothness(bottom, points) {
return (Math.min(bottom, points.seat.y) - points.armhole.y) * 0.3
}
export function constructBackHem(points, measurements, options, Point, paths, Path) {
let centerPoint
// Extra length for butt
let extraBackLength = 0
let bonusLengthMeasurement = measurements.hpsToWaistBack
switch (options.length) {
case 'underbust':
centerPoint = points.cbUnderbust
bonusLengthMeasurement = measurements.waistToUnderbust
break
case 'waist':
centerPoint = points.cbWaist
break
case 'hips':
centerPoint = points.cbHips
break
case 'seat':
centerPoint = points.cbSeat
extraBackLength = (measurements.seatBack - measurements.seat / 2) / 2
bonusLengthMeasurement *= 2
break
case 'knee':
centerPoint = points.cbKnee
extraBackLength = (measurements.seatBack - measurements.seat / 2) / 2
bonusLengthMeasurement *= 3
break
case 'floor':
centerPoint = points.cbFloor
extraBackLength = (measurements.seatBack - measurements.seat / 2) / 2
bonusLengthMeasurement *= 3
}
if (!centerPoint) {
centerPoint = points.cbSeat
}
const hemBottom = centerPoint.y + bonusLengthMeasurement * options.lengthBonus
points.cbHem = new Point(
0,
centerPoint.y + bonusLengthMeasurement * options.lengthBonus + extraBackLength
)
points.midHem = new Point(points.hem.x * 0.66, points.cbHem.y)
paths.sideSeam = constructSideSeam(
Path,
Point,
points,
hemBottom,
getBottomSmoothness(hemBottom, points)
).addClass('fabric')
paths.hem = new Path()
.move(points.cbHem)
.curve(points.midHem, points.midHem, points.hem)
.addClass('fabric')
}
export function constructFrontHem(points, measurements, options, Point, paths, Path) {
let centerPoint
let bonusLengthMeasurement = measurements.hpsToWaistBack
switch (options.length) {
case 'underbust':
centerPoint = points.cfUnderbust
bonusLengthMeasurement = measurements.waistToUnderbust
break
case 'waist':
centerPoint = points.cfWaist
break
case 'hips':
centerPoint = points.cfHips
break
case 'seat':
centerPoint = points.cfSeat
bonusLengthMeasurement *= 2
break
case 'knee':
centerPoint = points.cfKnee
bonusLengthMeasurement *= 3
break
case 'floor':
centerPoint = points.cfFloor
bonusLengthMeasurement *= 3
}
if (!centerPoint) {
centerPoint = points.cfSeat
}
const hemBottom = centerPoint.y + bonusLengthMeasurement * options.lengthBonus
points.cfHem = new Point(0, centerPoint.y + bonusLengthMeasurement * options.lengthBonus)
points.midHem = new Point(points.hem.x * 0.66, points.cfHem.y)
paths.sideSeam = constructSideSeam(
Path,
Point,
points,
hemBottom,
getBottomSmoothness(hemBottom, points)
).addClass('fabric')
paths.hem = new Path()
.move(points.cfHem)
.curve(points.midHem, points.midHem, points.hem)
.addClass('fabric')
}
export function createArmHoles(
options,
store,
points,
paths,
Path,
snippets,
Snippet,
strapWidth,
notchType = 'notch'
) {
if (options.sleeves) {
if (store.get('capSleeves')) {
const sleeveCapFactor = (options.sleeveLength + 0.2) * 4
const capLength = sleeveCapFactor * store.get('sleeveCapHeight')
console.log('caplength', capLength)
points.sleeveCap = points.shoulder.shift(points.neck.angle(points.shoulder) - 15, capLength)
points.sleeveCapStart = points.shoulder.shift(
points.neck.angle(points.shoulder),
capLength * -0.2
)
points.sleeveCapCp = points.sleeveCap
.shiftTowards(points.armholePitchCp1, capLength * 0.2)
.rotate(-90, points.sleeveCap)
paths.shoulder = new Path()
.move(points.sleeveCap)
.curve(points.sleeveCapCp, points.shoulder, points.sleeveCapStart)
.line(points.neck)
.addClass('fabric')
paths.armhole = new Path()
.move(points.armhole)
.curve(points.armholeCp2, points.armholeHollowCp1, points.armholeHollow)
.curve(points.armholeHollowCp2, points.armholePitchCp1, points.sleeveCap)
.addClass('fabric')
} else {
paths.shoulder = new Path().move(points.shoulder).line(points.neck).addClass('fabric')
paths.armhole = new Path()
.move(points.armhole)
.curve(points.armholeCp2, points.armholeHollowCp1, points.armholeHollow)
.curve(points.armholeHollowCp2, points.armholePitchCp1, points.armholePitch)
.curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder)
.addClass('fabric')
snippets.armholePitchNotch = new Snippet(notchType, points.armholePitch)
}
} else {
points.strapTop = points.neck.shift(
points.shoulder.angle(points.hps),
-strapWidth * points.shoulder.dist(points.hps)
)
points.strapTopCp1 = points.strapTop
.shiftTowards(
points.hps,
0.8 * points.strapTop.dy(points.armhole) * (0.2 + options.necklineBend)
)
.rotate(90, points.strapTop)
paths.armhole = new Path()
.move(points.armhole)
.curve(points.armholeCp2, points.armholeHollowCp1, points.armholeHollow)
.curve(points.armholeHollowCp2, points.strapTopCp1, points.strapTop)
.addClass('fabric')
paths.shoulder = new Path().move(points.strapTop).line(points.neck).addClass('fabric')
}
}
export function plotSideLineMeasurements(points, sideSeam, utils, macro) {
const offsets = {
seat: points.seat.y,
hips: points.hips.y,
waist: points.waist.y,
underbust: points.underbust.y,
dart: points.dartTip?.y,
armhole: points.armhole.y + 0.01,
}
let prevY = (points.cfHem ?? points.cbHem).y
const strings = Object.keys(offsets)
for (let i = 0; i < strings.length; i++) {
const key = strings[i]
const y = offsets[key]
if (!y || y > points.hem.y) {
continue
}
let intersects = sideSeam.intersectsY(y)
if (intersects.length > 0) {
const sidePoint = intersects[0]
const centerPoint = sidePoint.clone()
centerPoint.x = 0
const prevPoint = centerPoint.clone()
prevPoint.y = prevY
prevY = y
macro('hd', {
id: 'wAt' + utils.capitalize(key),
from: centerPoint,
to: sidePoint,
y: y,
})
macro('vd', {
id: 'hBelow' + utils.capitalize(key),
from: centerPoint,
to: prevPoint,
x: 30,
})
}
}
}

146
designs/bibi/src/sleeve.mjs Normal file
View file

@ -0,0 +1,146 @@
import { sleevecap as brianSleeveCap } from '@freesewing/brian'
import { hidePresets } from '@freesewing/core'
export const sleeve = {
name: 'bibi.sleeve',
from: brianSleeveCap,
hide: hidePresets.HIDE_TREE,
options: {
sleeveLength: {
pct: 20,
min: -20,
max: 110,
menu: (settings, mergedOptions) =>
mergedOptions.sleeves === false ? false : 'style.sleeves',
},
cuffEase: {
pct: 20,
min: 0,
max: 200,
menu: (settings, mergedOptions) =>
mergedOptions.sleeves === false ? false : 'style.sleeves',
},
},
measurements: ['shoulderToWrist', 'wrist'],
draft: bibiSleeve,
}
function bibiSleeve({
options,
store,
measurements,
points,
paths,
Point,
Path,
sa,
macro,
snippets,
Snippet,
part,
}) {
points.sleeveTip = paths.sleevecap.edge('top')
points.sleeveTop = new Point(0, points.sleeveTip.y) // Always in center
// Determine the sleeve length
store.set('sleeveLength', measurements.shoulderToWrist * options.sleeveLength)
store.set('capSleeves', options.sleeveLength < 0.05)
store.set('sleeveCapHeight', -points.sleeveTop.y)
if (store.get('capSleeves')) {
return part.hide()
}
if (!options.sleeves) {
store.set('sleeveLength', 0)
return part.hide()
}
// Wrist
points.centerWrist = points.sleeveTop.shift(-90, measurements.shoulderToWrist)
points.wristRight = points.centerWrist.shift(0, (measurements.wrist * (1 + options.cuffEase)) / 2)
points.wristLeft = points.wristRight.rotate(180, points.centerWrist)
points.cuffRight = points.bicepsRight.shiftFractionTowards(
points.wristRight,
options.sleeveLength
)
points.cuffLeft = points.bicepsLeft.shiftFractionTowards(points.wristLeft, options.sleeveLength)
points.centerCuff = points.cuffRight.shiftFractionTowards(points.cuffLeft, 0.5)
// Paths
paths.sleevecap.hide()
paths.seam = new Path()
.move(points.bicepsLeft)
.move(points.cuffLeft)
.move(points.cuffRight)
.line(points.bicepsRight)
.join(paths.sleevecap)
.close()
.attr('class', 'fabric')
if (sa) paths.sa = paths.seam.offset(sa).attr('class', 'fabric sa')
/*
* Annotations
*/
// Anchor point for sampling
points.gridAnchor = new Point(0, 0)
// Grainline
macro('grainline', {
from: points.centerCuff,
to: points.centerBiceps,
})
// Cut list
store.cutlist.addCut({ cut: 2, from: 'fabric' })
// Logo
points.logo = points.centerBiceps.shiftFractionTowards(points.centerCuff, 0.3)
snippets.logo = new Snippet('logo', points.logo)
// Title
macro('title', { at: points.centerBiceps, nr: 3, title: 'sleeve' })
// Notches
points.frontNotch = paths.sleevecap.shiftAlong(store.get('frontArmholeToArmholePitch'))
points.backNotch = paths.sleevecap.reverse().shiftAlong(store.get('backArmholeToArmholePitch'))
snippets.frontNotch = new Snippet('notch', points.frontNotch)
snippets.backNotch = new Snippet('bnotch', points.backNotch)
// Dimensions
macro('vd', {
id: 'hCuffToArmhole',
from: points.cuffLeft,
to: points.bicepsLeft,
x: points.bicepsLeft.x - sa - 15,
})
macro('vd', {
id: 'hFull',
from: points.cuffLeft,
to: points.sleeveTip,
x: points.bicepsLeft.x - sa - 30,
})
macro('hd', {
id: 'wFull',
from: points.bicepsLeft,
to: points.bicepsRight,
y: points.sleeveTip.y - sa - 30,
})
macro('hd', {
id: 'wCuff',
from: points.cuffLeft,
to: points.cuffRight,
y: points.cuffLeft.y + sa + 30,
})
macro('pd', {
id: 'lSleevevap',
path: paths.sleevecap.reverse(),
d: -1 * sa - 15,
})
return part
}

View file

@ -0,0 +1,20 @@
// This file is auto-generated | Any changes you make will be overwritten.
import { Bibi, i18n } from '../src/index.mjs'
// Shared tests
import { testPatternConfig } from '../../../tests/designs/config.mjs'
import { testPatternI18n } from '../../../tests/designs/i18n.mjs'
import { testPatternDrafting } from '../../../tests/designs/drafting.mjs'
import { testPatternSampling } from '../../../tests/designs/sampling.mjs'
// Test config
testPatternConfig(Bibi)
// Test translation
testPatternI18n(Bibi, i18n)
// Test drafting - Change the second parameter to `true` to log errors
testPatternDrafting(Bibi, false)
// Test sampling - Change the second parameter to `true` to log errors
testPatternSampling(Bibi, false)

View file

@ -8,6 +8,7 @@ import { Bee as bee } from '@freesewing/bee'
import { Bella as bella } from '@freesewing/bella' import { Bella as bella } from '@freesewing/bella'
import { Benjamin as benjamin } from '@freesewing/benjamin' import { Benjamin as benjamin } from '@freesewing/benjamin'
import { Bent as bent } from '@freesewing/bent' import { Bent as bent } from '@freesewing/bent'
import { Bibi as bibi } from '@freesewing/bibi'
import { Bob as bob } from '@freesewing/bob' import { Bob as bob } from '@freesewing/bob'
import { Breanna as breanna } from '@freesewing/breanna' import { Breanna as breanna } from '@freesewing/breanna'
import { Brian as brian } from '@freesewing/brian' import { Brian as brian } from '@freesewing/brian'
@ -66,6 +67,7 @@ const designs = {
bella, bella,
benjamin, benjamin,
bent, bent,
bibi,
bob, bob,
breanna, breanna,
brian, brian,

View file

@ -8,6 +8,7 @@ import { Bee as bee } from '@freesewing/bee'
import { Bella as bella } from '@freesewing/bella' import { Bella as bella } from '@freesewing/bella'
import { Benjamin as benjamin } from '@freesewing/benjamin' import { Benjamin as benjamin } from '@freesewing/benjamin'
import { Bent as bent } from '@freesewing/bent' import { Bent as bent } from '@freesewing/bent'
import { Bibi as bibi } from '@freesewing/bibi'
import { Bob as bob } from '@freesewing/bob' import { Bob as bob } from '@freesewing/bob'
import { Breanna as breanna } from '@freesewing/breanna' import { Breanna as breanna } from '@freesewing/breanna'
import { Brian as brian } from '@freesewing/brian' import { Brian as brian } from '@freesewing/brian'
@ -63,6 +64,7 @@ const designs = {
bella, bella,
benjamin, benjamin,
bent, bent,
bibi,
bob, bob,
breanna, breanna,
brian, brian,

View file

@ -8,6 +8,7 @@ import { i18n as bee } from '@freesewing/bee'
import { i18n as bella } from '@freesewing/bella' import { i18n as bella } from '@freesewing/bella'
import { i18n as benjamin } from '@freesewing/benjamin' import { i18n as benjamin } from '@freesewing/benjamin'
import { i18n as bent } from '@freesewing/bent' import { i18n as bent } from '@freesewing/bent'
import { i18n as bibi } from '@freesewing/bibi'
import { i18n as bob } from '@freesewing/bob' import { i18n as bob } from '@freesewing/bob'
import { i18n as breanna } from '@freesewing/breanna' import { i18n as breanna } from '@freesewing/breanna'
import { i18n as brian } from '@freesewing/brian' import { i18n as brian } from '@freesewing/brian'
@ -69,6 +70,7 @@ export const designs = {
bella, bella,
benjamin, benjamin,
bent, bent,
bibi,
bob, bob,
breanna, breanna,
brian, brian,

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,3 @@
// __SDEFILE__ - This file is a dependency for the stand-alone environment // __SDEFILE__ - This file is a dependency for the stand-alone environment
// This file is auto-generated by the prebuild script | Any changes will be overwritten // This file is auto-generated by the prebuild script | Any changes will be overwritten
export const designs = ["aaron","albert","bee","bella","benjamin","bent","bob","breanna","brian","bruce","carlita","carlton","cathrin","charlie","cornelius","diana","examples","florence","florent","gozer","hi","holmes","hortensia","huey","hugo","jaeger","legend","lucy","lumina","lumira","lunetius","magde","naomiwu","noble","octoplushy","onyx","otis","paco","penelope","plugintest","rendertest","sandy","shelly","shin","simon","simone","skully","sven","tamiko","teagan","tiberius","titan","trayvon","tristan","uma","wahid","walburga","waralee","yuri"] export const designs = ["aaron","albert","bee","bella","benjamin","bent","bibi","bob","breanna","brian","bruce","carlita","carlton","cathrin","charlie","cornelius","diana","examples","florence","florent","gozer","hi","holmes","hortensia","huey","hugo","jaeger","legend","lucy","lumina","lumira","lunetius","magde","naomiwu","noble","octoplushy","onyx","otis","paco","penelope","plugintest","rendertest","sandy","shelly","shin","simon","simone","skully","sven","tamiko","teagan","tiberius","titan","trayvon","tristan","uma","wahid","walburga","waralee","yuri"]