diff --git a/config/software/designs.json b/config/software/designs.json index 75abc902887..03ad76be50b 100644 --- a/config/software/designs.json +++ b/config/software/designs.json @@ -284,6 +284,13 @@ "difficulty": 1, "tags": [ "tops", "historic" ] }, + "unice": { + "description": "A FreeSewing pattern for a basic, highly-customizable underwear pattern", + "code": [ "Anna Puk", "Natalia Sayang" ], + "design": [ "Anna Puk", "Natalia Sayang" ], + "difficulty": 1, + "tags": [ "bottoms", "underwear" ] + }, "ursula": { "description": "A FreeSewing pattern for a basic, highly-customizable underwear pattern", "code": "Natalia Sayang", diff --git a/designs/unice/CHANGELOG.md b/designs/unice/CHANGELOG.md new file mode 100644 index 00000000000..6b458471bea --- /dev/null +++ b/designs/unice/CHANGELOG.md @@ -0,0 +1,15 @@ +# Change log for: @freesewing/unice + + +## 2.21.0 (2022-06-27) + +### Added + + - Unice is an underwear pattern + + +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. + diff --git a/designs/unice/README.md b/designs/unice/README.md new file mode 100644 index 00000000000..3c02b12a2d2 --- /dev/null +++ b/designs/unice/README.md @@ -0,0 +1,284 @@ +![FreeSewing](https://static.freesewing.org/banner.png) +

@freesewing/unice on NPM + License: MIT + Code quality on DeepScan + Open issues tagged pkg:unice + All Contributors +

Follow @freesewing_org on Twitter + Chat with us on Discord + Become a FreeSewing Patron + Follow @freesewing_org on Twitter +

+ +# @freesewing/unice + +A FreeSewing pattern for a basic, highly-customizable underwear pattern + + + + +> #### Note: Version 3 is a work in progress +> +> We are working on a new major version (v3) but it is not ready for prime-time. +> For production use, please refer to our v2 packages (the `latest` on NPM) +> or [the `v2` branch in our monorepo](https://github.com/freesewing/freesewing/tree/v2). + +## What am I looking at? πŸ€” + +This repository is our *monorepo* holding all our NPM designs, plugins, other NPM packages, and (web)sites. + +This folder holds: @freesewing/unice + +If you're not entirely sure what to do or how to start, type this command: + +``` +npm run tips +``` + +> 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/# 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 +our sewing patterns adapted to your measurements. + +If you're a developer, our documentation is on [freesewing.dev](https://freesewing.dev/). +Our [core library](https://freesewing.dev/reference/api/) is a *batteries-included* toolbox +for parametric design of sewing patterns. But we also provide 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 create-freesewing-pattern +``` + +Or, consult our getting started guides +for [Linux](https://freesewing.dev/tutorials/getting-started-linux/), +[MacOS](https://freesewing.dev/tutorials/getting-started-mac/), +or [Windows](https://freesewing.dev/tutorials/getting-started-windows/). + +We also have a [pattern design tutorial](https://freesewing.dev/tutorials/pattern-design/) that +walks you through your first parametric design, +and [a friendly community](https://freesewing.org/community/where/) with +people who can help you when you get stuck. + +## Support FreeSewing: Become a patron πŸ₯° + +FreeSewing is an open source project run by a community, +and financially supported by our patrons. + +If you feel what we do is worthwhile, and you can spend a few coind without +hardship, then you should [join us and become a patron](https://freesewing.org/community/join). + +## Links πŸ‘©β€πŸ’» + + - πŸ’» Makers website: [freesewing.org](https://freesewing.org) + - πŸ’» Developers website: [freesewing.dev](https://freesewing.dev) + - πŸ’¬ Chat: On Discord via [discord.freesewing.org](https://discord.freesewing.org/) + - βœ… Todo list/Kanban board: On Github via [todo.freesewing.org](https://todo.freesewing.org/) + - 🐦 Twitter: [@freesewing_org](https://twitter.com/freesewing_org) + - πŸ“· Instagram: [@freesewing_org](https://instagram.com/freesewing_org) + +## 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 🀯 + +Our [chatrooms on Discord](https://chat.freesewing.org/) are the best place to ask questions, +share your feedback, or just hang out. + +If you want to report a problem, please [create an issue](https://github.com/freesewing/freesewing/issues/new). + + + +## Contributors ✨ + +Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Adam Tomkins

πŸ“–

Alexandre Ignjatovic

πŸ’»

AlfaLyr

πŸ’» πŸ”Œ 🎨

Andrew James

πŸ“–

Anneke

πŸ“– 🌍

Annie Kao

πŸ“–

Anternative

πŸ“–

Anthony

πŸ’¬

Ari Grayzel-student

πŸ’»

BenJamesBen

πŸ’» πŸ“–

Cameron Dubas

πŸ“–

Carsten Biebricher

πŸ“–

Cathy Zoller

πŸ“–

Chantal Lapointe

🌍

Damien PIQUET

πŸ’»

Darigov Research

πŸ“– πŸ€”

Elena FdR

πŸ“– πŸ“

Emmanuel Nyachoke

πŸ’» πŸ“–

Enoch Riese

πŸ’»

EvEkSwed

🌍

Fantastik-Maman

🌍

Forrest O.

πŸ“–

FrΓ©dΓ©ric

🌍

Glenn Matthews

πŸ“–

Greg Sadetsky

πŸ“–

Igor Couto

πŸ›

Ikko Ashimine

πŸ“–

Irapeke

🌍

Jacek Sawoszczuk

πŸ“–

Jason Williams

πŸ“–

Jeremy Jackson

πŸ’»

Joe Schofield

πŸ“–

Joebidido

🌍

Joost De Cock

🚧

Josh Essman

πŸ“–

Kake

πŸ“–

Kapunahele Wong

πŸ“–

Karen

πŸ“– πŸ“‹

Katie McGinley

πŸ“–

Kieran Klaassen

πŸ’»

Kittycatou

🌍

Kris

πŸ“–

Kristin Ruben

πŸ’»

Loudepeuter

🌍

Lucian

πŸ“‹

Marcus

🌍

Martin Tribo

πŸ“–

Nadege Michel

⚠️ πŸ“–

Natalia

πŸ’» 🎨 πŸ“

Nathan Yergler

πŸ“–

Nick Dower

πŸ“– πŸ’» πŸ›

Nikhil Chelliah

πŸ“–

OysteinHoiby

πŸ’»

Patrick Forringer

πŸ”Œ

Paul

πŸ“– πŸ“ 🌍

Phillip Thelen

πŸ’»

Pixieish

πŸ“–

Prof. dr. Sorcha NΓ­ Dhubhghaill

πŸ“–

Quentin FELIX

πŸ’» 🎨

Rik Hekker

πŸ›

Sam Livingston-Gray

πŸ“–

Sanne

πŸ’» πŸ“–

Sara Latorre

🌍

SeaZeeZee

πŸ“– πŸ’»

SimonbJohnson

πŸ›

Slylele

πŸ“– 🌍

Soazillon

🌍

SoneaTheBest

🌍

Stefan Sydow

🌍 πŸ“– πŸ’»

TrΓ­ona

πŸ“–

Unmutual

πŸ“–

Wouter van Wageningen

πŸ’» 🎨 πŸ”§

amysews

πŸ“–

anna-puk

πŸ’»

beautifulsummermoon

🌍

berce

πŸ“–

biou

πŸ’»

bobgeorgethe3rd

πŸ’» πŸ“– 🎨

brmlyklr

πŸ“–

chri5b

πŸ’» ⚠️

dingcycle

🌍

drowned-in-books

πŸ’¬

econo202

πŸ“–

ericamattos

🌍

fightingrabbit

πŸ’»

gaylyndie

πŸ“–

grimlokason

πŸ’»

hellgy

🎨

jackseye

πŸ“–

marckiesel

🌍

mesil

πŸ›

starfetch

πŸ’» πŸ“– 🌍 🎨

ttimearl

πŸ–‹

tuesgloomsday

πŸ“–

valadaptive

πŸ’»

viocky

🌍

woolishboy

πŸ’»

yc

🌍
+ + + + + + +This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! + diff --git a/designs/unice/build.mjs b/designs/unice/build.mjs new file mode 100644 index 00000000000..c5581c1e4a2 --- /dev/null +++ b/designs/unice/build.mjs @@ -0,0 +1,50 @@ +/* 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 +let result +(async () => { + result = await esbuild.build(options).catch(() => process.exit(1)) + + if (process.env.VERBOSE) { + const info = await esbuild.analyzeMetafile(result.metafile) + console.log(info) + } + + // Also build a version that has all dependencies bundled + // This makes it easy to run tests + await esbuild + .build({ + ...options, + minify: false, + keepNames: true, + sourcemap: false, + outfile: 'tests/dist/index.mjs', + format: 'esm', + external: [], + }) + .catch(() => process.exit(1)) + +})() diff --git a/designs/unice/config/index.js b/designs/unice/config/index.js new file mode 100644 index 00000000000..25bc838e8cf --- /dev/null +++ b/designs/unice/config/index.js @@ -0,0 +1,62 @@ +import { config as ursulaConfig } from '@freesewing/ursula' +import pkg from '../package.json' assert { type: 'json' } + +const { version } = pkg +const design = ['Anna Puk', 'Natalia Sayang'] + +const config = { + ...ursulaConfig, + version, + design, + code: design, + name: 'unice', + inject: { + front: 'ursulaFront', + back: 'ursulaBack', + gusset: 'ursulaGusset', + front2: 'front', + back2: 'back', + gusset2: 'gusset', + }, + hide: ['ursulaBack', 'ursulaFront', 'ursulaGusset','front', 'back', 'gusset'], + parts: ['front','back','gusset','elastic','front2','back2','gusset2'], + optionalMeasurements: ['crossSeam','crossSeamFront'], + measurements: ['waist', 'seat', 'waistToSeat', 'waistToUpperLeg','hips','waistToHips'], + optionGroups: { + ...ursulaConfig.optionGroups, + fit: [ + 'fabricStretchX', + 'fabricStretchY', + 'adjustStretch', + 'elasticStretch', + 'useCrossSeam', + 'gussetWidth', + 'gussetLength' + ], + }, + dependencies: { + back: 'front', + gusset: 'back', + elastic: 'gusset', + front2: 'elastic', + back2: 'elastic', + gusset2: 'elastic', + }, + options: { + ...ursulaConfig.options, + gussetShift: 0.015, // fraction of seat circumference - could be an advanced option? + gussetWidth: { pct: 7.2, min: 2, max: 12 }, // Gusset width in relation to waist-to-upperleg + fabricStretchX: { pct: 15, min: 0, max: 100 }, // horizontal stretch (range set wide for beta testing) + fabricStretchY: {pct: 0, min: 0, max: 100 }, // vertical stretch (range set wide for beta testing) + rise: { pct: 60, min: 30, max: 100 }, // extending rise beyond 100% would require adapting paths.sideLeft! + legOpening: { pct: 45, min: 5, max: 85 }, + // booleans + useCrossSeam: { bool: true }, + adjustStretch: {bool: true}, // to not stretch fabric to the limits + } +} + +//delete config.options.fabricStretch + +export default config + diff --git a/designs/unice/data.mjs b/designs/unice/data.mjs new file mode 100644 index 00000000000..510883477b0 --- /dev/null +++ b/designs/unice/data.mjs @@ -0,0 +1,4 @@ +// This file is auto-generated | All changes you make will be overwritten. +export const name = "@freesewing/unice" +export const version = "3.0.0-alpha.0" +export const data = { name, version } diff --git a/designs/unice/example/.babelrc b/designs/unice/example/.babelrc new file mode 100644 index 00000000000..6e3090a4956 --- /dev/null +++ b/designs/unice/example/.babelrc @@ -0,0 +1,10 @@ +{ + "plugins": [ + ["prismjs", { + "languages": ["javascript", "css", "markup"], + "plugins": ["line-numbers"], + "theme": "twilight", + "css": true + }] + ] +} diff --git a/designs/unice/example/README.md b/designs/unice/example/README.md new file mode 100644 index 00000000000..73d3153ee6f --- /dev/null +++ b/designs/unice/example/README.md @@ -0,0 +1,96 @@ +

+Freesewing logo +
+FreeSewing v2 +

+

A JavaScript library for made-to-measure sewing patterns

+

Follow @freesewing_org on Twitter + Chat with us on Gitter + Become a FreeSewing Patron + Follow @freesewing_org on Twitter + +

+ +# unice example + +This project was bootstrapped with [Create Freesewing Pattern](https://en.freesewing.dev/create-freesewing-pattern): + +```js +npm init freesewing-pattern +``` + +This example folder is part of the local development environment. +It is **not** part of the pattern's source code. + +To run this example, follow these steps: + + - In the folder above this one, run: `yarn start` (or `npm start`) + - Then, in new terminal, run the same command in this folder: `yarn start` (or `npm start`) + +This will spin up the development environment, similar to [our online demo](https://unice.freesewing.dev/). + +## About FreeSewing πŸ€” + +Where the world of makers and developers collide, that's where you'll find FreeSewing. + +Our [core library](https://freesewing.dev/en/freesewing) is a *batteries-included* toolbox +for parametric design of sewing patterns. It's a modular system (check our list +of [plugins](https://freesewing.dev/en/plugins) and getting started is as simple as: + +```bash +npm init freesewing-pattern +``` + +The [getting started] section on [freesewing.dev](https://freesewing.dev/) is a good +entrypoint to our documentation, but you'll find a lot more there, including +our [API documentation](https://freesewing.dev/en/freesewing/api), +as well as [examples](https://freesewing.dev/en/freesewing/examples), +and [best practices](https://freesewing.dev/en/do). + +If you're a maker, checkout [freesewing.org](https://freesewing/) where you can generate +our sewing patterns adapted to your measurements. + +## Support FreeSewing: Become a patron πŸ₯° + +FreeSewing is an open source project run by a community, +and financially supported by our patrons. + +If you feel what we do is worthwhile, you too +should [become a patron](https://freesewing.org/patrons/join). + +## Links πŸ‘©β€πŸ’» + + - πŸ’» Makers website: [freesewing.org](https://freesewing.org) + - πŸ’» Developers website: [freesewing.dev](https://freesewing.org) + - πŸ’¬ Chat: [gitter.im/freesewing](https://gitter.im/freesewing/freesewing) + - 🐦 Twitter: [@freesewing_org](https://twitter.com/freesewing_org) + - πŸ“· Instagram: [@freesewing_org](https://instagram.com/freesewing_org) + +## 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 🀯 + +Our [chatroom on Gitter](https://gitter.im) is the best place to ask questions, +share your feedback, or just hang out. + +If you want to report a problem, please [create an issue](https://github.com/freesewing/freesewing/issues/new). + diff --git a/designs/unice/example/netlify.toml b/designs/unice/example/netlify.toml new file mode 100644 index 00000000000..9890e6c62f2 --- /dev/null +++ b/designs/unice/example/netlify.toml @@ -0,0 +1,9 @@ +[build] + base = "designs/unice/example" + publish = "build" + command = "npm run build" + +[[redirects]] + from = "/*" + to = "/index.html" + status = 200 diff --git a/designs/unice/example/package.json b/designs/unice/example/package.json new file mode 100644 index 00000000000..77774ed36a3 --- /dev/null +++ b/designs/unice/example/package.json @@ -0,0 +1,56 @@ +{ + "name": "unice", + "homepage": "https://unice.freesewing.dev/", + "version": "", + "private": true, + "dependencies": { + "@fontsource/permanent-marker": "latest", + "@fontsource/roboto-mono": "latest", + "@fontsource/ubuntu": "latest", + "@freesewing/components": "latest", + "@freesewing/core": "latest", + "@freesewing/css-theme": "latest", + "@freesewing/i18n": "latest", + "@freesewing/models": "latest", + "@freesewing/mui-theme": "latest", + "@freesewing/pattern-info": "latest", + "@freesewing/plugin-bundle": "latest", + "@freesewing/plugin-theme": "latest", + "@freesewing/plugin-i18n": "latest", + "@freesewing/plugin-svgattr": "latest", + "@freesewing/utils": "latest", + "@material-ui/core": "^4.11.4", + "@material-ui/icons": "^4.11.2", + "@material-ui/lab": "^v4.0.0-alpha.57", + "prismjs": "1.25.0", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-intl": "^5.18.0", + "react-scripts": "^4.0.3", + "react-error-overlay": "6.0.9", + "file-saver": "^2.0.5", + "react-markdown": "6.0.2", + "source-map-explorer": "^2.5.2" + }, + "scripts": { + "analyze": "source-map-explorer 'build/static/js/*.js'", + "size": "source-map-explorer 'build/static/js/*.js' --tsv --no-root", + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": [ + "defaults" + ], + "devDependencies": { + "babel-plugin-prismjs": "2.0.1", + "react-error-overlay": "6.0.9" + }, + "resolutions": { + "react-error-overlay": "6.0.9" + } +} diff --git a/designs/unice/example/public/favicon.ico b/designs/unice/example/public/favicon.ico new file mode 100644 index 00000000000..95061a260f1 Binary files /dev/null and b/designs/unice/example/public/favicon.ico differ diff --git a/designs/unice/example/public/index.html b/designs/unice/example/public/index.html new file mode 100644 index 00000000000..af182fd3cc0 --- /dev/null +++ b/designs/unice/example/public/index.html @@ -0,0 +1,41 @@ + + + + + + + + + + + + unice + + + +
+ + + diff --git a/designs/unice/example/public/layout.css b/designs/unice/example/public/layout.css new file mode 100644 index 00000000000..c62502f9791 --- /dev/null +++ b/designs/unice/example/public/layout.css @@ -0,0 +1 @@ +div.layout-wrapper{width:100%;margin:0;padding:0;background-color:red;background:#f8f9fa;background:linear-gradient(90deg, #f1f3f5 0%, #f1f3f5 25%, #f8f9fa 26%, #f8f9fa 100%)}div.layout-wrapper div.layout{display:flex;max-width:1600px;margin:auto;padding:0;flex-direction:row;flex-wrap:nowrap;justify-content:space-between;background-color:#f8f9fa;min-height:calc(100vh - 64px)}div.layout-wrapper div.layout>aside{width:33%;background:#f1f3f5;border-right:2px solid #dee2e6}div.layout-wrapper div.layout>section{margin:0;padding:1rem}div.layout-wrapper div.layout>section>div.content{max-width:66ch;min-width:340px}div.layout-wrapper div.layout>section>div.content.wide{max-width:100%;margin:auto}.theme-wrapper.dark header{background-color:#1a1d21}.theme-wrapper.dark div.layout-wrapper{background:#f8f9fa;background:linear-gradient(90deg, #1a1d21 0%, #1a1d21 25%, #212529 26%, #212529 100%)}.theme-wrapper.dark div.layout-wrapper div.layout{background-color:#212529}.theme-wrapper.dark div.layout-wrapper div.layout>aside{background-color:#1a1d21;border-right:2px solid #343a40}header a svg{color:#ced4da}header a:first-of-type svg{color:#f8f9fa}header a:hover svg{color:#b197fc}header a span,header button span{color:#ced4da}header a span svg,header button span svg{color:#dee2e6}header a:hover span,header button:hover span{color:#f8f9fa}header a:hover span svg,header button:hover span svg{color:#b197fc}header a,header button{padding:0 1vw !important}@media (min-width: 1200px){div.layout>section{width:63%}}@media (max-width: 1199px) and (min-width: 960px){div.layout>aside{width:298px}div.layout>section{width:calc(100% - 300px - 4rem);max-width:none;margin:0 1rem 0 3rem}}@media (max-width: 959px){div.layout>aside{width:218px}div.layout>section{width:calc(100% - 220px - 4rem);max-width:none;margin:0;padding:0 2rem}div.layout>section div.content{min-width:inherit}}@media (max-width: 599px){div.layout>aside{display:none}div.layout>section{width:calc(100%);margin:0 auto;padding:0 1.5rem;max-width:none}}div.gatsby-highlight{margin-bottom:1rem}@media (max-width: 599px){#mobile-menu{position:fixed;top:0;left:0;width:100%;height:100vh;padding:0 0 1rem;max-width:600px;z-index:-10;transition:opacity 0.25s ease 0s;opacity:0;overflow:scroll}#mobile-menu>ul,#mobile-menu>div{transform:translate(0px, 10px);transition:transform 0.25s ease 0s}.theme-wrapper.show-menu #mobile-menu{opacity:1;z-index:10}.theme-wrapper.show-menu #mobile-menu>div{transform:translate(0px, 0px)}}.theme-wrapper.light div.draft-ui-menu,.theme-wrapper.light div.menu{background:#f1f3f5}.theme-wrapper.dark div.draft-ui-menu,.theme-wrapper.dark div.menu{background:#343a40}.theme-wrapper.show-menu div.menu{opacity:1;z-index:10}.theme-wrapper.show-menu div.menu>div{transform:translate(0px, 0px)}div.spaced-buttons>button{margin:0 0.5rem 0.5rem 0}div.spaced>*{margin:0 0.5rem 0.5rem 0}ul#pre-main-menu{margin:0;padding:0}.boldish{font-weight:500}.freesewing.draft{padding:1rem}li.action{clear:both}li.action span.MuiSwitch-root{float:right}.theme-wrapper.light ul#draft-config li.action.toggle.off,.theme-wrapper.dark ul#draft-config li.action.toggle.off{color:#868e96}.theme-wrapper.light ul#draft-config li.action.toggle.off>span svg,.theme-wrapper.dark ul#draft-config li.action.toggle.off>span svg{color:#868e96}footer{background-color:#1a1d21;color:#adb5bd;padding:3rem 0 6rem}footer a{color:#dee2e6 !important;font-weight:400}footer a:hover{color:#d0bfff !important}footer div.cols{display:flex;flex-direction:row;justify-content:space-between;max-width:1600px;margin:auto;padding:0 1.5rem}footer div.cols>div{min-width:150px;max-width:calc(20% - 4rem);padding:0 2rem 0 0;width:100%}footer ul{text-align:left;font-size:1.1rem;margin:0;padding:0;width:100%}footer ul li:first-of-type{padding:0.35rem 0.75rem}footer ul li{display:block}footer ul li a:hover{text-decoration:none !important}footer ul li.heading{font-weight:bold;border-bottom:3px solid #adb5bd;margin-bottom:0.5rem}@media (min-width: 1200px){footer div.cols>div:last-of-type{min-width:350px}}@media (min-width: 600px) and (max-width: 959px){footer div.cols{flex-wrap:wrap}footer div.cols>div{width:calc(30% - 4rem);padding:0 1rem}}@media (max-width: 599px){footer div.cols{display:block}footer div.cols>div{margin:2rem auto 0;max-width:calc(100% - 4rem)}footer div.cols>div:first-of-type{margin-top:0}} diff --git a/designs/unice/example/public/manifest.json b/designs/unice/example/public/manifest.json new file mode 100644 index 00000000000..578f27ed307 --- /dev/null +++ b/designs/unice/example/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "unice", + "name": "unice", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/designs/unice/example/src/App.js b/designs/unice/example/src/App.js new file mode 100644 index 00000000000..7bb0f0e7112 --- /dev/null +++ b/designs/unice/example/src/App.js @@ -0,0 +1,45 @@ +import React from 'react' +import freesewing from '@freesewing/core' +import Workbench from '@freesewing/components/Workbench' +import '@freesewing/css-theme' +import Pattern from './pattern/src/index.js' +/* + * The following symlink is required to make this import work: + * `root_folder/example/src/pattern => `../../` + * + * Without it, we can't import the pattern as a local file + * since create-react-app does not allow imports outside ./src + * If it's imported as a dependency, webpack will cache the + * build and there will be no hot-reloading of changes + */ + +const App = (props) => { + // You can use this to add translations + /* + let translations = { + JSON: 'JSON', + someOtherString: 'Some other string that needs translation' + } + */ + + // Adds support for loading an external pattern configuration + let recreate = false + if (window) recreate = window.location.pathname.substr(1).split('/') + if (recreate.length === 3 && recreate[0] === 'recreate') { + recreate = { from: recreate[1], id: recreate[2] } + } else { + recreate = false + } + + return ( + + ) +} + +export default App diff --git a/designs/unice/example/src/index.js b/designs/unice/example/src/index.js new file mode 100644 index 00000000000..24aefad45a1 --- /dev/null +++ b/designs/unice/example/src/index.js @@ -0,0 +1,12 @@ +import React from 'react' +import ReactDOM from 'react-dom' +import App from './App' +import * as serviceWorker from './serviceWorker' +import './layout.css' + +ReactDOM.render(, document.getElementById('root')) + +// If you want your app to work offline and load faster, you can change +// unregister() to register() below. Note this comes with some pitfalls. +// Learn more about service workers: http://bit.ly/CRA-PWA +serviceWorker.unregister() diff --git a/designs/unice/example/src/layout.css b/designs/unice/example/src/layout.css new file mode 100644 index 00000000000..a4963e16e55 --- /dev/null +++ b/designs/unice/example/src/layout.css @@ -0,0 +1,273 @@ +* { + box-sizing: border-box; +} +.MuiToolbar-root { + overflow-y: auto; +} +div.layout-wrapper { + width: 100%; + margin: 0; + padding: 0; + background: #f8f9fa; + background: linear-gradient(90deg, #f1f3f5 0%, #f1f3f5 25%, #f8f9fa 26%, #f8f9fa 100%); +} +div.layout-wrapper div.layout { + display: flex; + max-width: 1600px; + margin: auto; + padding: 0; + flex-direction: row; + flex-wrap: nowrap; + justify-content: space-between; + background-color: #f8f9fa; + min-height: calc(100vh - 64px); +} +div.layout-wrapper div.layout > aside { + width: 33%; + background: #f1f3f5; + border-right: 2px solid #dee2e6; +} +div.layout-wrapper div.layout > section { + margin: 0; + padding: 1rem; +} +div.layout-wrapper div.layout > section > div.content { + max-width: 66ch; + min-width: 340px; +} +div.layout-wrapper div.layout > section > div.content.wide { + max-width: 100%; + margin: auto; +} + +.theme-wrapper.dark header { + background-color: #1a1d21; +} + +.theme-wrapper.dark div.layout-wrapper { + background: #f8f9fa; + background: linear-gradient(90deg, #1a1d21 0%, #1a1d21 25%, #212529 26%, #212529 100%); +} +.theme-wrapper.dark div.layout-wrapper div.layout { + background-color: #212529; +} +.theme-wrapper.dark div.layout-wrapper div.layout > aside { + background-color: #1a1d21; + border-right: 2px solid #343a40; +} + +/* monitor */ +@media (min-width: 1200px) { + div.layout > section { + width: 63%; + } +} + +/* slate */ +@media (max-width: 1199px) and (min-width: 960px) { + div.layout > aside { + width: 298px; + } + div.layout > section { + width: calc(100% - 300px - 4rem); + max-width: none; + margin: 0 1rem 0 3rem; + } +} + +/* tablet */ +@media (max-width: 959px) { + div.layout > aside { + width: 218px; + } + div.layout > section { + width: calc(100% - 220px - 4rem); + max-width: none; + margin: 0; + padding: 0 2rem; + } + div.layout > section div.content { + min-width: inherit; + } +} + +/* mobile */ +@media (max-width: 599px) { + div.layout > aside { + display: none; + } + div.layout > section { + width: calc(100%); + margin: 0 auto; + padding: 0 1.5rem; + max-width: none; + } +} + +div.gatsby-highlight { + margin-bottom: 1rem; +} + +@media (max-width: 599px) { + #mobile-menu { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100vh; + padding: 0 0 1rem; + max-width: 600px; + z-index: -10; + transition: opacity 0.25s ease 0s; + opacity: 0; + overflow: scroll; + } + #mobile-menu > ul, + #mobile-menu > div { + transform: translate(0px, 10px); + transition: transform 0.25s ease 0s; + } + .theme-wrapper.show-menu #mobile-menu { + opacity: 1; + z-index: 10; + } + .theme-wrapper.show-menu #mobile-menu > div { + transform: translate(0px, 0px); + } +} + +.theme-wrapper.light div.draft-ui-menu, +.theme-wrapper.light div.menu { + background: #f1f3f5; +} + +.theme-wrapper.dark div.draft-ui-menu, +.theme-wrapper.dark div.menu { + background: #343a40; +} + +.theme-wrapper.show-menu div.menu { + opacity: 1; + z-index: 10; +} +.theme-wrapper.show-menu div.menu > div { + transform: translate(0px, 0px); +} + +div.spaced-buttons > button { + margin: 0 0.5rem 0.5rem 0; +} + +div.spaced > * { + margin: 0 0.5rem 0.5rem 0; +} + +ul#pre-main-menu { + margin: 0; + padding: 0; +} + +.boldish { + font-weight: 500; +} + +.freesewing.draft { + padding: 1rem; +} + +li.action { + clear: both; +} + +li.action span.MuiSwitch-root { + float: right; +} + +.theme-wrapper.light ul#draft-config li.action.toggle.off, +.theme-wrapper.dark ul#draft-config li.action.toggle.off { + color: #868e96; +} +.theme-wrapper.light ul#draft-config li.action.toggle.off > span svg, +.theme-wrapper.dark ul#draft-config li.action.toggle.off > span svg { + color: #868e96; +} + +footer { + background-color: #1a1d21; + color: #adb5bd; + padding: 3rem 0 6rem; +} +footer a { + color: #dee2e6 !important; + font-weight: 400; +} +footer a:hover { + color: #d0bfff !important; +} +footer div.cols { + display: flex; + flex-direction: row; + justify-content: space-between; + max-width: 1600px; + margin: auto; + padding: 0 1.5rem; +} +footer div.cols > div { + min-width: 150px; + max-width: calc(20% - 4rem); + padding: 0 2rem 0 0; + width: 100%; +} +footer ul { + text-align: left; + font-size: 1.1rem; + margin: 0; + padding: 0; + width: 100%; +} +footer ul li:first-of-type { + padding: 0.35rem 0.75rem; +} +footer ul li { + display: block; +} +footer ul li a:hover { + text-decoration: none !important; +} +footer ul li.heading { + font-weight: bold; + border-bottom: 3px solid #adb5bd; + margin-bottom: 0.5rem; +} + +/* XL screens */ +@media (min-width: 1200px) { + footer div.cols > div:last-of-type { + min-width: 350px; + } +} + +/* SM screens */ +@media (min-width: 600px) and (max-width: 959px) { + footer div.cols { + flex-wrap: wrap; + } + footer div.cols > div { + width: calc(30% - 4rem); + padding: 0 1rem; + } +} + +/* XS screens */ +@media (max-width: 599px) { + footer div.cols { + display: block; + } + footer div.cols > div { + margin: 2rem auto 0; + max-width: calc(100% - 4rem); + } + footer div.cols > div:first-of-type { + margin-top: 0; + } +} diff --git a/designs/unice/example/src/pattern b/designs/unice/example/src/pattern new file mode 100644 index 00000000000..6581736d623 --- /dev/null +++ b/designs/unice/example/src/pattern @@ -0,0 +1 @@ +../../ \ No newline at end of file diff --git a/designs/unice/example/src/serviceWorker.js b/designs/unice/example/src/serviceWorker.js new file mode 100644 index 00000000000..4fe923e7795 --- /dev/null +++ b/designs/unice/example/src/serviceWorker.js @@ -0,0 +1,123 @@ +// In production, we register a service worker to serve assets from local cache. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on the "N+1" visit to a page, since previously +// cached resources are updated in the background. + +// To learn more about the benefits of this model, read https://goo.gl/KwvDNy. +// This link also includes instructions on opting out of this behavior. + +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/) +) + +export function register(config) { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location) + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 + return + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js` + + if (isLocalhost) { + // This is running on localhost. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config) + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit https://goo.gl/SC7cgQ' + ) + }) + } else { + // Is not local host. Just register service worker + registerValidSW(swUrl, config) + } + }) + } +} + +function registerValidSW(swUrl, config) { + navigator.serviceWorker + .register(swUrl) + .then((registration) => { + registration.onupdatefound = () => { + const installingWorker = registration.installing + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the old content will have been purged and + // the fresh content will have been added to the cache. + // It's the perfect time to display a "New content is + // available; please refresh." message in your web app. + console.log('New content is available; please refresh.') + + // Execute callback + if (config.onUpdate) { + config.onUpdate(registration) + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.') + + // Execute callback + if (config.onSuccess) { + config.onSuccess(registration) + } + } + } + } + } + }) + .catch((error) => { + console.error('Error during service worker registration:', error) + }) +} + +function checkValidServiceWorker(swUrl, config) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then((response) => { + // Ensure service worker exists, and that we really are getting a JS file. + if ( + response.status === 404 || + response.headers.get('content-type').indexOf('javascript') === -1 + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then((registration) => { + registration.unregister().then(() => { + window.location.reload() + }) + }) + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl, config) + } + }) + .catch(() => { + console.log('No internet connection found. App is running in offline mode.') + }) +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then((registration) => { + registration.unregister() + }) + } +} diff --git a/designs/unice/package.json b/designs/unice/package.json new file mode 100644 index 00000000000..4c3f21efd5f --- /dev/null +++ b/designs/unice/package.json @@ -0,0 +1,67 @@ +{ + "name": "@freesewing/unice", + "version": "3.0.0-alpha.0", + "description": "A FreeSewing pattern for a basic, highly-customizable underwear pattern", + "author": "Anna Puk (https://github.com/anna-puk)", + "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": { + ".": "./dist/index.mjs" + }, + "scripts": { + "build": "node build.mjs", + "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", + "prettier": "npx prettier --write 'src/*.mjs' 'tests/*.mjs'", + "testci": "npx mocha tests/*.test.mjs --reporter ../../tests/reporters/terse.js", + "cibuild_step5": "node build.mjs" + }, + "peerDependencies": { + "@freesewing/core": "^3.0.0-alpha.0", + "@freesewing/plugin-bundle": "^3.0.0-alpha.0", + "@freesewing/config-helpers": "^3.0.0-alpha.0" + }, + "dependencies": {}, + "devDependencies": { + "mocha": "^10.0.0", + "chai": "^4.2.0" + }, + "files": [ + "dist/*", + "README.md" + ], + "publishConfig": { + "access": "public", + "tag": "next" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8" + } +} diff --git a/designs/unice/src/back.mjs b/designs/unice/src/back.mjs new file mode 100644 index 00000000000..f2da67d4093 --- /dev/null +++ b/designs/unice/src/back.mjs @@ -0,0 +1,42 @@ +import { pctBasedOn } from '@freesewing/core' +import {back as ursulaBack } from '@freesewing/ursula' + +export const back = { + name: 'unice.back', + measurements: ['waist', 'seat', 'waistToSeat', 'waistToUpperLeg','hips','waistToHips'], + optionalMeasurements: ['crossSeam','crossSeamFront'], + options: { + gussetShift: 0.015, // fraction of seat circumference - could be an advanced option? + gussetWidth: { pct: 7.2, min: 2, max: 12, menu: 'fit' }, // Gusset width in relation to waist-to-upperleg + gussetLength: { pct: 12.7, min: 10, max: 16, menu: 'fit' }, // Gusset length in relation to seat + fabricStretch: { pct: 15, min: 0, max: 100, menu: 'fit' }, // used in Ursula + fabricStretchX: { pct: 15, min: 0, max: 100, menu: 'fit' }, // horizontal stretch (range set wide for beta testing) + fabricStretchY: {pct: 0, min: 0, max: 100, menu: 'fit' }, // vertical stretch (range set wide for beta testing) + rise: { pct: 60, min: 30, max: 100, menu: 'style' }, // extending rise beyond 100% would require adapting paths.sideLeft! + legOpening: { pct: 45, min: 5, max: 85, menu: 'style' }, + frontDip: { pct: 5.0, min: -5, max: 15, menu: 'style' }, + taperToGusset: { pct: 70, min: 5, max: 100, menu: 'style' }, + // booleans + useCrossSeam: { bool: true, menu: 'fit' }, + adjustStretch: {bool: true, menu: 'fit' }, // to not stretch fabric to the limits + }, + draft: ({ + utils, + store, + sa, + Point, + points, + Path, + paths, + Snippet, + snippets, + options, + measurements, + complete, + paperless, + macro, + part, + }) => { + return part + }, +} diff --git a/designs/unice/src/front.mjs b/designs/unice/src/front.mjs new file mode 100644 index 00000000000..c9a160ac812 --- /dev/null +++ b/designs/unice/src/front.mjs @@ -0,0 +1,309 @@ +import { pctBasedOn } from '@freesewing/core' + +export const front = { + name: 'unice.front', + measurements: ['waist', 'seat', 'waistToSeat', 'waistToUpperLeg','hips','waistToHips'], + optionalMeasurements: ['crossSeam','crossSeamFront'], + options: { + gussetShift: 0.015, // fraction of seat circumference - could be an advanced option? + gussetWidth: { pct: 7.2, min: 2, max: 12, menu: 'fit' }, // Gusset width in relation to waist-to-upperleg + gussetLength: { pct: 12.7, min: 10, max: 16, menu: 'fit' }, // Gusset length in relation to seat + fabricStretchX: { pct: 15, min: 0, max: 100, menu: 'fit' }, // horizontal stretch (range set wide for beta testing) + fabricStretchY: {pct: 0, min: 0, max: 100, menu: 'fit' }, // vertical stretch (range set wide for beta testing) + rise: { pct: 60, min: 30, max: 100, menu: 'style' }, // extending rise beyond 100% would require adapting paths.sideLeft! + legOpening: { pct: 45, min: 5, max: 85, menu: 'style' }, + frontDip: { pct: 5.0, min: -5, max: 15, menu: 'style' }, + taperToGusset: { pct: 70, min: 5, max: 100, menu: 'style' }, + // booleans + useCrossSeam: { bool: true, menu: 'fit' }, + adjustStretch: {bool: true, menu: 'fit' }, // to not stretch fabric to the limits + }, + draft: ({ + utils, + store, + sa, + Point, + points, + Path, + paths, + Snippet, + snippets, + options, + measurements, + complete, + paperless, + macro, + part, + }) => { + // Stretch utility method + + // Use stretch inputs to calculate four different scale factors: horizontal/vertical and 'regular'/'reduced', depending on direction of the tension + // xScale: for parts that go across the body (= stretched horizontally) + // xScaleReduced: parts that are not under (horizontal) tension, e.g. the gusset + // yScale: for parts which are stretched vertically but not horizontally (anything below leg opening) + // yScaleReduced: parts which are already under horizontal stretch, which limits vertical stretch + + if (options.adjustStretch) { // roughly 15% of stretch is reserved for comfort + // horizontal: first, 'regular' stretch (for parts that go across the body) + if (options.fabricStretchX < 0.30) { + // subtract 15, but never go below 0 + store.set('xScale', utils.stretchToScale(Math.max(0 , options.fabricStretchX - 0.15))) + } else { + store.set('xScale', utils.stretchToScale(options.fabricStretchX / 2)) + // rough approximation of rule of thumb quoted in Sanne's July 29, 2021 showcase + } + // use half of whatever the regular stretch is (no util available, convert from stretch to fraction manually + store.set('xScaleReduced',(1 + store.get('xScale'))/2) + + // vertical: + if (options.fabricStretchY < 0.30) { + // subtract 15, but never go below 0 + store.set('yScale', utils.stretchToScale(Math.max(0 , options.fabricStretchY - 0.15))) + } else { + store.set('yScale', utils.stretchToScale(options.fabricStretchY / 2)) + // rough approximation of rule of thumb quoted in Sanne's July 29, 2021 showcase + } + // reduced vertical stretch calculated below, same as for non-adjusted case + } else { + // in order: regular, then reduced horizontal stretch, followed by regular vertical stretch + store.set('xScale', utils.stretchToScale(options.fabricStretchX)) + store.set('xScaleReduced', utils.stretchToScale(options.fabricStretchX / 2)) + store.set('yScale', utils.stretchToScale(options.fabricStretchY)) + } + if (options.fabricStretchY < 0.20) { + store.set('yScaleReduced',1) + } else { + // reduced yScale gradually increases from equivalent of stretch 0 to 5%, then cuts off (uses third-order polynomial) + // function to approximate Sanne's guidelines given in Discord (roughly 2.5% for stretch 30-40%, 5% above that) + store.set('yScaleReduced', utils.stretchToScale(Math.min( 0.05, 6.25 * Math.pow(options.fabricStretchY - 0.20, 3)))) + } + + // // temporarily overrule yScale and yScaleReduced + // store.set('yScale',1) + // store.set('yScaleReduced',1) + + + // // Part definition starts here + + // determine height of front part: use cross seam (and cross seam front) if selected and available + // NOTE: neither crossSeam not frontHeight are adjusted for (vertical) stretch + if (options.useCrossSeam && measurements.crossSeam) { + store.set('crossSeam',measurements.crossSeam) + } else { // use original approximation: front and back are roughly waistToUpperLeg high, plus gusset length + store.set('crossSeam',measurements.waistToUpperLeg * (1 + options.backToFrontLength) + options.gussetLength * measurements.seat) + } + // optionally use crossSeamFront to determine relative length of front and back + // this does not account for vertical stretch yet + if (options.useCrossSeam && measurements.crossSeamFront) { // subtract half the gusset length from cross seam front, and an additional 3.5% of the seat circumference to move the gusset upward (to match commercial panties) + store.set('frontHeight',measurements.crossSeamFront - measurements.seat*(0.5*options.gussetLength + options.gussetShift)) + } else { // subtract gusset length, divide by roughly 2 + store.set('frontHeight',(store.get('crossSeam') - options.gussetLength * measurements.seat)/(1 + options.backToFrontLength)) + } + + + // Create points + + // side seam is on a line from upper leg to seat to hips (optional?) to waist + points.frontWaistMid = new Point(measurements.seat / 4, 0) + points.frontWaistLeft = new Point( + measurements.seat / 4 - (measurements.waist / 4) * store.get('xScale'), + 0 + ) + points.frontSeatLeft = new Point( + measurements.seat / 4 - (measurements.seat / 4) * store.get('xScale'), + measurements.waistToSeat * store.get('yScaleReduced') + ) + points.frontUpperLegLeft = new Point( + measurements.seat / 4 - (measurements.seat / 4) * store.get('xScale'), // assume same circ. as seat + measurements.waistToUpperLeg * store.get('yScaleReduced') + ) + points.frontHipLeft = new Point( + measurements.seat / 4 - (measurements.hips / 4) * store.get('xScale'), + measurements.waistToHips * store.get('yScaleReduced') + ) + + // use these points to define an invisible path + paths.sideLeft = new Path() + .move(points.frontUpperLegLeft) + .line(points.frontSeatLeft) + .line(points.frontHipLeft) + .line(points.frontWaistLeft) + .setRender(false) // only show when debugging + + /* Waist band is somewhere on the sideLeft path */ + points.frontWaistBandLeft = paths.sideLeft.shiftFractionAlong(options.rise) + points.frontWaistBandRight = points.frontWaistBandLeft.flipX(points.frontWaistMid) + points.frontWaistBandMid = points.frontWaistBandLeft + .shiftFractionTowards(points.frontWaistBandRight, 0.5) + .shift(270, measurements.waistToUpperLeg * options.frontDip) /* Waist band dip */ + + /* Leg opening is also on the sideLeft path, and cannot be higher than rise */ + /* Minimum side seam length is defined as 3.5% of the sideLeft path (which is at least waistToUpperLeg long) */ + store.set('adjustedLegOpening',Math.min(options.legOpening,options.rise - 0.035)) // TODO: account for rise having a different domain + + points.frontLegOpeningLeft = paths.sideLeft.shiftFractionAlong(store.get('adjustedLegOpening')) + points.frontLegOpeningRight = points.frontLegOpeningLeft.flipX(points.frontWaistMid) // Waist band low point + + // calculate the actual front height, using yScale above and yScaleReduced below leg opening + store.set('frontHeightAbove',points.frontWaistLeft.dy(points.frontLegOpeningLeft)) + + var frontHeightBelow + frontHeightBelow = store.get('yScale')*(store.get('frontHeight') - store.get('frontHeightAbove')/store.get('yScaleReduced')) + + var frontHeightReduced + frontHeightReduced = frontHeightBelow + store.get('frontHeightAbove') + + // gusset width uses modified xScale (barely stretches) and depends on waistToUpperLeg - least sensitive to girth + points.frontGussetLeft = new Point( + measurements.seat / 4 - (measurements.waistToSeat * options.gussetWidth * store.get('xScaleReduced')) * 2.2, + frontHeightReduced + ) + points.frontGussetMid = new Point(measurements.seat / 4, frontHeightReduced) + + /* Flip points to right side */ + points.frontGussetRight = points.frontGussetLeft.flipX(points.frontWaistMid) + points.frontHipRight = points.frontSeatLeft.flipX(points.frontWaistMid) + points.frontWaistRight = points.frontWaistLeft.flipX(points.frontWaistMid) + + /* Middle point for label */ + points.frontMidMid = points.frontLegOpeningLeft.shiftFractionTowards( + points.frontLegOpeningRight, + 0.5 + ) + + // Create control points + + /* Control points for leg opening curves */ + points.frontLegOpeningLeftCp1 = points.frontLegOpeningLeft.shift( + 180, + points.frontGussetLeft.dy(points.frontLegOpeningLeft) / 3 + ) + + /* Control point above gusset moves higher as taperToGusset (= front exposure) increases, but is limited by both the leg opening (allow minimal arching only) and the rise (leg opening must not intersect the waist band) */ + points.frontGussetLeftCp1 = points.frontGussetLeft + .shift(270, Math.max(Math.max(points.frontGussetLeft.dy(points.frontWaistMid) * options.taperToGusset / 2,points.frontGussetLeft.dy(points.frontLegOpeningLeft) * 2),points.frontGussetLeft.dy(points.frontWaistBandMid))) + + /* Control point for waistband dip */ + points.frontWaistBandLeftCp1 = points.frontWaistBandMid.shift(0,points.frontWaistBandMid.dx(points.frontWaistBandLeft) / 3 ) + + + /* Flip control points to right side */ + points.frontGussetRightCp1 = points.frontGussetLeftCp1.flipX(points.frontWaistMid) + points.frontLegOpeningRightCp1 = points.frontLegOpeningLeftCp1.flipX(points.frontWaistMid) + points.frontWaistBandRightCp1 = points.frontWaistBandLeftCp1.flipX(points.frontWaistMid) + + // Draw paths + + paths.seam = new Path() + .move(points.frontWaistBandMid) + .curve(points.frontWaistBandLeftCp1, points.frontWaistBandLeft, points.frontWaistBandLeft) // Waist band dip + .line(points.frontLegOpeningLeft) + .curve(points.frontLegOpeningLeftCp1, points.frontGussetLeftCp1, points.frontGussetLeft) + .line(points.frontGussetMid) + .line(points.frontGussetRight) + .curve(points.frontGussetRightCp1, points.frontLegOpeningRightCp1, points.frontLegOpeningRight) + .line(points.frontWaistBandRight) + .curve(points.frontWaistBandRight, points.frontWaistBandRightCp1, points.frontWaistBandMid) // Waist band dip + .close() + .attr('class', 'fabric') + + // Store points for use in other parts + + /* Store side seam points for use in back */ + + store.set('sideSeamWaist', points.frontWaistBandLeft) + store.set('sideSeamHip', points.frontLegOpeningLeft) + + /* Store gusset points for use in gusset */ + + store.set('frontGussetLeft', points.frontGussetLeft) + store.set('frontGussetRight', points.frontGussetRight) + store.set('frontGussetMid', points.frontGussetMid) + + /* Store lengths for use in elastic */ + + paths.frontLegOpening = new Path() + .move(points.frontGussetRight) + .curve( + points.frontGussetRightCp1, + points.frontLegOpeningRightCp1, + points.frontLegOpeningRight + ) + .setRender(false) + store.set('frontLegOpeningLength',paths.frontLegOpening.length()) + + paths.frontWaistBand = new Path() + .move(points.frontWaistBandRight) + .curve( + points.frontWaistBandRightCp1, + points.frontWaistBandLeftCp1, + points.frontWaistBandLeft + ) + .setRender(false) + store.set('frontWaistBandLength',paths.frontWaistBand.length()) + + // Complete? + if (complete) { + if (sa) { + paths.sa = paths.seam.offset(sa).attr('class', 'fabric sa') + } + } + + macro('title', { + at: points.frontMidMid, + nr: 1, + title: 'front', + }) + + macro('grainline', { + from: points.frontGussetMid, + to: points.frontGussetMid.shiftFractionTowards(points.frontWaistBandMid, 0.5), + }) + + // Paperless? + if (paperless) { + macro('hd', { + from: points.frontWaistBandRight, + to: points.frontWaistBandLeft, + y: points.frontWaistBandRight.y + sa - 15, + }) + macro('hd', { + from: points.frontLegOpeningRight, + to: points.frontLegOpeningLeft, + y: points.frontLegOpeningRight.y + sa - 15, + }) + macro('hd', { + from: points.frontGussetLeft, + to: points.frontGussetRight, + y: points.frontGussetLeft.y + sa + 15, + }) + macro('vd', { + from: points.frontWaistBandMid, + to: points.frontGussetMid, + x: points.frontWaistBandMid.x + sa + 15, + }) + macro('ld', { + from: points.frontWaistBandLeft, + to: points.frontLegOpeningLeft, + d: points.frontWaistBandLeft.y + sa - 15, + }) + macro('pd', { + path: new Path() + .move(points.frontGussetRight) + .curve( + points.frontGussetRightCp1, + points.frontLegOpeningRightCp1, + points.frontLegOpeningRight + ), + d: 15, + }) + /* macro('vd', { + from: points.frontWaistBandLeft, + to: points.frontWaistBandMid, + x: points.frontWaistBandMid.x + sa + 15, + }) */ + } + + return part + }, +} diff --git a/designs/unice/src/gusset.mjs b/designs/unice/src/gusset.mjs new file mode 100644 index 00000000000..39b86acb13d --- /dev/null +++ b/designs/unice/src/gusset.mjs @@ -0,0 +1,4 @@ +const method = part => part + +export default method + diff --git a/designs/unice/src/index.mjs b/designs/unice/src/index.mjs new file mode 100644 index 00000000000..b8c55238e26 --- /dev/null +++ b/designs/unice/src/index.mjs @@ -0,0 +1,14 @@ +import { Design } from '@freesewing/core' +import { front } from './front.mjs' +// import { back } from './back.mjs' +// import { gusset } from './gusset.mjs' +import { data } from '../data.mjs' + +// Setup our new design +const Unice = new Design({ + data, + parts: [front], // parts: [back, front], +}) + +// Named exports +export {front, Unice} // export { back, front, gusset, Unice } \ No newline at end of file diff --git a/designs/unice/tests/shared.test.mjs b/designs/unice/tests/shared.test.mjs new file mode 100644 index 00000000000..13c82f4ee71 --- /dev/null +++ b/designs/unice/tests/shared.test.mjs @@ -0,0 +1,16 @@ +// This file is auto-generated | Any changes you make will be overwritten. +import { Unice } from './dist/index.mjs' + +// Shared tests +import { testPatternConfig } from '../../../tests/designs/config.mjs' +import { testPatternDrafting } from '../../../tests/designs/drafting.mjs' +import { testPatternSampling } from '../../../tests/designs/sampling.mjs' + +// Test config +testPatternConfig(Unice) + +// Test drafting - Change the second parameter to `true` to log errors +testPatternDrafting(Unice, false) + +// Test sampling - Change the second parameter to `true` to log errors +testPatternSampling(Unice, false)