1
0
Fork 0

Merge branch 'develop' into more-lint

This commit is contained in:
Joost De Cock 2022-09-28 19:09:50 +02:00 committed by GitHub
commit c0ede14d74
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
118 changed files with 2870 additions and 4892 deletions

View file

@ -649,6 +649,12 @@
- Migrated from Rollup to Esbuild for all builds
### unice
#### Added
- Unice is an underwear pattern
### ursula
#### Changed
@ -679,12 +685,6 @@
### yuri
#### Changed
- Migrated from Rollup to Esbuild for all builds
### examples
#### Changed
- Migrated from Rollup to Esbuild for all builds
@ -1272,16 +1272,6 @@
### yuri
#### Changed
- Switched to default import for version from package.json
### examples
#### Added
- Added plugin_gore example
#### Changed
- Switched to default import for version from package.json
@ -2537,13 +2527,6 @@
- Added the (disabled) waistbandHeight option from Titan
- Changed to Titan's waistbandWidth option
### examples
#### Added
- Added examples for bartack plugin
- Added examples for new buttonhole-start/end snippets
### plugin-buttons
#### Added
@ -4029,12 +4012,6 @@
### wahid
#### Added
- Initial release
### examples
#### Added
- Initial release

View file

@ -65,10 +65,6 @@ diana:
peer:
'@freesewing/brian': *freesewing
'@freesewing/plugin-bust': *freesewing
examples:
peer:
'@freesewing/plugin-mirror': *freesewing
'@freesewing/plugin-gore': *freesewing
holmes:
_:
'@freesewing/plugin-gore': *freesewing

View file

@ -24,10 +24,6 @@ design:
- pattern
- sewing
- sewing pattern
examples:
- documentation
- example
- parametric design
i18n:
- i18n
- internationalisation

View file

@ -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",
@ -321,10 +328,6 @@
}
},
"utilities": {
"examples": {
"description": "A FreeSewing pattern holding examples for our documentation",
"code": "Joost De Cock"
},
"legend": {
"description": "A FreeSewing pattern to document pattern notation",
"code": "Joost De Cock"

View file

@ -6,52 +6,52 @@ const crossBox = {
name: 'crossbox',
version,
macros: {
crossBox: function (so) {
let id = this.getId()
crossBox: function (so, { points, Point, paths, Path, getId }) {
let id = getId()
let shiftFraction = 0.1
this.points[id + '_boxTopLeft'] = so.from.copy()
this.points[id + '_boxBottomRight'] = so.to.copy()
this.points[id + '_boxTopRight'] = new this.Point(so.to.x, so.from.y)
this.points[id + '_boxBottomLeft'] = new this.Point(so.from.x, so.to.y)
points[id + '_boxTopLeft'] = so.from.copy()
points[id + '_boxBottomRight'] = so.to.copy()
points[id + '_boxTopRight'] = new Point(so.to.x, so.from.y)
points[id + '_boxBottomLeft'] = new Point(so.from.x, so.to.y)
this.points[id + '_topCrossTL'] = this.points[id + '_boxTopLeft'].shiftFractionTowards(
this.points[id + '_boxBottomRight'],
points[id + '_topCrossTL'] = points[id + '_boxTopLeft'].shiftFractionTowards(
points[id + '_boxBottomRight'],
shiftFraction
)
this.points[id + '_topCrossTR'] = this.points[id + '_boxTopRight'].shiftFractionTowards(
this.points[id + '_boxBottomLeft'],
points[id + '_topCrossTR'] = points[id + '_boxTopRight'].shiftFractionTowards(
points[id + '_boxBottomLeft'],
shiftFraction
)
this.points[id + '_topCrossBL'] = this.points[id + '_boxBottomLeft'].shiftFractionTowards(
this.points[id + '_boxTopRight'],
points[id + '_topCrossBL'] = points[id + '_boxBottomLeft'].shiftFractionTowards(
points[id + '_boxTopRight'],
shiftFraction
)
this.points[id + '_topCrossBR'] = this.points[id + '_boxBottomRight'].shiftFractionTowards(
this.points[id + '_boxTopLeft'],
points[id + '_topCrossBR'] = points[id + '_boxBottomRight'].shiftFractionTowards(
points[id + '_boxTopLeft'],
shiftFraction
)
this.paths[id + 'crossBox'] = new this.Path()
.move(this.points[id + '_boxTopLeft'])
.line(this.points[id + '_boxTopRight'])
.line(this.points[id + '_boxBottomRight'])
.line(this.points[id + '_boxBottomLeft'])
.line(this.points[id + '_boxTopLeft'])
paths[id + 'crossBox'] = new Path()
.move(points[id + '_boxTopLeft'])
.line(points[id + '_boxTopRight'])
.line(points[id + '_boxBottomRight'])
.line(points[id + '_boxBottomLeft'])
.line(points[id + '_boxTopLeft'])
.close()
.attr('class', 'lining dotted stroke-sm')
this.paths[id + '_topCross'] = new this.Path()
.move(this.points[id + '_topCrossTL'])
.line(this.points[id + '_topCrossBR'])
.line(this.points[id + '_topCrossTR'])
.line(this.points[id + '_topCrossBL'])
.line(this.points[id + '_topCrossTL'])
.line(this.points[id + '_topCrossTR'])
.move(this.points[id + '_topCrossBR'])
.line(this.points[id + '_topCrossBL'])
paths[id + '_topCross'] = new Path()
.move(points[id + '_topCrossTL'])
.line(points[id + '_topCrossBR'])
.line(points[id + '_topCrossTR'])
.line(points[id + '_topCrossBL'])
.line(points[id + '_topCrossTL'])
.line(points[id + '_topCrossTR'])
.move(points[id + '_topCrossBR'])
.line(points[id + '_topCrossBL'])
.attr('class', 'lining dotted stroke-sm')
if (typeof so.text === 'string') {
this.points.textAnchor = this.points[id + '_boxTopLeft']
.shiftFractionTowards(this.points[id + '_boxBottomRight'], 0.5)
points.textAnchor = points[id + '_boxTopLeft']
.shiftFractionTowards(points[id + '_boxBottomRight'], 0.5)
.attr('data-text', so.text)
.attr('data-text-class', 'center')
}

View file

@ -27,7 +27,7 @@ function draftCharlieFlyExtension({ points, paths, Path, complete, macro, sa, pa
.line(points.styleWaistIn)
.line(points.flyTop)
.hide()
paths.seam = paths.saBase.clone().line(points.flyCorner).close().hide().attr('class', 'fabric')
paths.seam = paths.saBase.clone().line(points.flyCorner).close().unhide().attr('class', 'fabric')
if (complete) {
macro('cutonfold', {

View file

@ -1,38 +0,0 @@
# Change log for: @freesewing/examples
## 2.21.0 (2022-06-27)
### Changed
- Migrated from Rollup to Esbuild for all builds
## 2.20.0 (2022-01-24)
### Added
- Added plugin_gore example
### Changed
- Switched to default import for version from package.json
## 2.15.0 (2021-04-15)
### Added
- Added examples for bartack plugin
- Added examples for new buttonhole-start/end snippets
## 2.0.0 (2019-08-25)
### Added
- Initial release
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.

View file

@ -1,122 +0,0 @@
import pkg from '../package.json' assert { type: 'json' }
const { version } = pkg
export default {
version,
name: 'examples',
design: 'Joost De Cock',
code: 'Joost De Cock',
department: 'womenswear',
type: 'pattern',
difficulty: 1,
tags: ['example'],
optionGroups: {
fit: ['fixme'],
},
measurements: [],
dependencies: {
//point_attr: 'path_attr'
},
parts: [
'point_attr',
'path_move',
'path_line',
'path_curve',
'path__curve',
'path_curve_',
'path_close',
'path_ops',
'path_attr',
'path_clone',
'path_divide',
'path_edge',
'path_end',
'path_intersects',
'path_intersectsx',
'path_intersectsy',
'path_join',
'path_length',
'path_noop',
'path_offset',
'path_reverse',
'path_shiftalong',
'path_shiftfractionalong',
'path_split',
'path_start',
'path_translate',
'path_trim',
'plugin_banner',
'plugin_bartack',
'plugin_bartackalong',
'plugin_bartackfractionalong',
'plugin_buttons',
'plugin_cutonfold',
'plugin_dimension',
'plugin_gore',
'plugin_grainline',
'plugin_logo',
'plugin_mirror',
'plugin_notches',
'plugin_round',
'plugin_scalebox',
'plugin_sprinkle',
'plugin_title',
'point_angle',
'point_attr',
'point_clone',
'point_copy',
'point_dist',
'point_dx',
'point_dy',
'point_flipx',
'point_flipy',
'point_shift',
'point_shiftfractiontowards',
'point_shifttowards',
'point_shiftoutwards',
'point_sitson',
'point_sitsroughlyon',
'point_rotate',
'point_translate',
'settings_sa',
'snippet',
'snippet_attr',
'snippet_clone',
'snippets_bnotch',
'snippets_notch',
'snippets_button',
'snippets_buttonhole',
'snippets_buttonhole_start',
'snippets_buttonhole_end',
'snippets_snapsocket',
'snippets_snapstud',
'snippets_logo',
'utils_linesintersect',
'utils_beamsintersect',
'utils_beamintersectsx',
'utils_beamintersectsy',
'utils_lineintersectscurve',
'utils_curvesintersect',
'utils_pointonbeam',
'utils_pointonline',
'utils_pointoncurve',
'utils_circlesintersect',
'utils_beamintersectscircle',
'utils_lineintersectscircle',
'utils_curveintersectsy',
'utils_curveintersectsx',
'utils_splitcurve',
'docs_overview',
'docs_coords',
],
options: {
focus: '',
// Optiongroups are needed for now, because workbench
fixme: {
pct: 50,
min: 0,
max: 100,
},
},
}

View file

@ -1,321 +0,0 @@
import { box } from './shared.mjs'
export const docs_coords = {
name: 'examples.box_coords',
draft: ({ Point, points, paths, Path, part }) => {
points.origin = new Point(10, 10)
points.x = new Point(100, 10)
points.y = new Point(10, 50)
points.textX = points.x.shift(135, 2).attr('data-text', 'X')
points.textY = points.y.shift(180, 5).attr('data-text', 'Y')
paths.coords = new Path()
.move(points.y)
.line(points.origin)
.line(points.x)
.attr('class', 'mark')
.attr('marker-start', 'url(#dimensionFrom)')
.attr('marker-end', 'url(#dimensionTo)')
return box(part, 100, 50)
},
}
export const docs_overview = {
name: 'examples.docs_overview',
draft: ({ Point, points, Path, paths, options, part }) => {
/**
* Returs the value passed to it randomized with a given tolerance
*/
const about = (value, tolerance = 5) => {
let randomized = (tolerance / 100) * Math.random() * value
let fixed = (1 - tolerance / 100) * value
return fixed + randomized
}
/**
* like about, but also randomly makes value negative
* This is for degrees
*/
const nabout = (value, tolerance = 5) => {
if (Math.random() > 0.5) return about(value, tolerance)
else return -1 * about(value, tolerance)
}
/**
* Draws a w*h box that's randomized by tolerance to give it
* that hand-drawn look.
*
* Returns a Path object
*/
const box = (name, origin, width, height, tolerance = 10) => {
let base = height
if (width < height) base = width
let t = base * (tolerance / 100)
points[name + 'TopLeft'] = new Point(about(origin.x, t), about(origin.y, t))
points[name + 'BottomLeft'] = new Point(about(origin.x, t), about(origin.y + height, t))
points[name + 'BottomRight'] = new Point(
about(origin.x + width, t),
about(origin.y + height, t)
)
points[name + 'TopRight'] = new Point(about(origin.x + width, t), about(origin.y, t))
points[name + 'Mid'] = points[name + 'TopLeft'].shiftFractionTowards(
points[name + 'BottomRight'],
0.5
)
points[name + 'Mid'].y += 3
let f = 0.3
let r = tolerance / 2
points[name + 'TopLeftCp1'] = points[name + 'TopLeft']
.shiftFractionTowards(points[name + 'BottomLeft'], about(f))
.rotate(nabout(r), points[name + 'TopLeft'])
points[name + 'TopLeftCp2'] = points[name + 'TopLeft']
.shiftFractionTowards(points[name + 'TopRight'], about(f))
.rotate(nabout(r), points[name + 'TopLeft'])
points[name + 'BottomLeftCp1'] = points[name + 'BottomLeft']
.shiftFractionTowards(points[name + 'TopLeft'], about(f))
.rotate(nabout(r), points[name + 'BottomLeft'])
points[name + 'BottomLeftCp2'] = points[name + 'BottomLeft']
.shiftFractionTowards(points[name + 'BottomRight'], about(f))
.rotate(nabout(r), points[name + 'BottomLeft'])
points[name + 'BottomRightCp1'] = points[name + 'BottomRight']
.shiftFractionTowards(points[name + 'BottomLeft'], about(f))
.rotate(nabout(r), points[name + 'BottomRight'])
points[name + 'BottomRightCp2'] = points[name + 'BottomRight']
.shiftFractionTowards(points[name + 'TopRight'], about(f))
.rotate(nabout(r), points[name + 'BottomRight'])
points[name + 'TopRightCp1'] = points[name + 'TopRight']
.shiftFractionTowards(points[name + 'BottomRight'], about(f))
.rotate(nabout(r), points[name + 'TopRight'])
points[name + 'TopRightCp2'] = points[name + 'TopRight']
.shiftFractionTowards(points[name + 'TopLeft'], about(f))
.rotate(nabout(r), points[name + 'TopRight'])
return new Path()
.move(points[name + 'TopLeft'])
.curve(
points[name + 'TopLeftCp1'],
points[name + 'BottomLeftCp1'],
points[name + 'BottomLeft']
)
.curve(
points[name + 'BottomLeftCp2'],
points[name + 'BottomRightCp1'],
points[name + 'BottomRight']
)
.curve(
points[name + 'BottomRightCp2'],
points[name + 'TopRightCp1'],
points[name + 'TopRight']
)
.curve(points[name + 'TopRightCp2'], points[name + 'TopLeftCp2'], points[name + 'TopLeft'])
.close()
.attr('class', options.focus === name ? 'note' : 'fabric')
}
/**
* Draws an arrow from to
* Returns a Path object
*/
const arrow = (name, text = '', tolerance = 10) => {
let from = points[name + 'From']
let to = points[name + 'To']
from = from.shiftTowards(to, 3)
to = to.shiftTowards(from, 3)
let base = from.dist(to)
let t = base * (tolerance / 100)
from.x = about(from.x, t)
from.x = about(from.x, t)
to.x = about(to.x, t)
to.x = about(to.x, t)
let f = 0.3
let r = tolerance / 2
points[name + 'FromCp'] = from.shiftFractionTowards(to, about(f)).rotate(nabout(r), from)
points[name + 'ToCp'] = to.shiftFractionTowards(from, about(f)).rotate(nabout(r), to)
points[name + 'Tip1'] = to.shiftTowards(from, about(3.8)).rotate(about(15), to)
points[name + 'Tip2'] = to.shiftTowards(from, about(3.5)).rotate(about(-15), to)
let path = new Path()
.move(from)
.curve(points[name + 'FromCp'], points[name + 'ToCp'], to)
.move(points[name + 'Tip1'])
.line(to)
.line(points[name + 'Tip2'])
.attr('class', 'fabric')
if (options.focus === name) path = path.attr('class', 'note')
if (text)
return path
.attr('data-text', ' ' + text)
.attr('data-text-class', 'scribble')
.attr('data-text-class', options.focus === name ? 'fill-note' : '')
else return path
}
const drawBox = (name, x, y, width, height, tolerance = 5, text = true) => {
points[name + 'Origin'] = new Point(x, y)
paths[name] = box(name, points[name + 'Origin'], width, height, tolerance)
if (text) {
points[name + 'Mid'].attr('data-text', name).attr('data-text-class', 'center scribble')
if (options.focus === name) points[name + 'Mid'].attr('data-text-class', 'fill-note')
}
}
const svgLogo = (anchor, size = 1) => {
points.svg15 = anchor
.shift(45, 4 * size)
.attr('data-circle', 1.2 * size)
.attr('data-circle-class', 'fill-fabric')
points.svg3 = anchor
.shift(0, 4 * size)
.attr('data-circle', 1.2 * size)
.attr('data-circle-class', 'fill-fabric')
points.svg45 = anchor
.shift(-45, 4 * size)
.attr('data-circle', 1.2 * size)
.attr('data-circle-class', 'fill-fabric')
points.svg6 = anchor
.shift(-90, 4 * size)
.attr('data-circle', 1.2 * size)
.attr('data-circle-class', 'fill-fabric')
points.svg75 = anchor
.shift(-135, 4 * size)
.attr('data-circle', 1.2 * size)
.attr('data-circle-class', 'fill-fabric')
points.svg9 = anchor
.shift(180, 4 * size)
.attr('data-circle', 1.2 * size)
.attr('data-circle-class', 'fill-fabric')
points.svg105 = anchor
.shift(135, 4 * size)
.attr('data-circle', 1.2 * size)
.attr('data-circle-class', 'fill-fabric')
points.svg12 = anchor
.shift(90, 4 * size)
.attr('data-circle', 1.2 * size)
.attr('data-circle-class', 'fill-fabric')
points.svgText = anchor
.clone()
.attr('data-text', 'SVG')
.attr('data-text-class', 'text-xl scribble')
points.svgText.x += size * 7
points.svgText.y += 6
paths.svgLogo = new Path()
.move(points.svg15)
.line(points.svg75)
.move(points.svg3)
.line(points.svg9)
.move(points.svg45)
.line(points.svg105)
.move(points.svg6)
.line(points.svg12)
.attr('class', 'stroke-l')
}
const reactLogo = (anchor, size = 1) => {
h = 3 * size
w = 6 * size
points.reactTop1 = anchor.shift(45, w)
points.reactBottom1 = anchor.shift(-135, w)
points.reactTop1Cp1 = points.reactTop1.shift(135, h)
points.reactTop1Cp2 = points.reactTop1.shift(-45, h)
points.reactBottom1Cp1 = points.reactBottom1.shift(135, h)
points.reactBottom1Cp2 = points.reactBottom1.shift(-45, h)
points.reactTop2 = points.reactTop1.rotate(60, anchor)
points.reactBottom2 = points.reactBottom1.rotate(60, anchor)
points.reactTop2Cp1 = points.reactTop1Cp1.rotate(60, anchor)
points.reactTop2Cp2 = points.reactTop1Cp2.rotate(60, anchor)
points.reactBottom2Cp1 = points.reactBottom1Cp1.rotate(60, anchor)
points.reactBottom2Cp2 = points.reactBottom1Cp2.rotate(60, anchor)
points.reactTop3 = points.reactTop1.rotate(-60, anchor)
points.reactBottom3 = points.reactBottom1.rotate(-60, anchor)
points.reactTop3Cp1 = points.reactTop1Cp1.rotate(-60, anchor)
points.reactTop3Cp2 = points.reactTop1Cp2.rotate(-60, anchor)
points.reactBottom3Cp1 = points.reactBottom1Cp1.rotate(-60, anchor)
points.reactBottom3Cp2 = points.reactBottom1Cp2.rotate(-60, anchor)
points.svgLogo = anchor
.clone()
.attr('data-circle', 1 * size)
.attr('data-circle-class', 'fill-fabric')
points.reactText = anchor
.clone()
.attr('data-text', 'React')
.attr('data-text-class', 'text-xl scribble')
points.reactText.x += size * 7
points.reactText.y += 6
paths.reactLogo = new Path()
.move(points.reactTop1)
.curve(points.reactTop1Cp1, points.reactBottom1Cp1, points.reactBottom1)
.curve(points.reactBottom1Cp2, points.reactTop1Cp2, points.reactTop1)
.close()
.move(points.reactTop2)
.curve(points.reactTop2Cp1, points.reactBottom2Cp1, points.reactBottom2)
.curve(points.reactBottom2Cp2, points.reactTop2Cp2, points.reactTop2)
.close()
.move(points.reactTop3)
.curve(points.reactTop3Cp1, points.reactBottom3Cp1, points.reactBottom3)
.curve(points.reactBottom3Cp2, points.reactTop3Cp2, points.reactTop3)
.close()
}
// Other parts first so they're behind
drawBox('Part4', 4, -19, 40, 65, 5, false)
drawBox('Part3', 1, -16, 40, 65, 5, false)
drawBox('Part2', -2, -13, 40, 65, 5, false)
drawBox('Part', -5, -10, 40, 65, 5)
paths.Part4.attr('class', 'fill-bg')
paths.Part3.attr('class', 'fill-bg')
paths.Part2.attr('class', 'fill-bg')
paths.Part.attr('class', 'fill-bg')
points.PartMid.y = points.PartTopLeft.y + 9
let x = 0
let y = 0
let w = 30
let h = 15
drawBox('Points', x, y, w, h)
y += 18
drawBox('Paths', x, y, w, h)
y += 18
drawBox('Snippets', x, y, w, h)
x = -35
y = -3
w = 25
h = 20
drawBox('config', x, y, w, h)
y += 23
drawBox('Store', x, y, w, h)
x = -40
y = -30
drawBox('Pattern', x, y, 90, 90)
points.PatternMid.y = points.PatternTopLeft.y + 9
drawBox('settings', -100, 6, 40, 20)
drawBox('draft', 80, 3, 20, 25)
points.arrow1From = points.settingsTopRight.shiftFractionTowards(
points.settingsBottomRight,
0.5
)
points.arrow1To = points.PatternTopLeft.shiftFractionTowards(points.PatternBottomLeft, 0.5)
paths.arrow1 = arrow('arrow1')
points.arrow2From = points.PatternTopRight.shiftFractionTowards(points.PatternBottomRight, 0.5)
points.arrow2To = points.draftTopLeft.shiftFractionTowards(points.draftBottomLeft, 0.5)
paths.arrow2 = arrow('arrow2', 'draft()')
svgLogo(points.draftMid.shift(70, 50))
reactLogo(points.draftMid.shift(-80, 36))
points.arrow3From = points.draftTopLeft.shiftFractionTowards(points.draftTopRight, 0.5)
points.arrow3To = points.svgText.clone()
paths.arrow3 = arrow('arrow3', 'render()')
points.arrow4From = points.draftBottomLeft.shiftFractionTowards(points.draftBottomRight, 0.5)
points.arrow4To = points.reactText.clone().shift(40, 15)
paths.arrow4 = arrow('arrow4')
paths.extend = new Path()
.move(points.draftTopRight)
.line(points.draftTopRight.shift(0, 40))
.attr('class', 'hidden')
return part
},
}

View file

@ -1,384 +0,0 @@
import { Design } from '@freesewing/core'
import { pluginBundle } from '@freesewing/plugin-bundle'
import { gorePlugin } from '@freesewing/plugin-gore'
import { data } from '../data.mjs'
// Path API
import {
path__curve,
path_addclass,
path_attr,
path_move,
path_line,
path_curve,
path_curve_,
path_close,
path_ops,
path_clone,
path_divide,
path_edge,
path_end,
path_intersects,
path_intersectsx,
path_intersectsy,
path_join,
path_length,
path_noop,
path_offset,
path_reverse,
path_shiftalong,
path_shiftfractionalong,
path_smurve,
path_smurve_,
path_split,
path_start,
path_translate,
path_trim,
} from './path.mjs'
// Point API
import {
point_addcircle,
point_addtext,
point_angle,
point_attr,
point_clone,
point_copy,
point_dist,
point_dx,
point_dy,
point_flipx,
point_flipy,
point_setcircle,
point_settext,
point_shift,
point_shiftfractiontowards,
point_shifttowards,
point_shiftoutwards,
point_sitson,
point_sitsroughlyon,
point_rotate,
point_translate,
} from './point.mjs'
// Snippet API
import { snippet, snippet_attr, snippet_clone } from './snippet.mjs'
// Utils API
import {
utils_linesintersect,
utils_beamsintersect,
utils_beamintersectsx,
utils_beamintersectsy,
utils_lineintersectscurve,
utils_curvesintersect,
utils_pointonbeam,
utils_pointonline,
utils_pointoncurve,
utils_circlesintersect,
utils_beamintersectscircle,
utils_lineintersectscircle,
utils_curveintersectsx,
utils_curveintersectsy,
utils_splitcurve,
} from './utils.mjs'
// Plugins
import {
plugin_banner,
plugin_bartack,
plugin_bartackalong,
plugin_bartackfractionalong,
plugin_buttons,
plugin_cutonfold,
plugin_dimension,
plugin_gore,
plugin_grainline,
plugin_logo,
plugin_mirror,
plugin_notches,
plugin_round,
plugin_sprinkle,
plugin_scalebox,
plugin_title,
} from './plugins.mjs'
// Snippets
import {
snippet_bnotch,
snippet_notch,
snippet_button,
snippet_buttonhole,
snippet_buttonholestart,
snippet_buttonholeend,
snippet_snapsocket,
snippet_snapstud,
snippet_logo,
} from './snippets.mjs'
// Stacks
import {
stacks_top,
stacks_left,
stacks_right,
stacks_bottom,
stacks_leftEye,
stacks_rightEye,
stacks_mouth,
} from './stacks.mjs'
// Settings
import { settings_sa } from './settings.mjs'
// Docs illustrations
import { docs_coords, docs_overview } from './docs.mjs'
// Setup our new design
const Examples = new Design({
data,
parts: [
// Path API
path__curve,
path_addclass,
path_attr,
path_move,
path_line,
path_curve,
path_curve_,
path_close,
path_ops,
path_clone,
path_divide,
path_edge,
path_end,
path_intersects,
path_intersectsx,
path_intersectsy,
path_join,
path_length,
path_noop,
path_offset,
path_reverse,
path_shiftalong,
path_shiftfractionalong,
path_smurve,
path_smurve_,
path_split,
path_start,
path_translate,
path_trim,
// Point API
point_addcircle,
point_addtext,
point_angle,
point_attr,
point_clone,
point_copy,
point_dist,
point_dx,
point_dy,
point_flipx,
point_flipy,
point_setcircle,
point_settext,
point_shift,
point_shiftfractiontowards,
point_shifttowards,
point_shiftoutwards,
point_sitson,
point_sitsroughlyon,
point_rotate,
point_translate,
// Snippet API
snippet,
snippet_attr,
snippet_clone,
// Utils API
utils_linesintersect,
utils_beamsintersect,
utils_beamintersectsx,
utils_beamintersectsy,
utils_lineintersectscurve,
utils_curvesintersect,
utils_pointonbeam,
utils_pointonline,
utils_pointoncurve,
utils_circlesintersect,
utils_beamintersectscircle,
utils_lineintersectscircle,
utils_curveintersectsx,
utils_curveintersectsy,
utils_splitcurve,
// Plugins
plugin_banner,
plugin_bartack,
plugin_bartackalong,
plugin_bartackfractionalong,
plugin_buttons,
plugin_cutonfold,
plugin_dimension,
plugin_gore,
plugin_grainline,
plugin_logo,
plugin_mirror,
plugin_notches,
plugin_round,
plugin_sprinkle,
plugin_scalebox,
plugin_title,
// Snippets
snippet_bnotch,
snippet_notch,
snippet_button,
snippet_buttonhole,
snippet_buttonholestart,
snippet_buttonholeend,
snippet_snapsocket,
snippet_snapstud,
snippet_logo,
// Stacks
stacks_top,
stacks_left,
stacks_right,
stacks_bottom,
stacks_leftEye,
stacks_rightEye,
stacks_mouth,
// Settings
settings_sa,
// Docs
docs_coords,
docs_overview,
],
plugins: [pluginBundle, gorePlugin],
})
// Named exports
export {
// Path API
path__curve,
path_addclass,
path_attr,
path_move,
path_line,
path_curve,
path_curve_,
path_close,
path_ops,
path_clone,
path_divide,
path_edge,
path_end,
path_intersects,
path_intersectsx,
path_intersectsy,
path_join,
path_length,
path_noop,
path_offset,
path_reverse,
path_shiftalong,
path_shiftfractionalong,
path_smurve,
path_smurve_,
path_split,
path_start,
path_translate,
path_trim,
// Point API
point_addcircle,
point_addtext,
point_angle,
point_attr,
point_clone,
point_copy,
point_dist,
point_dx,
point_dy,
point_flipx,
point_flipy,
point_setcircle,
point_settext,
point_shift,
point_shiftfractiontowards,
point_shifttowards,
point_shiftoutwards,
point_sitson,
point_sitsroughlyon,
point_rotate,
point_translate,
// Snippet API
snippet,
snippet_attr,
snippet_clone,
// Utils API
utils_linesintersect,
utils_beamsintersect,
utils_beamintersectsx,
utils_beamintersectsy,
utils_lineintersectscurve,
utils_curvesintersect,
utils_pointonbeam,
utils_pointonline,
utils_pointoncurve,
utils_circlesintersect,
utils_beamintersectscircle,
utils_lineintersectscircle,
utils_curveintersectsx,
utils_curveintersectsy,
utils_splitcurve,
// Plugins
plugin_banner,
plugin_bartack,
plugin_bartackalong,
plugin_bartackfractionalong,
plugin_buttons,
plugin_cutonfold,
plugin_dimension,
plugin_gore,
plugin_grainline,
plugin_logo,
plugin_mirror,
plugin_notches,
plugin_round,
plugin_sprinkle,
plugin_scalebox,
plugin_title,
// Snippets
snippet_bnotch,
snippet_notch,
snippet_button,
snippet_buttonhole,
snippet_buttonholestart,
snippet_buttonholeend,
snippet_snapsocket,
snippet_snapstud,
snippet_logo,
// Stacks
stacks_top,
stacks_left,
stacks_right,
stacks_bottom,
stacks_leftEye,
stacks_rightEye,
stacks_mouth,
// Docs
docs_coords,
docs_overview,
Examples,
}

View file

@ -1,745 +0,0 @@
import { box } from './shared.mjs'
export const path__curve = {
name: 'examples.path__curve',
draft: ({ Point, points, Path, paths, part }) => {
points.from = new Point(5, 20)
points.cp2 = new Point(60, 30)
points.to = new Point(90, 20)
paths.line = new Path()
.move(points.from)
._curve(points.cp2, points.to)
.attr('data-text', 'Path._curve()')
.attr('data-text-class', 'text-sm center fill-note')
return box(part, 100, 25)
},
}
export const path_addclass = {
name: 'examples.path_addclass',
draft: ({ Point, points, Path, paths, part }) => {
points.from = new Point(5, 10)
points.to = new Point(95, 10)
paths.line = new Path()
.move(points.from)
.line(points.to)
.addClass('note dashed')
return box(part, 100, 20)
},
}
export const path_attr = {
name: 'examples.path_attr',
draft: ({ Point, points, Path, paths, part }) => {
points.B = new Point(10, 50)
points.BCp2 = new Point(40, 10)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, 90)
paths.example = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.attr('class', 'canvas')
.attr('data-text', 'supportFreesewingBecomeAPatron')
.attr('data-text-class', 'text-xs center')
return part
},
}
export const path_clone = {
name: 'examples.path_clone',
draft: ({ Point, points, Path, paths, part }) => {
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
paths.clone = paths.example
.clone()
.attr('class', 'note lashed stroke-l')
.attr('style', 'stroke-opacity: 0.5')
return part
},
}
export const path_close = {
name: 'examples.path_close',
draft: ({ Point, points, Path, paths, part }) => {
points.from = new Point(10, 20)
points.cp2 = new Point(60, 30)
points.to = new Point(90, 20)
paths.line = new Path()
.move(points.from)
._curve(points.cp2, points.to)
.close()
.reverse() // To keep text from being upside-down
.attr('data-text', 'Path._close()')
.attr('data-text-class', 'text-sm right fill-note')
return box(part, 100, 25)
},
}
export const path_curve = {
name: 'examples.path_curve',
draft: ({ Point, points, Path, paths, part }) => {
points.from = new Point(10, 20)
points.cp1 = new Point(40, 0)
points.cp2 = new Point(60, 30)
points.to = new Point(90, 20)
paths.line = new Path()
.move(points.from)
.curve(points.cp1, points.cp2, points.to)
.attr('data-text', 'Path.curve()')
.attr('data-text-class', 'text-sm center fill-note')
return box(part, 100, 25)
},
}
export const path_curve_ = {
name: 'examples.path_curve_',
draft: ({ Point, points, Path, paths, part }) => {
points.from = new Point(10, 20)
points.cp1 = new Point(40, 0)
points.to = new Point(90, 20)
paths.line = new Path()
.move(points.from)
.curve_(points.cp1, points.to)
.attr('data-text', 'Path.curve_()')
.attr('data-text-class', 'text-sm center fill-note')
return box(part, 100, 25)
},
}
export const path_divide = {
name: 'examples.path_divide',
draft: ({ Point, points, Path, paths, part }) => {
points.A = new Point(55, 40)
points.B = new Point(10, 70)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 60)
points.CCp1 = new Point(50, -30)
points.D = new Point(50, 80)
points.DCp1 = new Point(140, 50)
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.curve(points.DCp1, points.DCp1, points.D)
.close()
let style = 'stroke-width: 4; stroke-opacity: 0.5;'
let i = 0
for (let p of paths.example.divide()) {
i++
paths[i] = p.attr('style', style).attr('style', `stroke: hsl(${i * 70}, 100%, 50%)`)
}
return part
},
}
export const path_edge = {
name: 'examples.path_edge',
draft: ({ Point, points, Path, paths, Snippet, snippets, part }) => {
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
points.D = new Point(-60, 90)
points.E = new Point(90, 190)
paths.demo = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.curve(points.E, points.D, points.A)
.close()
for (let i of [
'topLeft',
'topRight',
'bottomLeft',
'bottomRight',
'top',
'left',
'bottom',
'right',
])
snippets[i] = new Snippet('notch', paths.demo.edge(i))
return part
},
}
export const path_end = {
name: 'examples.path_end',
draft: ({ Point, points, Path, paths, Snippet, snippets, part }) => {
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
paths.demo = new Path().move(points.A).line(points.B).curve(points.BCp2, points.CCp1, points.C)
snippets.end = new Snippet('notch', paths.demo.end())
return part
},
}
export const path_intersects = {
name: 'examples.path_intersects',
draft: ({ Point, points, Path, paths, Snippet, snippets, part }) => {
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
points.D = new Point(50, 130)
points.DCp1 = new Point(150, 30)
points._A = new Point(55, 40)
points._B = new Point(0, 55)
points._BCp2 = new Point(40, -20)
points._C = new Point(90, 40)
points._CCp1 = new Point(50, -30)
points._D = new Point(40, 120)
points._DCp1 = new Point(180, 40)
paths.demo1 = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.curve(points.DCp1, points.DCp1, points.D)
paths.demo2 = new Path()
.move(points._A)
.line(points._B)
.curve(points._BCp2, points._CCp1, points._C)
.curve(points._DCp1, points._DCp1, points._D)
for (let p of paths.demo1.intersects(paths.demo2)) {
snippets[part.getId()] = new Snippet('notch', p)
}
return part
},
}
export const path_intersectsx = {
name: 'examples.path_intersectsx',
draft: ({ Point, points, Path, paths, Snippet, snippets, part }) => {
points.A = new Point(95, 50)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
points.D = new Point(50, 130)
points.DCp1 = new Point(150, 30)
points.top = new Point(60, -10)
points.bot = new Point(60, 140)
paths.line = new Path().move(points.top).line(points.bot).attr('class', 'lining dashed')
paths.demo = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.curve(points.DCp1, points.DCp1, points.D)
for (let p of paths.demo.intersectsX(60)) {
snippets[part.getId()] = new Snippet('notch', p)
}
return part
},
}
export const path_intersectsy = {
name: 'examples.path_intersectsy',
draft: ({ Point, points, Path, paths, Snippet, snippets, part }) => {
points.A = new Point(55, 40)
points.B = new Point(10, 70)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 60)
points.CCp1 = new Point(50, -30)
points.D = new Point(50, 80)
points.DCp1 = new Point(140, 50)
points.top = new Point(10, 58)
points.bot = new Point(130, 58)
paths.line = new Path().move(points.top).line(points.bot).attr('class', 'lining dashed')
paths.demo = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.curve(points.DCp1, points.DCp1, points.D)
for (let p of paths.demo.intersectsY(58)) {
snippets[part.getId()] = new Snippet('notch', p)
}
return part
},
}
export const path_join = {
name: 'examples.path_join',
draft: ({ Point, points, Path, paths, part }) => {
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
paths.path1 = new Path().move(points.A).line(points.B).attr('class', 'various')
paths.path2 = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.attr('class', 'canvas')
paths.joint = paths.path1
.join(paths.path2)
.attr('class', 'note lashed stroke-l')
.attr('style', 'stroke-opacity: 0.5')
return part
},
}
export const path_length = {
name: 'examples.path_length',
draft: ({ Point, points, Path, paths, macro, part }) => {
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
macro('pd', {
path: paths.example,
d: -20,
})
macro('pd', {
path: new Path().move(points.B).line(points.A),
d: 10,
})
macro('pd', {
path: new Path().move(points.B).curve(points.BCp2, points.CCp1, points.C),
d: -10,
})
return part
},
}
export const path_line = {
name: 'examples.path_line',
draft: ({ Point, points, Path, paths, part }) => {
points.from = new Point(10, 10)
points.to = new Point(90, 10)
paths.line = new Path()
.move(points.from)
.line(points.to)
.attr('data-text', 'Path.line()')
.attr('data-text-class', 'text-sm center fill-note')
return box(part, 100, 15)
},
}
export const path_move = {
name: 'examples.path_move',
draft: ({ Point, points, Path, paths, part }) => {
points.to = new Point(50, 10)
.attr('data-text', 'Path.move()')
.attr('data-text-class', 'fill-note center')
paths.noline = new Path().move(points.to)
return box(part, 100, 15)
},
}
export const path_noop = {
name: 'examples.path_noop',
draft: ({ Point, points, Path, paths, part }) => {
points.left = new Point(10, 10)
points.dartLeft = new Point(40, 10)
points.dartTip = new Point(50, 50)
points.dartRight = new Point(60, 10)
points.right = new Point(90, 10)
paths.without = new Path()
.move(points.left)
.line(points.dartLeft)
.noop('dart')
.line(points.right)
paths.withDart = paths.without
.insop('dart', new Path().line(points.dartTip).line(points.dartRight))
.attr('style', 'stroke-width: 2px; stroke-opacity: 0.5; stroke: orange;')
return part
},
}
export const path_offset = {
name: 'examples.path_offset',
draft: ({ Point, points, Path, paths, part }) => {
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.line(points.A)
.close()
paths.offset = paths.example.offset(-10).attr('class', 'interfacing')
paths.lineOffset = new Path().move(points.A).line(points.B).offset(-5).attr('class', 'various')
paths.curveOffset = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.offset(-5)
.attr('class', 'canvas')
return part
},
}
export const path_ops = {
name: 'examples.path_ops',
draft: ({ Point, points, Path, paths, options, part }) => {
const textClasses = (label) =>
options.focus === label ? 'center text-xs fill-note' : 'center text-xs'
points.A = new Point(10, 10)
.attr('data-text', 'Path.move()')
.attr('data-text-class', textClasses('move'))
points.B = new Point(70, 30)
points.BCp2 = new Point(40, 10)
points.C = new Point(90, -50)
points.CCp1 = new Point(125, -30)
points.D = new Point(20, -50)
points.DCp = new Point(40, 0)
points.E = new Point(-20, -20)
points.ECp = new Point(-20, -50)
paths.line = new Path()
.move(points.A)
.line(points.B)
.attr('data-text', 'Path.line()')
.attr('data-text-class', textClasses('line'))
paths.curve = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.attr('data-text', 'Path.curve()')
.attr('data-text-class', textClasses('curve'))
paths._curve = new Path()
.move(points.C)
._curve(points.DCp, points.D)
.attr('data-text', 'Path._curve()')
.attr('data-text-class', textClasses('_curve'))
paths.curve_ = new Path()
.move(points.D)
.curve_(points.ECp, points.E)
.attr('data-text', 'Path.curve_()')
.attr('data-text-class', textClasses('curve_'))
paths.close = new Path()
.move(points.E)
.line(points.A)
.attr('data-text', 'Path.close()')
.attr('data-text-class', textClasses('close'))
paths.example = paths.line.join(paths.curve).join(paths._curve).join(paths.curve_).close()
return part
},
}
export const path_reverse = {
name: 'examples.path_reverse',
draft: ({ Point, points, Path, paths, part }) => {
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
paths.example = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.attr('data-text', 'freesewingIsMadeByJoostDeCockAndContributors')
.attr('data-text-class', 'text-xs fill-note')
paths.reverse = paths.example
.reverse()
.attr('data-text', 'freesewingIsMadeByJoostDeCockAndContributors')
.attr('data-text-class', 'text-xs fill-lining')
return part
},
}
export const path_shiftalong = {
name: 'examples.path_shiftalong',
draft: ({ Point, points, Path, paths, Snippet, snippets, part }) => {
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
points.x1 = paths.example
.shiftAlong(20)
.attr('data-text', '2cm')
.attr('data-text-class', 'center fill-note')
.attr('data-text-lineheight', 6)
points.x2 = paths.example
.shiftAlong(90)
.attr('data-text', '9cm')
.attr('data-text-class', 'center fill-note')
.attr('data-text-lineheight', 6)
snippets.x1 = new Snippet('notch', points.x1)
snippets.x2 = new Snippet('notch', points.x2)
return part
},
}
export const path_shiftfractionalong = {
name: 'examples.path_shiftfractionalong',
draft: ({ Point, points, Path, paths, Snippet, snippets, part }) => {
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
points.x1 = paths.example
.shiftFractionAlong(0.2)
.attr('data-text', '20%')
.attr('data-text-class', 'center fill-note')
.attr('data-text-lineheight', 6)
points.x2 = paths.example
.shiftFractionAlong(0.9)
.attr('data-text', '90%')
.attr('data-text-class', 'center fill-note')
.attr('data-text-lineheight', 6)
snippets.xl = new Snippet('notch', points.x1)
snippets.x2 = new Snippet('notch', points.x2)
return part
},
}
export const path_smurve = {
name: 'examples.path_smurve',
draft: ({ Point, points, Path, paths, part }) => {
points.from = new Point(10, 20)
points.cp1 = new Point(40, 10)
points.cp2 = new Point(60, 30)
points.to = new Point(90, 20)
points.scp2 = new Point(140, 30)
points.sto = new Point(170, 20)
paths.line = new Path()
.move(points.from)
.curve(points.cp1, points.cp2, points.to)
.smurve(points.scp2, points.sto)
.attr('data-text', 'Path.smurve()')
.attr('data-text-class', 'text-sm center fill-note')
return box(part, 180, 40)
},
}
export const path_smurve_ = {
name: 'examples.path_smurve_',
draft: ({ Point, points, Path, paths, part }) => {
points.from = new Point(10, 20)
points.cp1 = new Point(40, 10)
points.cp2 = new Point(60, 30)
points.to = new Point(90, 20)
points.sto = new Point(170, 20)
paths.line = new Path()
.move(points.from)
.curve(points.cp1, points.cp2, points.to)
.smurve_(points.sto)
.attr('data-text', 'Path.smurve_()')
.attr('data-text-class', 'text-sm center fill-note')
return box(part, 180, 40)
},
}
export const path_split = {
name: 'examples.path_split',
draft: ({ Point, points, Path, paths, Snippet, snippets, part }) => {
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
points.D = new Point(50, 130)
points.DCp1 = new Point(150, 30)
paths.demo = new Path()
.move(points.D)
.curve(points.DCp1, points.DCp1, points.C)
.curve(points.CCp1, points.BCp2, points.B)
.line(points.A)
points.split = paths.demo.shiftFractionAlong(0.75)
snippets.x = new Snippet('x', points.split)
let style = 'stroke-width: 3; stroke-opacity: 0.5;'
let halves = paths.demo.split(points.split)
for (let i in halves) {
paths[i] = halves[i].attr('style', style).attr('style', `stroke: hsl(${i * 70}, 100%, 50%)`)
}
return part
},
}
export const path_start = {
name: 'examples.path_start',
draft: ({ Point, points, Path, paths, Snippet, snippets, part }) => {
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
snippets.start = new Snippet('notch', paths.example.start())
return part
},
}
export const path_translate = {
name: 'examples.path_translate',
draft: ({ Point, points, Path, paths, macro, part }) => {
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
paths.A = new Path().move(points.A).line(points.B).curve(points.BCp2, points.CCp1, points.C)
paths.B = paths.A.translate(60, 30)
points.step1 = points.B.shift(0, 60)
points.step2 = points.step1.shift(-90, 30)
macro('ld', {
from: points.B,
to: points.step1,
noStartMarker: true,
})
macro('ld', {
from: points.step1,
to: points.step2,
noStartMarker: true,
})
return part
},
}
export const path_trim = {
name: 'examples.path_trim',
draft: ({ Point, points, Path, paths, part }) => {
points.center = new Point(0, 0)
points.base = new Point(0, 10)
points.tip = new Point(0, 50)
points.tipCpRight = new Point(30, 50)
points.tipCpLeft = new Point(-30, 50)
paths.example = new Path().move(points.base)
for (let i = 0; i < 4; i++) {
points['base' + i] = points.base.rotate(60 * i, points.center)
points['tip' + i] = points.tip.rotate(60 * i, points.center)
points['tipCpRight' + i] = points.tipCpRight.rotate(60 * i, points.center)
points['tipCpLeft' + i] = points.tipCpLeft.rotate(60 * i, points.center)
if (i < 2) {
paths.example
.line(points['base' + i])
.curve(points['base' + i], points['tipCpLeft' + i], points['tip' + i])
.curve(points['tipCpRight' + i], points['base' + i], points['base' + i])
} else {
paths.example
.line(points['base' + i])
.line(points['tip' + i])
.line(points['tipCpRight' + i])
.line(points['base' + i])
}
}
paths.offset = paths.example.offset(10).attr('class', 'lining dotted stroke-sm')
paths.trimmed = paths.offset
.trim()
.attr('class', 'various stroke-xl')
.attr('style', 'stroke-opacity: 0.5;')
return part
},
}

View file

@ -1,342 +0,0 @@
import { box } from './shared.mjs'
export const plugin_banner = {
name: 'examples.plugin_banner',
draft: ({ points, Point, paths, Path, macro, part }) => {
points.from = new Point(0, 0)
points.to = new Point(320, 0)
paths.banner = new Path().move(points.from).line(points.to)
macro('banner', {
path: paths.banner,
text: 'banner plugin',
})
// Prevent clipping of text
paths.box = new Path().move(new Point(0, -20)).line(new Point(0, 20)).attr('class', 'hidden')
return part
},
}
export const plugin_bartack = {
name: 'examples.plugin_bartack',
draft: ({ Point, points, macro, part }) => {
points.a = new Point(15, 15)
macro('bartack', {
anchor: points.a,
angle: 30,
length: 15,
})
return box(part, 60, 30)
},
}
export const plugin_bartackalong = {
name: 'examples.plugin_bartackalong',
draft: ({ Point, Path, points, paths, macro, part }) => {
points.a = new Point(15, 15)
points.b = new Point(20, 20)
points.c = new Point(30, 20)
points.d = new Point(35, 15)
points.e = new Point(20, 10)
points.f = new Point(30, 10)
paths.a = new Path().move(points.a).curve(points.b, points.c, points.d).hide()
macro('bartackAlong', {
path: paths.a,
})
macro('sprinkle', {
snippet: 'notch',
on: ['e', 'f'],
})
return box(part, 60, 30)
},
}
export const plugin_bartackfractionalong = {
name: 'examples.plugin_bartackfractionalong',
draft: ({ Point, Path, points, paths, macro, part }) => {
points.a = new Point(15, 15)
points.b = new Point(20, 20)
points.c = new Point(30, 20)
points.d = new Point(35, 15)
points.e = new Point(20, 10)
points.f = new Point(30, 10)
paths.a = new Path().move(points.a).curve(points.b, points.c, points.d).hide()
macro('bartackFractionAlong', {
path: paths.a,
start: 0.2,
end: 0.8,
})
macro('sprinkle', {
snippet: 'notch',
on: ['e', 'f'],
})
return box(part, 60, 30)
},
}
export const plugin_buttons = {
name: 'examples.plugin_buttons',
draft: ({ Point, snippets, Snippet, part }) => {
snippets.button = new Snippet('button', new Point(20, 10))
snippets.buttonhole = new Snippet('buttonhole', new Point(40, 10))
snippets.buttonholeStart = new Snippet('buttonhole-start', new Point(60, 10))
snippets.buttonholeEnd = new Snippet('buttonhole-end', new Point(80, 10))
snippets.snapMale = new Snippet('snap-stud', new Point(100, 10))
snippets.snapFemale = new Snippet('snap-socket', new Point(120, 10))
return box(part, 140, 20)
},
}
export const plugin_cutonfold = {
name: 'examples.plugin_cutonfold',
draft: ({ Point, points, Path, paths, macro, part }) => {
points.topLeft = new Point(0, 0)
points.topRight = new Point(150, 0)
points.bottomRight = new Point(150, 30)
points.bottomLeft = new Point(0, 30)
paths.box = new Path()
.move(points.topLeft)
.line(points.topRight)
.line(points.bottomRight)
.line(points.bottomLeft)
.close()
macro('cutonfold', {
from: points.bottomLeft,
to: points.bottomRight,
grainline: true,
})
return part
},
}
export const plugin_dimension = {
name: 'examples.plugin_dimension',
draft: ({ Point, points, Path, paths, macro, part }) => {
points.A = new Point(0, 0)
points.B = new Point(0, 100)
points.C = new Point(50, 100)
points.D = new Point(100, 50)
points.DCp1 = new Point(100, 0)
paths.box = new Path()
.move(points.A)
.line(points.B)
.line(points.C)
.line(points.D)
.curve(points.DCp1, points.A, points.A)
.close()
macro('vd', {
from: points.A,
to: points.B,
x: points.A.x - 15,
})
macro('hd', {
from: points.B,
to: points.C,
y: points.B.y + 15,
})
macro('ld', {
from: points.C,
to: points.D,
d: -15,
})
macro('ld', {
from: points.C,
to: points.D,
d: -30,
text: 'Custom text',
})
macro('pd', {
path: new Path().move(points.A).curve(points.A, points.DCp1, points.D),
d: -15,
})
return part
},
}
export const plugin_gore = {
name: 'examples.plugin_gore',
draft: ({ Point, points, macro, part }) => {
points.anchor = new Point(0, 0)
macro('gore', {
from: points.anchor,
radius: 60,
gores: 5,
extraLength: 20,
})
return part
},
}
export const plugin_grainline = {
name: 'examples.plugin_grainline',
draft: ({ Point, points, macro, part }) => {
points.grainlineFrom = new Point(10, 10)
points.grainlineTo = new Point(100, 10)
macro('grainline', {
from: points.grainlineFrom,
to: points.grainlineTo,
})
return box(part, 110, 15)
},
}
export const plugin_logo = {
name: 'examples.plugin_logo',
draft: ({ points, Point, snippets, Snippet, part }) => {
points.anchor = new Point(50, 25)
snippets.logo = new Snippet('logo', points.anchor).attr('data-scale', 0.666)
return box(part, 100, 35)
},
}
export const plugin_mirror = {
name: 'examples.plugin_mirror',
draft: ({ Point, Path, points, paths, macro, part }) => {
points.a = new Point(5, 5)
points.b = new Point(45, 30)
points.c = new Point(5, 30)
points.d = new Point(45, 5)
points.mid = new Point(25, 15)
paths.a = new Path().move(points.a).curve(points.b, points.c, points.d)
macro('mirror', {
mirror: [points.b, points.d],
points: [points.mid],
paths: [paths.a],
})
macro('sprinkle', {
snippet: 'notch',
on: ['mid', 'mirroredMid'],
})
return box(part, 100, 40)
},
}
export const plugin_notches = {
name: 'examples.plugin_notches',
draft: ({ Point, snippets, Snippet, part }) => {
snippets.notch = new Snippet('notch', new Point(60, 10))
snippets.bnotch = new Snippet('bnotch', new Point(80, 10))
return box(part, 140, 20)
},
}
export const plugin_round = {
name: 'examples.plugin_round',
draft: ({ Point, points, Path, paths, macro, part }) => {
points.topLeft = new Point(0, 0)
points.bottomLeft = new Point(0, 30)
points.topRight = new Point(100, 0)
points.bottomRight = new Point(100, 30)
paths.demo = new Path()
.move(points.topLeft)
.line(points.bottomLeft)
.line(points.bottomRight)
.line(points.topRight)
.close()
.attr('class', 'note dashed')
macro('round', {
from: points.topLeft,
to: points.bottomRight,
via: points.bottomLeft,
radius: 10,
prefix: 'bl',
})
macro('round', {
from: points.bottomRight,
to: points.topLeft,
via: points.topRight,
radius: 20,
prefix: 'tr',
})
return part
},
}
export const plugin_scalebox = {
name: 'examples.plugin_scalebox',
draft: ({ Point, points, macro, part }) => {
points.anchor1 = new Point(0, 0)
points.anchor2 = new Point(70, 0)
macro('scalebox', { at: points.anchor1 })
macro('miniscale', { at: points.anchor2 })
return part
},
}
export const plugin_sprinkle = {
name: 'examples.plugin_sprinkle',
draft: ({ Point, points, macro, part }) => {
points.a = new Point(10, 10)
points.b = new Point(20, 15)
points.c = new Point(30, 10)
points.d = new Point(40, 15)
points.e = new Point(50, 10)
points.f = new Point(60, 15)
points.g = new Point(70, 10)
points.h = new Point(80, 15)
points.i = new Point(90, 10)
macro('sprinkle', {
snippet: 'button',
on: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'],
})
return box(part, 100, 25)
},
}
export const plugin_title = {
name: 'examples.plugin_title',
draft: ({ Point, points, macro, part }) => {
points.title = new Point(90, 45)
macro('title', {
at: points.title,
nr: 4,
title: 'sleeve',
prefix: 'test',
})
return box(part, 200, 70)
},
}

View file

@ -1,457 +0,0 @@
import { box } from './shared.mjs'
export const point_addcircle = {
name: 'examples.point_addcircle',
draft: ({ Point, points, part }) => {
points.a = new Point(30, 10).addCircle(3, 'lining dashed').addCircle(7, 'mark dashed')
points.b = new Point(50, 10)
.addCircle(1, 'interfacing')
.addCircle(3, 'fabric')
.addCircle(5, 'lining')
.addCircle(7, 'mark')
.addCircle(9, 'note')
points.c = new Point(70, 10).addCircle(3, 'interfacing').addCircle(7, 'mark lashed')
return box(part, 100, 20)
},
}
export const point_addtext = {
name: 'examples.point_addtext',
draft: ({ Point, points, part }) => {
points.anchor = new Point(100, 25)
.addText('supportFreesewingBecomeAPatron', 'center')
.addText('please?')
return box(part, 200, 50)
},
}
export const point_angle = {
name: 'examples.point_angle',
draft: ({ Point, points, Path, paths, part }) => {
points.sun = new Point(10, 5)
points.moon = points.sun.shift(-15, 70)
points.text = points.sun
.shiftFractionTowards(points.moon, 0.8)
.attr('data-text', points.sun.angle(points.moon) + '°')
.attr('data-text-class', 'text-sm fill-note center')
paths.line = new Path().move(points.sun).line(points.moon).attr('class', 'dashed')
return part
},
}
export const point_attr = {
name: 'examples.point_attr',
draft: ({ Point, points, part }) => {
points.anchor = new Point(100, 25)
.attr('data-text', 'supportFreesewingBecomeAPatron')
.attr('data-text-class', 'center')
return box(part, 200, 50)
},
}
export const point_clone = {
name: 'examples.point_clone',
draft: ({ Point, points, Snippet, snippets, part }) => {
points.A = new Point(25, 25)
.attr('data-text', 'Point A')
.attr('data-text-class', 'text-xl')
.attr('data-text-fill-opacity', '0.5')
points.B = points.A.clone().attr('data-text', 'Point B')
snippets.x = new Snippet('notch', points.A)
return box(part)
},
}
export const point_copy = {
name: 'examples.point_copy',
draft: ({ Point, points, Snippet, snippets, part }) => {
points.A = new Point(50, 25).attr('data-text', 'Point A').attr('data-text-class', 'text-xl')
points.B = points.A.copy().attr('data-text', 'Point B')
snippets.x = new Snippet('notch', points.A)
return box(part)
},
}
export const point_dist = {
name: 'examples.point_dist',
draft: ({ Point, points, Path, paths, part }) => {
points.from = new Point(10, 10)
points.to = new Point(80, 70)
points.text = points.from
.shiftFractionTowards(points.to, 0.6)
.attr('data-text', points.from.dist(points.to) + 'mm')
.attr('data-text-class', 'text-sm fill-note center')
paths.line = new Path().move(points.from).line(points.to).attr('class', 'dashed')
return part
},
}
export const point_dx = {
name: 'examples.point_dx',
draft: ({ Point, points, Path, paths, part }) => {
points.from = new Point(10, 10)
points.to = new Point(80, 70)
paths.line = new Path().move(points.from).line(points.to).attr('class', 'dashed')
points.totop = points.from.shift(0, points.from.dx(points.to))
points.text_dx = points.from
.shiftFractionTowards(points.totop, 0.6)
.shiftFractionTowards(points.to, 0.1)
.attr('data-text', points.from.dx(points.to) + 'mm')
.attr('data-text-class', 'text-sm fill-note center')
paths.line_dx = new Path().move(points.from).line(points.totop).attr('class', 'dashed')
paths.line_dy = new Path().move(points.to).line(points.totop).attr('class', 'dashed')
return part
},
}
export const point_dy = {
name: 'examples.point_dy',
draft: ({ Point, points, Path, paths, part }) => {
points.from = new Point(10, 10)
points.to = new Point(80, 70)
paths.line = new Path().move(points.from).line(points.to).attr('class', 'dashed')
points.totop = points.from.shift(0, points.from.dx(points.to))
paths.line_dx = new Path().move(points.from).line(points.totop).attr('class', 'dashed')
points.text_dy = points.totop
.shiftFractionTowards(points.to, 0.4)
.attr('data-text', points.from.dy(points.to) + 'mm')
.attr('data-text-class', 'text-sm fill-note right')
paths.line_dy = new Path().move(points.to).line(points.totop).attr('class', 'dashed')
return part
},
}
export const point_flipx = {
name: 'examples.point_flipy',
draft: ({ Point, points, Path, paths, part }) => {
points.top = new Point(50, 10)
points.out1 = new Point(70, 30)
points.in1 = new Point(55, 35)
points.out2 = new Point(75, 50)
points.in2 = new Point(60, 55)
points.out3 = new Point(80, 70)
points.in3 = new Point(55, 70)
points.trunkOut = new Point(55, 80)
points.trunkIn = new Point(50, 80)
points._out1 = points.out1.flipX(points.top)
points._in1 = points.in1.flipX(points.top)
points._out2 = points.out2.flipX(points.top)
points._in2 = points.in2.flipX(points.top)
points._out3 = points.out3.flipX(points.top)
points._in3 = points.in3.flipX(points.top)
points._trunkOut = points.trunkOut.flipX(points.top)
points.bottom = new Point(50, 80)
paths.tree = new Path()
.move(points.top)
.line(points.out1)
.line(points.in1)
.line(points.out2)
.line(points.in2)
.line(points.out3)
.line(points.in3)
.line(points.trunkOut)
.line(points._trunkOut)
.line(points._in3)
.line(points._out3)
.line(points._in2)
.line(points._out2)
.line(points._in1)
.line(points._out1)
.close()
paths.mirror = new Path().move(points.top).line(points.bottom).attr('class', 'note dashed')
return part
},
}
export const point_flipy = {
name: 'examples.point_flipy',
draft: ({ Point, points, Path, paths, part }) => {
points.start = new Point(0, 50)
points.churchTowerWallLeft = new Point(10, 50)
points.churchTowerRoofLeft = new Point(10, 30)
points.churchTowerTop = new Point(15, 10)
points.churchTowerRoofRight = new Point(20, 30)
points.churchRoofRight = new Point(50, 30)
points.churchWallRight = new Point(50, 50)
points.houseWallLeft = new Point(65, 50)
points.houseRoofLeft = new Point(65, 35)
points.houseRoofTop = new Point(75, 25)
points.houseRoofRight = new Point(85, 35)
points.houseWallRight = new Point(85, 50)
points.end = new Point(95, 50)
points.mirror = new Point(0, 60)
points.mirrorLineEnd = new Point(95, 60)
points._start = points.start.flipY(points.mirror)
points._churchTowerWallLeft = points.churchTowerWallLeft.flipY(points.mirror)
points._churchTowerRoofLeft = points.churchTowerRoofLeft.flipY(points.mirror)
points._churchTowerTop = points.churchTowerTop.flipY(points.mirror)
points._churchTowerRoofRight = points.churchTowerRoofRight.flipY(points.mirror)
points._churchRoofRight = points.churchRoofRight.flipY(points.mirror)
points._churchWallRight = points.churchWallRight.flipY(points.mirror)
points._houseWallLeft = points.houseWallLeft.flipY(points.mirror)
points._houseRoofLeft = points.houseRoofLeft.flipY(points.mirror)
points._houseRoofTop = points.houseRoofTop.flipY(points.mirror)
points._houseRoofRight = points.houseRoofRight.flipY(points.mirror)
points._houseWallRight = points.houseWallRight.flipY(points.mirror)
points._end = points.end.flipY(points.mirror)
paths.skylineTop = new Path()
.move(points.start)
.line(points.churchTowerWallLeft)
.line(points.churchTowerRoofLeft)
.line(points.churchTowerTop)
.line(points.churchTowerRoofRight)
.line(points.churchRoofRight)
.line(points.churchWallRight)
.line(points.houseWallLeft)
.line(points.houseRoofLeft)
.line(points.houseRoofTop)
.line(points.houseRoofRight)
.line(points.houseWallRight)
.line(points.end)
paths.skylineBottom = new Path()
.move(points._start)
.line(points._churchTowerWallLeft)
.line(points._churchTowerRoofLeft)
.line(points._churchTowerTop)
.line(points._churchTowerRoofRight)
.line(points._churchRoofRight)
.line(points._churchWallRight)
.line(points._houseWallLeft)
.line(points._houseRoofLeft)
.line(points._houseRoofTop)
.line(points._houseRoofRight)
.line(points._houseWallRight)
.line(points._end)
paths.mirrorLine = new Path()
.move(points.mirror)
.line(points.mirrorLineEnd)
.attr('class', 'note dashed')
return part
},
}
export const point_rotate = {
name: 'examples.point_rotate',
draft: ({ Point, points, Path, paths, part }) => {
points.sun = new Point(40, 40)
points.moon = new Point(70, 40)
let step = 360 / 36
for (let i = 1; i < 37; i++) {
let angle = step * i
points[`moon${i}`] = points.moon.rotate(angle, points.sun)
paths[`moon${i}`] = new Path().move(points.sun).line(points[`moon${i}`])
}
return part
},
}
export const point_setcircle = {
name: 'examples.point_setcircle',
draft: ({ Point, points, part }) => {
points.a = new Point(30, 10).setCircle(3, 'lining dashed').setCircle(7, 'mark dashed')
points.b = new Point(50, 10)
.setCircle(1, 'interfacing')
.setCircle(3, 'fabric')
.setCircle(5, 'lining')
.setCircle(7, 'mark')
.setCircle(9, 'note')
points.c = new Point(70, 10).setCircle(3, 'interfacing').setCircle(7, 'mark lashed')
return box(part, 100, 20)
},
}
export const point_settext = {
name: 'examples.point_settext',
draft: ({ Point, points, part }) => {
points.anchor = new Point(100, 25)
.setText('supportFreesewingBecomeAPatron', 'center')
.setText('please?')
return box(part, 200, 50)
},
}
export const point_shift = {
name: 'examples.point_shift',
draft: ({ Point, points, macro, part }) => {
points.A = new Point(90, 40).attr('data-text', 'Point A').attr('data-text-class', 'right')
points.B = points.A.shift(155, 70)
.attr('data-text', 'Point B is point A shifted 7cm\nat a 155 degree angle')
.attr('data-text-lineheight', 6)
macro('ld', {
from: points.B,
to: points.A,
d: -10,
})
return box(part, 100, 45)
},
}
export const point_shiftfractiontowards = {
name: 'examples.point_shiftfractiontowards',
draft: ({ Point, points, Path, paths, macro, part }) => {
points.A = new Point(90, 70).attr('data-text', 'Point A')
points.B = new Point(10, 10).attr('data-text', 'Point B')
points.C = points.A.shiftFractionTowards(points.B, 0.5)
.attr('data-text', 'Point C is point A shifted 50%\nin the direction of point B')
.attr('data-text-class', 'center')
.attr('data-text-lineheight', 6)
paths.direction = new Path().move(points.A).line(points.B).attr('class', 'note dashed')
macro('ld', {
from: points.C,
to: points.A,
d: -10,
})
macro('ld', {
from: points.B,
to: points.A,
d: 20,
})
return part
},
}
export const point_shiftoutwards = {
name: 'examples.point_shiftoutwards',
draft: ({ Point, points, Path, paths, macro, part }) => {
points.A = new Point(90, 70).attr('data-text', 'Point A')
points.B = new Point(30, 30).attr('data-text', 'Point B')
points.C = points.A.shiftOutwards(points.B, 30)
.attr('data-text', 'Point C is point A shifted 3cm\nbeyond point B')
.attr('data-text-lineheight', 6)
paths.direction = new Path().move(points.A).line(points.C).attr('class', 'note dashed')
macro('ld', {
from: points.C,
to: points.B,
d: -10,
})
return box(part, 110, 75)
},
}
export const point_shifttowards = {
name: 'examples.point_shifttowards',
draft: ({ Point, points, Path, paths, macro, part }) => {
points.A = new Point(90, 70).attr('data-text', 'Point A')
points.B = new Point(10, 10).attr('data-text', 'Point B')
points.C = points.A.shiftTowards(points.B, 35)
.attr('data-text', 'Point C is point A shifted 3.5cm\nin the direction of point B')
.attr('data-text-class', 'center')
.attr('data-text-lineheight', 6)
paths.direction = new Path().move(points.A).line(points.B).attr('class', 'note dashed')
macro('ld', {
from: points.C,
to: points.A,
d: -10,
})
return box(part, 110, 80)
},
}
export const point_sitson = {
name: 'examples.point_sitson',
draft: ({ Point, points, Snippet, snippets, part }) => {
let s
for (let i = 0; i < 10; i++) {
points[`a${i}`] = new Point(i * 10, 40)
points[`b${i}`] = new Point(i * 10, i * 8)
if (points[`a${i}`].sitsOn(points[`b${i}`])) s = 'notch'
else s = 'bnotch'
snippets[`b${i}`] = new Snippet(s, points[`b${i}`])
snippets[`a${i}`] = new Snippet(s, points[`a${i}`])
}
return box(part)
},
}
export const point_sitsroughlyon = {
name: 'examples.point_sitsroughlyon',
draft: ({ Point, points, Snippet, snippets, part }) => {
box(part)
let s
for (let i = 0; i < 10; i++) {
points[`a${i}`] = new Point(i * 10, 40)
points[`b${i}`] = new Point(i * 10, i * 8)
if (points[`a${i}`].sitsRoughlyOn(points[`b${i}`])) s = 'notch'
else s = 'bnotch'
snippets[`b${i}`] = new Snippet(s, points[`b${i}`])
snippets[`a${i}`] = new Snippet(s, points[`a${i}`])
}
return part
},
}
export const point_translate = {
name: 'examples.point_translate',
draft: ({ Point, points, macro, part }) => {
points.A = new Point(20, 20).attr('data-text', 'Point A')
points.B = points.A.translate(120, 60)
.attr('data-text', 'Point B is point A with a\ntranslate(120, 60)\ntransform applied')
.attr('data-text-class', 'right')
.attr('data-text-dy', -6)
.attr('data-text-lineheight', 6)
macro('ld', {
from: points.A,
to: points.B,
text: 'translate(120,60)',
noStartMarker: true,
})
return box(part, 150, 85)
},
}

View file

@ -1,22 +0,0 @@
export const settings_sa = {
name: 'examples.settings_sa',
draft: ({ Point, points, Path, paths, part }) => {
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.line(points.A)
.close()
.attr('class', 'fabric')
paths.offset = paths.example.offset(-10).attr('class', 'fabric sa')
return part
},
}

View file

@ -1,13 +0,0 @@
/**
* This draws a (diagonal in a) box
* with with w and height h with a non-visible line.
* This is to force our examples parts to a certain size
*/
export function box(part, w = 100, h = 50) {
part.paths.box = new part.Path()
.move(new part.Point(0, 0))
.line(new part.Point(w, h))
.setClass('hidden noxray')
return part
}

View file

@ -1,39 +0,0 @@
import { box } from './shared.mjs'
export const snippet = {
name: 'examples.snippet',
draft: ({ Point, points, Snippet, snippets, part }) => {
points.anchor1 = new Point(20, 15)
points.anchor2 = new Point(50, 15)
points.anchor3 = new Point(80, 15)
snippets.demo1 = new Snippet('button', points.anchor1)
snippets.demo2 = new Snippet('buttonhole', points.anchor2)
snippets.demo3 = new Snippet('logo', points.anchor3).attr('data-scale', 0.5)
return box(part)
},
}
export const snippet_attr = {
name: 'examples.snippet_attr',
draft: ({ Point, points, Snippet, snippets, part }) => {
points.anchor = new Point(50, 15)
snippets.demo = new Snippet('logo', points.anchor)
.attr('data-scale', 0.8)
.attr('data-rotate', 180)
return box(part)
},
}
export const snippet_clone = {
name: 'examples.snippet_clone',
draft: ({ Point, points, Snippet, snippets, part }) => {
points.anchor = new Point(35, 35)
snippets.demo = new Snippet('logo', points.anchor).attr('style', 'color: #f006')
snippets.clone = snippets.demo.clone().attr('data-scale', 0.5)
return box(part)
},
}

View file

@ -1,91 +0,0 @@
import { box } from './shared.mjs'
export const snippet_bnotch = {
name: 'examples.snippet_bnotch',
draft: ({ Point, points, Snippet, snippets, part }) => {
points.anchor = new Point(50, 5)
snippets.demo = new Snippet('bnotch', points.anchor)
return box(part, 100, 10)
},
}
export const snippet_button = {
name: 'examples.snippet_button',
draft: ({ Point, points, Snippet, snippets, part }) => {
points.anchor = new Point(50, 5)
snippets.demo = new Snippet('button', points.anchor)
return box(part, 100, 10)
},
}
export const snippet_buttonholeend = {
name: 'examples.snippet_buttonholeend',
draft: ({ Point, points, Snippet, snippets, part }) => {
points.anchor = new Point(50, 0)
snippets.demo = new Snippet('buttonhole-end', points.anchor)
return box(part, 100, 10)
},
}
export const snippet_buttonholestart = {
name: 'examples.snippet_buttonholestart',
draft: ({ Point, points, Snippet, snippets, part }) => {
points.anchor = new Point(50, 10)
snippets.demo = new Snippet('buttonhole-start', points.anchor)
return box(part, 100, 10)
},
}
export const snippet_buttonhole = {
name: 'examples.snippet_buttonhole',
draft: ({ Point, points, Snippet, snippets, part }) => {
points.anchor = new Point(50, 5)
snippets.demo = new Snippet('buttonhole', points.anchor)
return box(part, 100, 10)
},
}
export const snippet_logo = {
name: 'examples.snippet_logo',
draft: ({ Point, points, Snippet, snippets, part }) => {
points.anchor = new Point(50, 35)
snippets.demo = new Snippet('logo', points.anchor)
return box(part, 100, 50)
},
}
export const snippet_notch = {
name: 'examples.snippet_notch',
draft: ({ Point, points, Snippet, snippets, part }) => {
points.anchor = new Point(50, 5)
snippets.demo = new Snippet('notch', points.anchor)
return box(part, 100, 10)
},
}
export const snippet_snapsocket = {
name: 'examples.snippet_snapsocket',
draft: ({ Point, points, Snippet, snippets, part }) => {
points.anchor = new Point(50, 5)
snippets.demo = new Snippet('snap-socket', points.anchor)
return box(part, 100, 10)
},
}
export const snippet_snapstud = {
name: 'examples.snippet_snapstud',
draft: ({ Point, points, Snippet, snippets, part }) => {
points.anchor = new Point(50, 5)
snippets.demo = new Snippet('snap-stud', points.anchor)
return box(part, 100, 10)
},
}

View file

@ -1,128 +0,0 @@
const stack = (settings, partName) => {
if (settings?.options?.stackIt === 'Do stack') return 'example'
else return partName
}
export const stacks_top = {
name: 'examples.stacks_top',
stack,
measurements: ['head'],
options: {
size: { pct: 50, min: 5, max: 100, menu: 'stack' },
x: { pct: 0, min: -100, max: 100, menu: 'stack' },
y: { pct: 0, min: -100, max: 100, menu: 'stack' },
stackIt: { dflt: 'Do stack', list: ['Do stack', 'Do not stack'], menu: 'stack' },
},
draft: ({ store, Point, points, Path, paths, options, measurements, part }) => {
store.set('size', measurements.head * options.size)
store.set('x', measurements.head * options.x)
store.set('y', measurements.head * options.y)
points.from = new Point(store.get('x'), store.get('y'))
points.to = points.from.shift(0, store.get('size'))
paths.line = new Path().move(points.from).line(points.to).attr('class', 'fabric stroke-4xl')
return part
},
}
export const stacks_right = {
name: 'examples.stacks_right',
stack,
after: stacks_top,
draft: ({ store, Point, points, Path, paths, part }) => {
points.from = new Point(store.get('x') + store.get('size'), store.get('y'))
points.to = points.from.shift(-90, store.get('size'))
paths.line = new Path().move(points.from).line(points.to).attr('class', 'fabric stroke-4xl')
return part
},
}
export const stacks_bottom = {
name: 'examples.stacks_bottom',
stack,
after: stacks_top,
draft: ({ store, Point, points, Path, paths, part }) => {
points.from = new Point(store.get('x') + store.get('size'), store.get('y') + store.get('size'))
points.to = points.from.shift(180, store.get('size'))
paths.line = new Path().move(points.from).line(points.to).attr('class', 'fabric stroke-4xl')
return part
},
}
export const stacks_left = {
name: 'examples.stacks_left',
stack,
after: stacks_top,
draft: ({ store, Point, points, Path, paths, part }) => {
points.from = new Point(store.get('x'), store.get('y') + store.get('size'))
points.to = points.from.shift(90, store.get('size'))
paths.line = new Path().move(points.from).line(points.to).attr('class', 'fabric stroke-4xl')
return part
},
}
export const stacks_leftEye = {
name: 'examples.stacks_leftEye',
stack,
after: stacks_top,
draft: ({ store, Point, points, part }) => {
points.leftEye = new Point(
store.get('x') + store.get('size') * 0.35,
store.get('y') + store.get('size') * 0.4
)
.attr('data-circle', store.get('size') * 0.1)
.attr('data-circle-class', 'stroke-6xl')
return part
},
}
export const stacks_rightEye = {
name: 'examples.stacks_rightEye',
stack,
after: stacks_top,
draft: ({ store, Point, points, part }) => {
points.rightEye = new Point(
store.get('x') + store.get('size') * 0.65,
store.get('y') + store.get('size') * 0.4
)
.attr('data-circle', store.get('size') * 0.08)
.attr('data-circle-class', 'stroke-7xl')
return part
},
}
export const stacks_mouth = {
name: 'examples.stacks_mouth',
stack,
after: stacks_top,
draft: ({ store, Point, points, paths, Path, part }) => {
points.left = new Point(
store.get('x') + store.get('size') * 0.15,
store.get('y') + store.get('size') * 0.5
)
points.right = new Point(
store.get('x') + store.get('size') * 0.85,
store.get('y') + store.get('size') * 0.5
)
points.leftCp = new Point(
store.get('x') + store.get('size') * 0.35,
store.get('y') + store.get('size') * 0.8
)
points.rightCp = new Point(
store.get('x') + store.get('size') * 0.65,
store.get('y') + store.get('size') * 0.8
)
paths.mouth = new Path()
.move(points.left)
.curve(points.leftCp, points.rightCp, points.right)
.attr('class', 'fabric stroke-7xl')
return part
},
}

View file

@ -1,445 +0,0 @@
import { box } from './shared.mjs'
export const utils_beamintersectscircle = {
name: 'examples.utils_beamintersectscircle',
draft: ({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.A = new Point(95, 45).attr('data-circle', 35).attr('data-circle-class', 'fabric')
points.B = new Point(55, 50)
points.C = new Point(75, 30)
points.D = new Point(55, 65)
points.E = new Point(115, 5)
points.F = new Point(65, 75)
points.G = new Point(125, 15)
paths.line1 = new Path().move(points.B).line(points.C)
paths.line2 = new Path().move(points.D).line(points.E)
paths.line3 = new Path().move(points.F).line(points.G)
let intersections1 = utils.beamIntersectsCircle(
points.A,
points.A.attributes.get('data-circle'),
points.B,
points.C
)
let intersections2 = utils.beamIntersectsCircle(
points.A,
points.A.attributes.get('data-circle'),
points.D,
points.E,
'y'
)
let intersections3 = utils.beamIntersectsCircle(
points.A,
points.A.attributes.get('data-circle'),
points.F,
points.G
)
snippets.first1 = new Snippet('bnotch', intersections1[0])
snippets.second1 = new Snippet('notch', intersections1[1])
snippets.first2 = new Snippet('bnotch', intersections2[0])
snippets.second2 = new Snippet('notch', intersections2[1])
snippets.first3 = new Snippet('bnotch', intersections3[0])
snippets.second3 = new Snippet('notch', intersections3[1])
return box(part, 200, 80)
},
}
export const utils_beamintersectsx = {
name: 'examples.utils_beamintersectsx',
draft: ({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.A = new Point(10, 10)
points.B = new Point(90, 30)
paths.AB = new Path().move(points.A).line(points.B)
snippets.x = new Snippet('notch', utils.beamIntersectsX(points.A, points.B, 40))
paths.help = new Path()
.move(new Point(40, 5))
.line(new Point(40, 35))
.attr('class', 'note dashed')
return part
},
}
export const utils_beamintersectsy = {
name: 'examples.utils_beamintersectsy',
draft: ({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.A = new Point(10, 10)
points.B = new Point(50, 40)
paths.AB = new Path().move(points.A).line(points.B)
snippets.x = new Snippet('notch', utils.beamIntersectsY(points.A, points.B, 30))
paths.help = new Path()
.move(new Point(0, 30))
.line(new Point(50, 30))
.attr('class', 'note dashed')
return part
},
}
export const utils_beamsintersect = {
name: 'examples.utils_beamsintersect',
draft: ({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.A = new Point(10, 10)
points.B = new Point(50, 40)
points.C = new Point(45, 20)
points.D = new Point(60, 15)
paths.AB = new Path().move(points.A).line(points.B)
paths.CD = new Path().move(points.C).line(points.D)
snippets.x = new Snippet('notch', utils.beamsIntersect(points.A, points.B, points.C, points.D))
return part
},
}
export const utils_circlesintersect = {
name: 'examples.utils_circlesintersect',
draft: ({ Point, points, Snippet, snippets, utils, part }) => {
points.A = new Point(10, 10).attr('data-circle', 15).attr('data-circle-class', 'fabric')
points.B = new Point(30, 30).attr('data-circle', 35).attr('data-circle-class', 'fabric')
points.C = new Point(90, 10).attr('data-circle', 15).attr('data-circle-class', 'various')
points.D = new Point(110, 30).attr('data-circle', 35).attr('data-circle-class', 'various')
let intersections1 = utils.circlesIntersect(
points.A,
points.A.attributes.get('data-circle'),
points.B,
points.B.attributes.get('data-circle')
)
let intersections2 = utils.circlesIntersect(
points.C,
points.C.attributes.get('data-circle'),
points.D,
points.D.attributes.get('data-circle'),
'y'
)
snippets.first1 = new Snippet('bnotch', intersections1[0])
snippets.second1 = new Snippet('notch', intersections1[1])
snippets.first2 = new Snippet('bnotch', intersections2[0])
snippets.second2 = new Snippet('notch', intersections2[1])
return part
},
}
export const utils_curveintersectsx = {
name: 'examples.utils_curveintersectsx',
draft: ({ Point, points, Path, paths, utils, snippets, Snippet, part }) => {
points.start = new Point(10, 15)
points.cp1 = new Point(80, 10)
points.cp2 = new Point(-50, 80)
points.end = new Point(110, 70)
paths.curve = new Path().move(points.start).curve(points.cp1, points.cp2, points.end)
for (let x of [30, 40]) {
points['from' + x] = new Point(x, 10)
points['to' + x] = new Point(x, 80)
paths['line' + x] = new Path()
.move(points['from' + x])
.line(points['to' + x])
.attr('class', 'lining dashed')
}
snippets.i40 = new Snippet(
'notch',
utils.curveIntersectsX(points.start, points.cp1, points.cp2, points.end, 40)
)
for (let p of utils.curveIntersectsX(points.start, points.cp1, points.cp2, points.end, 30))
snippets[p.y] = new Snippet('notch', p)
return part
},
}
export const utils_curveintersectsy = {
name: 'examples.utils_curveintersectsy',
draft: ({ Point, points, Path, paths, utils, snippets, Snippet, part }) => {
points.start = new Point(10, 45)
points.cp1 = new Point(50, 10)
points.cp2 = new Point(0, 80)
points.end = new Point(110, 70)
paths.curve = new Path().move(points.start).curve(points.cp1, points.cp2, points.end)
for (let y of [40, 50]) {
points['from' + y] = new Point(10, y)
points['to' + y] = new Point(110, y)
paths['line' + y] = new Path()
.move(points['from' + y])
.line(points['to' + y])
.attr('class', 'lining dashed')
}
snippets.i50 = new Snippet(
'notch',
utils.curveIntersectsY(points.start, points.cp1, points.cp2, points.end, 50)
)
for (let p of utils.curveIntersectsY(points.start, points.cp1, points.cp2, points.end, 40))
snippets[p.x] = new Snippet('notch', p)
return part
},
}
export const utils_curvesintersect = {
name: 'examples.utils_curvesintersect',
draft: ({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.A = new Point(10, 10)
points.Acp = new Point(310, 40)
points.B = new Point(110, 70)
points.Bcp = new Point(-210, 40)
points.C = new Point(20, -5)
points.Ccp = new Point(60, 300)
points.D = new Point(100, 85)
points.Dcp = new Point(70, -220)
paths.curveA = new Path().move(points.A).curve(points.Acp, points.Bcp, points.B)
paths.curveB = new Path().move(points.C).curve(points.Ccp, points.Dcp, points.D)
for (let p of utils.curvesIntersect(
points.A,
points.Acp,
points.Bcp,
points.B,
points.C,
points.Ccp,
points.Dcp,
points.D
)) {
snippets[part.getId()] = new Snippet('notch', p)
}
return part
},
}
export const utils_lineintersectscircle = {
name: 'examples.utils_lineintersectscircle',
draft: ({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.A = new Point(95, 45).attr('data-circle', 35).attr('data-circle-class', 'fabric')
points.B = new Point(55, 50)
points.C = new Point(75, 30)
points.D = new Point(55, 65)
points.E = new Point(115, 5)
points.F = new Point(65, 75)
points.G = new Point(125, 15)
paths.line1 = new Path().move(points.B).line(points.C)
paths.line2 = new Path().move(points.D).line(points.E)
paths.line3 = new Path().move(points.F).line(points.G)
let intersections1 = utils.lineIntersectsCircle(
points.A,
points.A.attributes.get('data-circle'),
points.B,
points.C
)
let intersections2 = utils.lineIntersectsCircle(
points.A,
points.A.attributes.get('data-circle'),
points.D,
points.E,
'y'
)
let intersections3 = utils.lineIntersectsCircle(
points.A,
points.A.attributes.get('data-circle'),
points.F,
points.G
)
snippets.first1 = new Snippet('bnotch', intersections1[0])
snippets.first2 = new Snippet('bnotch', intersections2[0])
snippets.second2 = new Snippet('notch', intersections2[1])
snippets.first3 = new Snippet('bnotch', intersections3[0])
snippets.second3 = new Snippet('notch', intersections3[1])
return box(part, 200, 80)
},
}
export const utils_lineintersectscurve = {
name: 'examples.utils_lineintersectscurve',
draft: ({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.A = new Point(10, 10)
points.Acp = new Point(310, 40)
points.B = new Point(110, 70)
points.Bcp = new Point(-210, 40)
points.E = new Point(20, -5)
points.D = new Point(100, 85)
paths.curve = new Path().move(points.A).curve(points.Acp, points.Bcp, points.B)
paths.line = new Path().move(points.E).line(points.D)
for (let p of utils.lineIntersectsCurve(
points.D,
points.E,
points.A,
points.Acp,
points.Bcp,
points.B
)) {
snippets[part.getId()] = new Snippet('notch', p)
}
return part
},
}
export const utils_linesintersect = {
name: 'examples.utils_linesintersect',
draft: ({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.A = new Point(10, 10)
points.B = new Point(50, 40)
points.C = new Point(15, 30)
points.D = new Point(60, 15)
paths.AB = new Path().move(points.A).line(points.B)
paths.CD = new Path().move(points.C).line(points.D)
snippets.X = new Snippet('notch', utils.linesIntersect(points.A, points.B, points.C, points.D))
return part
},
}
export const utils_pointonbeam = {
name: 'examples.utils_pointonbeam',
draft: ({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.from1 = new Point(10, 10)
points.to1 = new Point(90, 60)
points.from2 = new Point(10, 30)
points.to2 = new Point(90, 80)
points.b1 = new Point(170, 110)
points.b2 = new Point(170, 130)
let scatter = []
for (let i = 1; i < 36; i++) {
for (let j = 1; j < 27; j++) {
scatter.push(new Point(i * 10, j * 10))
}
}
let snippet
for (let point of scatter) {
if (utils.pointOnBeam(points.from1, points.to1, point)) snippet = 'notch'
else snippet = 'bnotch'
snippets[part.getId()] = new Snippet(snippet, point)
if (utils.pointOnBeam(points.from2, points.to2, point, 0.01)) {
snippet = 'notch'
} else snippet = 'bnotch'
snippets[part.getId()] = new Snippet(snippet, point)
}
paths.line1 = new Path().move(points.from1).line(points.to1).attr('class', 'fabric stroke-lg')
paths.lne1 = new Path().move(points.to1).line(points.b1).attr('class', 'fabric dashed')
paths.line2 = new Path().move(points.from2).line(points.to2).attr('class', 'fabric stroke-lg')
paths.lne2 = new Path().move(points.to2).line(points.b2).attr('class', 'fabric dashed')
return part
},
}
export const utils_pointoncurve = {
name: 'examples.utils_pointoncurve',
draft: ({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.start = new Point(10, 10)
points.cp1 = new Point(90, 10)
points.cp2 = new Point(10, 60)
points.end = new Point(90, 60)
let scatter = []
for (let i = 1; i < 19; i++) {
for (let j = 1; j < 14; j++) {
scatter.push(new Point(i * 10, j * 10))
}
}
let snippet
for (let point of scatter) {
if (utils.pointOnCurve(points.start, points.cp1, points.cp2, points.end, point)) {
snippet = 'notch'
} else snippet = 'bnotch'
snippets[part.getId()] = new Snippet(snippet, point)
}
paths.curve = new Path()
.move(points.start)
.curve(points.cp1, points.cp2, points.end)
.attr('class', 'fabric stroke-lg')
return part
},
}
export const utils_pointonline = {
name: 'examples.utils_pointonline',
draft: ({ Point, points, Path, paths, Snippet, snippets, utils, part }) => {
points.from1 = new Point(10, 10)
points.to1 = new Point(90, 60)
points.from2 = new Point(10, 30)
points.to2 = new Point(90, 80)
points.b1 = new Point(170, 110)
points.b2 = new Point(170, 130)
let scatter = []
for (let i = 1; i < 36; i++) {
for (let j = 1; j < 27; j++) {
scatter.push(new Point(i * 10, j * 10))
}
}
let snippet
for (let point of scatter) {
if (utils.pointOnLine(points.from1, points.to1, point)) snippet = 'notch'
else snippet = 'bnotch'
snippets[part.getId()] = new Snippet(snippet, point)
if (utils.pointOnLine(points.from2, points.to2, point, 0.01)) {
snippet = 'notch'
} else snippet = 'bnotch'
snippets[part.getId()] = new Snippet(snippet, point)
}
paths.line1 = new Path().move(points.from1).line(points.to1).attr('class', 'fabric stroke-lg')
paths.lne1 = new Path().move(points.to1).line(points.b1).attr('class', 'fabric dashed')
paths.line2 = new Path().move(points.from2).line(points.to2).attr('class', 'fabric stroke-lg')
paths.lne2 = new Path().move(points.to2).line(points.b2).attr('class', 'fabric dashed')
return part
},
}
export const utils_splitcurve = {
name: 'examples.utils_splitcurve',
draft: ({ Point, points, Path, paths, utils, part }) => {
points.from = new Point(40, 10)
points.to = new Point(40, 80)
paths.line = new Path().move(points.from).line(points.to).attr('class', 'lining dashed')
points.start = new Point(10, 15)
points.cp1 = new Point(80, 10)
points.cp2 = new Point(-50, 80)
points.end = new Point(110, 70)
points.i40 = utils.curveIntersectsX(points.start, points.cp1, points.cp2, points.end, 40)
let parts = utils.splitCurve(points.start, points.cp1, points.cp2, points.end, points.i40)
let colors = ['lining', 'interfacing']
for (let p of parts) {
let color = colors.pop()
paths[color] = new Path()
.move(p.start)
.curve(p.cp1, p.cp2, p.end)
.attr('class', 'stroke-xl ' + color)
}
return part
},
}

View file

@ -409,7 +409,6 @@ function draftHiBody({
.curve(points.body02cp1, points.body03cp2, points.body03)
.join(paths.allButDart)
.close()
.hide()
let gillPath = new Path()
.move(points.body17)

View file

@ -89,9 +89,7 @@ function hugoFront({
.line(points.raglanTipFront)
.join(neckOpeningParts[0].reverse())
paths.saBase.hide()
paths.seam = paths.saBase.clone().close().attr('class', 'fabric')
// FIXME: This pocket path is not getting set on the paths object
// It's something to do with the paths proxy which -- I assume -- is proxying the wrong object?
paths.seam = paths.saBase.clone().unhide().close().attr('class', 'fabric')
paths.pocket = new Path()
.move(points.pocketHem)
.line(points.pocketTip)

View file

@ -101,6 +101,6 @@ function hugoPocket({
export const pocket = {
name: 'hugo.pocket',
from: front,
hideDependencies: true,
// hideDependencies: true,
draft: hugoPocket,
}

View file

@ -47,6 +47,5 @@ function hugoPocketFacing({ sa, points, Path, paths, complete, paperless, macro,
export const pocketFacing = {
name: 'hugo.pocketFacing',
from: pocket,
hideDependencies: true,
draft: hugoPocketFacing,
}

View file

@ -61,7 +61,7 @@ function lunetiusLacerna({
.line(points.bottom)
.hide()
paths.saBase = new Path().move(points.top).line(points.topLeft).hide()
paths.seam = paths.saBase.join(paths.hem).join(paths.fold).attr('class', 'fabric').hide()
paths.seam = paths.saBase.join(paths.hem).join(paths.fold).attr('class', 'fabric')
// Complete?
if (complete) {

View file

@ -17,7 +17,7 @@ const pluginRound = ({ Point, points, Path, paths, macro, options, part }) => {
.attr('class', 'note dashed')
const opts = {
radius: options.roundRadius,
radius: Number(options.roundRadius),
hide: options.roundHide,
}

View file

@ -7,6 +7,7 @@ const pluginScalebox = ({ Point, points, macro, options, part }) => {
macro(options.scaleboxType, {
at: points.a,
rotate: options.scaleboxRotation,
})
}
@ -19,6 +20,7 @@ export const scalebox = {
after: base,
options: {
scaleboxType: { dflt: 'scalebox', list: ['scalebox', 'miniscale'], menu: 'scalebox' },
scaleboxRotation: { deg: 0, min: 0, max: 360, menu: 'scalebox' },
},
draft: pluginScalebox,
}

View file

@ -39,8 +39,7 @@ function teaganBack({
.join(paths.saBase)
.line(points.cfHem)
.close()
.hide()
.attr('class', 'fabric')
.setClass('fabric')
// Set store values required to draft sleevecap
store.set('sleevecapEase', 0)

View file

@ -0,0 +1,18 @@
env:
browser: true
es2021: true
extends: eslint:recommended
overrides:
- files: ["*.yaml", "*.yml"]
plugins: ["yaml"]
extends: ["plugin:yaml/recommended"]
parserOptions:
ecmaVersion: latest
sourceType: module
rules: {}
globals:
it: readonly
describe: readonly
process: readonly
__dirname: readonly

View file

@ -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.

View file

@ -1,13 +1,13 @@
![FreeSewing](https://static.freesewing.org/banner.png)
<p align='center'><a
href="https://www.npmjs.com/package/@freesewing/examples"
title="@freesewing/examples on NPM"
><img src="https://img.shields.io/npm/v/@freesewing/examples.svg"
alt="@freesewing/examples on NPM"/>
href="https://www.npmjs.com/package/@freesewing/unice"
title="@freesewing/unice on NPM"
><img src="https://img.shields.io/npm/v/@freesewing/unice.svg"
alt="@freesewing/unice on NPM"/>
</a><a
href="https://opensource.org/licenses/MIT"
title="License: MIT"
><img src="https://img.shields.io/npm/l/@freesewing/examples.svg?label=License"
><img src="https://img.shields.io/npm/l/@freesewing/unice.svg?label=License"
alt="License: MIT"/>
</a><a
href="https://deepscan.io/dashboard#view=project&tid=2114&pid=2993&bid=23256"
@ -15,10 +15,10 @@
><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%3Aexamples"
title="Open issues tagged pkg:examples"
><img src="https://img.shields.io/github/issues/freesewing/freesewing/pkg:examples.svg?label=Issues"
alt="Open issues tagged pkg:examples"/>
href="https://github.com/freesewing/freesewing/issues?q=is%3Aissue+is%3Aopen+label%3Apkg%3Aunice"
title="Open issues tagged pkg:unice"
><img src="https://img.shields.io/github/issues/freesewing/freesewing/pkg:unice.svg?label=Issues"
alt="Open issues tagged pkg:unice"/>
</a><a
href="#contributors-"
title="All Contributors"
@ -46,9 +46,9 @@
alt="Follow @freesewing_org on Twitter"/>
</a></p>
# @freesewing/examples
# @freesewing/unice
A FreeSewing pattern holding examples for our documentation
A FreeSewing pattern for a basic, highly-customizable underwear pattern
@ -63,7 +63,7 @@ A FreeSewing pattern holding examples for our documentation
This repository is our *monorepo* holding all our NPM designs, plugins, other NPM packages, and (web)sites.
This folder holds: @freesewing/examples
This folder holds: @freesewing/unice
If you're not entirely sure what to do or how to start, type this command:

View file

@ -1,4 +1,4 @@
// This file is auto-generated | All changes you make will be overwritten.
export const name = "@freesewing/examples"
export const name = "@freesewing/unice"
export const version = "3.0.0-alpha.0"
export const data = { name, version }

View file

@ -1,8 +1,8 @@
{
"name": "@freesewing/examples",
"name": "@freesewing/unice",
"version": "3.0.0-alpha.0",
"description": "A FreeSewing pattern holding examples for our documentation",
"author": "Joost De Cock <joost@joost.at> (https://github.com/joostdecock)",
"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",
@ -15,9 +15,14 @@
},
"keywords": [
"freesewing",
"documentation",
"example",
"parametric design"
"design",
"diy",
"fashion",
"made to measure",
"parametric design",
"pattern",
"sewing",
"sewing pattern"
],
"type": "module",
"module": "dist/index.mjs",
@ -41,9 +46,7 @@
"peerDependencies": {
"@freesewing/core": "^3.0.0-alpha.0",
"@freesewing/plugin-bundle": "^3.0.0-alpha.0",
"@freesewing/config-helpers": "^3.0.0-alpha.0",
"@freesewing/plugin-mirror": "^3.0.0-alpha.0",
"@freesewing/plugin-gore": "^3.0.0-alpha.0"
"@freesewing/config-helpers": "^3.0.0-alpha.0"
},
"dependencies": {},
"devDependencies": {

308
designs/unice/src/back.mjs Normal file
View file

@ -0,0 +1,308 @@
import { front } from './front.mjs'
export const back = {
name: 'unice.back',
measurements: ['waist', 'seat', 'waistToSeat', 'waistToUpperLeg', 'hips', 'waistToHips'],
optionalMeasurements: ['crossSeam', 'crossSeamFront'],
options: {
backToFrontLength: 1.15, // Maybe include this in advanced options?
backToFrontWidth: 1.1, // Maybe include this in advanced options?
gussetRatio: 0.7, // Relationship between front and back gusset widths
backDip: { pct: 2.5, min: -5, max: 15, menu: 'style' },
backExposure: { pct: 20, min: -30, max: 90, menu: 'style' },
},
after: front,
draft: ({
utils,
store,
sa,
Point,
points,
Path,
paths,
options,
measurements,
complete,
paperless,
macro,
part,
}) => {
// Design pattern here
// Create points
points.backWaistMid = new Point(measurements.seat / 4, 0)
points.backWaistBandLeft = new Point(
store.get('sideSeamWaist').x / options.backToFrontWidth,
store.get('sideSeamWaist').y
)
points.backLegOpeningLeft = new Point(
store.get('sideSeamHip').x / options.backToFrontWidth,
store.get('sideSeamHip').y
)
// back height is given by (estimated) cross seam, minus front and gusset lengths
// this does not account for vertical stretch yet
const backHeight =
store.get('crossSeam') - store.get('frontHeight') - options.gussetLength * measurements.seat
// calculate the actual back height, using yScale above and yScaleReduced below leg opening
const backHeightAbove = store.get('frontHeightAbove') // part above has same height front and back
let backHeightBelow
backHeightBelow =
store.get('yScale') * (backHeight - backHeightAbove / store.get('yScaleReduced'))
const backHeightReduced = backHeightBelow + backHeightAbove
points.backGussetLeft = new Point(
measurements.seat / 4 -
((measurements.waist * options.gussetWidth * store.get('xScale')) / options.gussetRatio) *
options.backToFrontWidth,
backHeightReduced
)
points.backGussetMid = new Point(measurements.seat / 4, backHeightReduced)
points.backGussetRight = points.backGussetLeft.flipX(points.backWaistMid)
points.backLegOpeningRight = points.backLegOpeningLeft.flipX(points.backWaistMid)
points.backWaistBandRight = points.backWaistBandLeft.flipX(points.backWaistMid)
points.backWaistBandMid = points.backWaistBandLeft
.shiftFractionTowards(points.backWaistBandRight, 0.5)
.shift(270, measurements.waistToUpperLeg * options.backDip)
/* Middle point for label */
points.backMidMid = points.backLegOpeningLeft.shiftFractionTowards(
points.backLegOpeningRight,
0.5
)
// Create control points
/* Control point for waistband dip */
points.backWaistBandLeftCp1 = points.backWaistBandMid.shift(
0,
points.backWaistBandMid.dx(points.backWaistBandLeft) / 3
)
/* Flip points to right side */
points.backWaistBandRightCp1 = points.backWaistBandLeftCp1.flipX(points.backWaistMid)
// Shape back coverage
/* Only have to do this on one side */
points.backLegOpeningCorner = utils.beamsIntersect(
points.backLegOpeningLeft,
points.backLegOpeningLeft.shift(180, points.backGussetLeft.dy(points.backLegOpeningLeft)),
points.backGussetLeft,
points.backGussetLeft.shift(270, points.backGussetLeft.dy(points.backLegOpeningLeft))
)
if (options.backExposure >= 0) {
/* If back exposure is high, like a thong style */
/* This controls the hip bit */
points.backLegOpeningLeftCp1 = points.backLegOpeningLeft.shiftFractionTowards(
points.backLegOpeningCorner,
options.backExposure
)
/* This controls the center bit */
points.backGussetLeftCp1 = points.backGussetLeft.shiftFractionTowards(
points.backWaistBandMid,
options.backExposure
)
points.backGussetLeft = points.backGussetLeft.shiftFractionTowards(
points.backGussetMid,
options.backExposure
) // This narrows the back of the gusset
points.backGussetRight = points.backGussetLeft.flipX(points.backWaistMid)
} else {
/* If back exposure is low and flares out to cover more */
store.set('adjustedBackExposure', options.backExposure * store.get('adjustedLegOpening')) // flare depends on leg opening
/* This controls the hip bit */
points.backLegOpeningLeftCp1 = points.backLegOpeningLeft.shift(
-45,
store.get('adjustedBackExposure') * points.backWaistBandMid.dx(points.backWaistBandLeft)
)
/* This controls the taper to gusset */
points.backGussetLeftCp1 = points.backGussetLeft.shift(
115,
store.get('adjustedBackExposure') * points.backWaistBandMid.dx(points.backWaistBandLeft)
)
/* center of the flare and its control points are on a line parallel to the backGussetLeft to backLegOpeningLeft line
* first, define the points on that line */
points.backFlare = points.backGussetLeft.shiftFractionTowards(points.backLegOpeningLeft, 0.5)
// points.backFlareCp1 = points.backGussetLeft.shiftFractionTowards(points.backLegOpeningLeft, 0.5 - store.get('adjustedBackExposure'))
points.backFlareCp1 = points.backGussetLeft.shiftFractionTowards(
points.backLegOpeningLeft,
0.7
)
points.backFlareCp2 = points.backGussetLeft.shiftFractionTowards(
points.backLegOpeningLeft,
0.3
)
/* then shift all three points outward */
points.backFlareLeft = points.backFlare.shift(
215,
points.backWaistBandMid.dx(points.backWaistBandLeft) * store.get('adjustedBackExposure') * 2
)
points.backFlareLeftCp1 = points.backFlareCp1.shift(
215,
points.backWaistBandMid.dx(points.backWaistBandLeft) * store.get('adjustedBackExposure') * 2
)
points.backFlareLeftCp2 = points.backFlareCp2.shift(
215,
points.backWaistBandMid.dx(points.backWaistBandLeft) * store.get('adjustedBackExposure') * 2
)
/* Flip points to the right */
points.backFlareRight = points.backFlareLeft.flipX(points.backWaistBandMid)
points.backFlareRightCp1 = points.backFlareLeftCp1.flipX(points.backWaistMid)
points.backFlareRightCp2 = points.backFlareLeftCp2.flipX(points.backWaistMid)
}
/* Flip points to the right */
points.backLegOpeningRightCp1 = points.backLegOpeningLeftCp1.flipX(points.backWaistMid)
points.backGussetRightCp1 = points.backGussetLeftCp1.flipX(points.backWaistMid)
// Draw paths
if (options.backExposure >= 0) {
paths.seam = new Path()
.move(points.backWaistBandMid)
.curve(points.backWaistBandLeftCp1, points.backWaistBandLeft, points.backWaistBandLeft) // Waist band dip
.line(points.backLegOpeningLeft)
.curve(points.backLegOpeningLeftCp1, points.backGussetLeftCp1, points.backGussetLeft)
.line(points.backGussetMid)
.line(points.backGussetRight)
.curve(points.backGussetRightCp1, points.backLegOpeningRightCp1, points.backLegOpeningRight)
.line(points.backWaistBandRight)
.curve(points.backWaistBandRight, points.backWaistBandRightCp1, points.backWaistBandMid) // Waist band dip
.close()
.attr('class', 'fabric')
} else {
paths.seam = new Path()
.move(points.backWaistBandMid)
.curve(points.backWaistBandLeftCp1, points.backWaistBandLeft, points.backWaistBandLeft) // Waist band dip
.line(points.backLegOpeningLeft)
.curve(points.backLegOpeningLeftCp1, points.backFlareLeftCp1, points.backFlareLeft)
.curve(points.backFlareLeftCp2, points.backGussetLeftCp1, points.backGussetLeft)
.line(points.backGussetMid)
.line(points.backGussetRight)
.curve(points.backGussetRightCp1, points.backFlareRightCp2, points.backFlareRight)
.curve(points.backFlareRightCp1, points.backLegOpeningRightCp1, points.backLegOpeningRight)
.line(points.backWaistBandRight)
.curve(points.backWaistBandRight, points.backWaistBandRightCp1, points.backWaistBandMid) // Waist band dip
.close()
.attr('class', 'fabric')
}
// Store points for use in other parts
/* Store gusset points for use in gusset */
store.set('backGussetLeft', points.backGussetLeft)
store.set('backGussetRight', points.backGussetRight)
/* Store lengths for use in elastic */
paths.backLegOpening =
options.backExposure >= 0
? new Path()
.move(points.backGussetRight)
.curve(
points.backGussetRightCp1,
points.backLegOpeningRightCp1,
points.backLegOpeningRight
)
.setHidden(true)
: new Path()
.move(points.backGussetRight)
.curve(points.backGussetRightCp1, points.backFlareRightCp2, points.backFlareRight)
.curve(
points.backFlareRightCp1,
points.backLegOpeningRightCp1,
points.backLegOpeningRight
)
.setHidden(true)
store.set('backLegOpeningLength', paths.backLegOpening.length())
paths.backWaistBand = new Path()
.move(points.backWaistBandRight)
.curve(points.backWaistBandRightCp1, points.backWaistBandLeftCp1, points.backWaistBandLeft)
.setHidden(true)
store.set('backWaistBandLength', paths.backWaistBand.length())
// Complete?
if (complete) {
if (sa) {
paths.sa = paths.seam.offset(sa).attr('class', 'fabric sa')
}
}
macro('title', {
at: points.backMidMid,
nr: 2,
title: 'back',
})
macro('grainline', {
from: points.backGussetMid,
to: points.backGussetMid.shiftFractionTowards(points.backWaistBandMid, 0.4),
})
points.scaleboxAnchor = points.scalebox = points.backMidMid.shift(90, -50)
macro('miniscale', { at: points.scalebox })
// Paperless?
if (paperless) {
macro('hd', {
from: points.backWaistBandRight,
to: points.backWaistBandLeft,
y: points.backWaistBandRight.y + sa - 15,
})
macro('hd', {
from: points.backLegOpeningRight,
to: points.backLegOpeningLeft,
y: points.backLegOpeningRight.y + sa - 15,
})
macro('hd', {
from: points.backGussetLeft,
to: points.backGussetRight,
y: points.backGussetLeft.y + sa + 15,
})
macro('vd', {
from: points.backWaistBandMid,
to: points.backGussetMid,
x: points.backWaistBandMid.x + sa + 15,
})
if (options.backExposure >= 0) {
macro('pd', {
path: new Path()
.move(points.backGussetRight)
.curve(
points.backGussetRightCp1,
points.backLegOpeningRightCp1,
points.backLegOpeningRight
),
d: 15,
})
} else {
macro('pd', {
path: new Path()
.move(points.backGussetRight)
.curve(points.backGussetRightCp1, points.backFlareRightCp2, points.backFlareRight)
.curve(
points.backFlareRightCp1,
points.backLegOpeningRightCp1,
points.backLegOpeningRight
),
d: 15,
})
}
}
return part
},
}

View file

@ -0,0 +1,39 @@
import { gusset } from './gusset.mjs'
export const elastic = {
name: 'unice.elastic',
options: {
elasticStretch: { pct: 8, min: 5, max: 15, menu: 'fit' },
},
after: gusset,
draft: ({ options, Point, points, Path, paths, store, utils, units, sa, part }) => {
// Stretch utility method
store.set('elasticScale', utils.stretchToScale(options.elasticStretch))
// Design pattern here
const legOpeningLength =
store.get('frontLegOpeningLength') +
store.get('backLegOpeningLength') +
store.get('gussetSideLength')
const waistBandLength = store.get('frontWaistBandLength') + store.get('backWaistBandLength')
points.elasticInfo = new Point(0, 0)
.attr('data-text', 'cutTwoPiecesOfElasticToFinishTheLegOpenings')
.attr('data-text', ':')
.attr('data-text', units(legOpeningLength * store.get('elasticScale') + 2 * sa))
.attr('data-text', '\n')
.attr('data-text', 'cutOnePieceOfElasticToFinishTheWaistBand')
.attr('data-text', ':')
.attr('data-text', units(waistBandLength * store.get('elasticScale') + 2 * sa))
// Draw a box around the text, so the part shows up correctly.
paths.box = new Path()
.move(new Point(-10, -10))
.line(new Point(-10, 15))
.line(new Point(200, 15))
.line(new Point(200, -10))
.close()
return part
},
}

340
designs/unice/src/front.mjs Normal file
View file

@ -0,0 +1,340 @@
import { pluginBundle } from '@freesewing/plugin-bundle'
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
},
plugins: [pluginBundle],
draft: ({
utils,
store,
sa,
Point,
points,
Path,
paths,
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.2, 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)
.setHidden(true) // 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)
console.log('points', points)
/* 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
)
.setHidden(true)
store.set('frontLegOpeningLength', paths.frontLegOpening.length())
paths.frontWaistBand = new Path()
.move(points.frontWaistBandRight)
.curve(points.frontWaistBandRightCp1, points.frontWaistBandLeftCp1, points.frontWaistBandLeft)
.setHidden(true)
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
},
}

View file

@ -0,0 +1,113 @@
import { back } from './back.mjs'
export const gusset = {
name: 'unice.gusset',
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' },
},
after: back,
draft: ({
store,
sa,
Point,
points,
Path,
paths,
options,
measurements,
complete,
paperless,
macro,
part,
}) => {
// Design pattern here
var yScaleDoubleLayer
yScaleDoubleLayer = (1 + store.get('yScale')) / 2 // double layer of fabric stretches half as much
// Create points
points.frontGussetLeft = new Point(store.get('frontGussetLeft').x, 0)
points.backGussetLeft = new Point(
store.get('backGussetLeft').x,
measurements.seat * options.gussetLength * yScaleDoubleLayer
)
points.frontGussetRight = new Point(store.get('frontGussetRight').x, 0)
points.backGussetRight = new Point(
store.get('backGussetRight').x,
measurements.seat * options.gussetLength * yScaleDoubleLayer
)
// Create control points
points.gussetCp1 = points.frontGussetLeft
.shiftFractionTowards(points.backGussetLeft, 0.5)
.shift(180, points.frontGussetRight.x / -15)
// Flip points to right side
points.gussetCp2 = points.gussetCp1.flipX(store.get('frontGussetMid'))
// Create point for title
points.frontMidMid = points.gussetCp1.shiftFractionTowards(points.gussetCp2, 0.5)
/* Store lengths for use in elastic */
paths.gussetLegOpening = new Path()
.move(points.backGussetRight)
.curve(points.backGussetRight, points.gussetCp2, points.frontGussetRight)
.setHidden(true)
store.set('gussetSideLength', paths.gussetLegOpening.length())
// Draw paths
paths.seam = new Path()
.move(points.frontGussetLeft)
.curve(points.gussetCp1, points.backGussetLeft, points.backGussetLeft)
.line(points.backGussetRight)
.curve(points.backGussetRight, points.gussetCp2, points.frontGussetRight)
.line(points.frontGussetLeft) // Without this, doesn't generate seam allowance
.close()
.attr('class', 'fabric')
// Complete?
if (complete) {
if (sa) {
paths.sa = paths.seam.offset(sa).attr('class', 'fabric sa')
}
macro('title', {
at: points.frontMidMid,
nr: 3,
title: 'gusset',
})
}
// Paperless?
if (paperless) {
macro('hd', {
from: points.frontGussetLeft,
to: points.frontGussetRight,
y: points.frontGussetLeft.y + sa + 15,
})
macro('hd', {
from: points.backGussetLeft,
to: points.backGussetRight,
y: points.backGussetLeft.y + sa + 15,
})
macro('vd', {
from: points.frontGussetRight,
to: points.backGussetRight,
x: points.frontGussetRight.x + sa + 15,
})
}
return part
},
}

View file

@ -0,0 +1,15 @@
import { Design } from '@freesewing/core'
import { front } from './front.mjs'
import { back } from './back.mjs'
import { gusset } from './gusset.mjs'
import { elastic } from './elastic.mjs'
import { data } from '../data.mjs'
// Setup our new design
const Unice = new Design({
data,
parts: [front, back, gusset, elastic],
})
// Named exports
export { front, back, gusset, elastic, Unice }

View file

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

View file

@ -3,14 +3,21 @@ title: Design
order: 10
---
The `Design` named export in FreeSewing's core library serves a single purpose:
To create new pattern designs.
The `Design` named export in FreeSewing's core library is a contructor that
creates new pattern designs.
## Design constructor
## Signature
```js
import { Design } from "@freesewing/core"
Pattern Design({
array parts,
object data
})
```
## Example
```js
const Sorcha = new Design({
// design configuration here
})
@ -21,9 +28,8 @@ It takes a single argument, an object holding the design's configuration.
## Design configuration
Since a design's configuration is managed at the part level,
the Design constructor only expects a `parts` attribute that should
the Design configuration object only requires a `parts` property that should
hold an array of parts to include in the Design.
```js
@ -44,14 +50,29 @@ to [the Store](/reference/api/store).
const Sorcha = new Design({
parts: [ front, back, sleeve ],
data: {
// Your custom data here
version: 3,
price: 12,
currency: 'euro'
}
})
```
<Tip>
## Notes
This Design constructor is a _super-constructor_.
The Design constructor is a _super-constructor_.
It will return a constructor method that will a pattern based on your design.
</Tip>
## Properties
In addition to the returned constructor method, an instantiated Design object
also provides the following properties:
### Design.designConfig
This holds the design configuration as passed to the Design constructor.
### Design.patternConfig
Holds the resolved pattern configuration based on the configuration passed to the Design constructor.

View file

@ -17,24 +17,24 @@ Path path._curve(Point cp2, Point to)
## Example
<TabbedExample part="path__curve" caption="Example of the Path.\_curve() method">
<Example caption="Example of the Path.\_curve() method">
```js
({ Point, points, Path, paths, part }) => {
points.from = new Point(5, 20);
points.cp2 = new Point(60, 30);
points.cp2 = new Point(60, 50);
points.to = new Point(90, 20);
paths.line = new Path()
.move(points.from)
._curve(points.cp2, points.to)
.attr("data-text", "Path._curve()")
.attr("data-text-class", "text-sm center fill-note");
.setText("Path._curve()", "text-sm center fill-note")
.attr("data-text-dy", -1)
return part
}
```
</TabbedExample>
</Example>
## Notes

View file

@ -4,31 +4,17 @@ title: Path.addClass()
The `Path.addClass()` method adds a CSS class to the path.
## Signature
```js
Path path.addClass(string className)
```
<Tip | compact>This method is chainable as it returns the `Path` object</Tip>
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
<Tip>
###### This method exists to save you some typing
Note that the two following calls yield the same result:
```js
path.attr('class', 'fabric')
path.addClass('fabric')
```
So the only purpose of this method is to save your some typing.
</Tip>
<Example part="path_addclass">
Example of the Path.addClass() method
</Example>
## Example
<Example caption="Example of the Path.addClass() method">
```js
({ Point, points, Path, paths, part }) => {
@ -43,3 +29,15 @@ Example of the Path.addClass() method
return part
}
```
</Example>
## Notes
The main purpose of this method is to save your some typing,
as the two following calls yield the same result:
```js
path.attr('class', 'fabric')
path.addClass('fabric')
```

View file

@ -0,0 +1,47 @@
---
title: Path.addText()
---
The `Path.addText()` method adds text to the path.
## Signature
```js
Path path.addText(string text, string className)
```
The second argument will optionally be used to set the CSS class for the text.
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
## Example
<Example caption="Example of the Path.addText() method">
```js
({ Point, points, Path, paths, part }) => {
points.from = new Point(5, 10)
points.to = new Point(95, 10)
paths.line = new Path()
.move(points.from)
.line(points.to)
.addText('FreeSewing rocks')
return part
}
```
</Example>
## Notes
The main purpose of this method is to save your some typing,
as the two following calls yield the same result:
```js
path.attr('data-text', 'Hello')
path.addText('Hello')
```
The difference with [Path.setText()](/reference/api/path/addtext) is that this
method will add to the existing text whereas `Path.setText()` will overwrite
existing text on the path,

View file

@ -5,13 +5,13 @@ title: Path.asPathString()
This `Path.asPathString()` returs the path as a string that can be used
as the `d` attribute for an SVG `path` element.
## Signature
```js
string path.asPathString()
```
<Note>
## Notes
This method is mostly aimed at people looking to implement their own rendering
methods, or integrate with SVG processing tools.
</Note>

View file

@ -8,6 +8,8 @@ It calls `this.attributes.add()` under the hood, and returns the Path object.
If the third parameter is set to `true` it will call `this.attributes.set()`
instead, thereby overwriting the value of the attribute.
## Signature
```js
Path path.attr(
string name,
@ -18,31 +20,35 @@ Path path.attr(
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
<Example part="path_attr">
Example of the Path.attr() method
</Example>
## Example
<Example caption=" Example of the Path.attr() method">
```js
({ Point, points, Path, paths, part }) => {
points.from = new Point(10, 50);
points.cp1 = new Point(40, 10);
points.cp2 = new Point(90, 30);
points.to = new Point(50, 90);
points.from = new Point(10, 20);
points.cp1 = new Point(20, -10);
points.cp2 = new Point(50, 50);
points.to = new Point(70, 20);
paths.example = new Path()
.move(points.from)
.curve(points.cp1, points.cp2, points.to)
.addClass("canvas")
.attr("data-text", "freesewingIsMadeByJoostDeCockAndContributors")
.attr("data-text-class", "text-xs center");
.setClass("canvas")
.setText("FreeSewing rocks", "text-xs center")
return part
}
```
</Example>
<Note>
Methods like `addClass`, `setClass`, `addCircle`, `setCircle`, `adddText`, and `setText`
## Notes
Methods like
[`Path.addClass`](/reference/api/path/addclass),
[`Path.setClass`](/reference/api/path/setclass),
[`Path.adddText`](/reference/api/path/addtext), and
[`Path.setText`](/reference/api/path/settext)
all call this method under the hood.
</Note>

View file

@ -5,6 +5,8 @@ title: Path.bbox()
The `Path.bbox()` method returns an object describing the bounding box of a path.
In other words, it gives you a rectangle the Path fits in.
## Signature
```js
object path.bbox()
```
@ -18,8 +20,6 @@ It returns an object with a signature like this:
}
```
<Note>
## Notes
This method is mostly aimed at people looking to implement their own layout solution.
</Note>

View file

@ -4,30 +4,36 @@ title: Path.clone()
The `Path.clone()` method returns a new `Path` object that is a deep copy of this path.
## Signature
```js
Path path.clone()
```
<Example part="path_clone">
Example of the Path.clone() method
## Example
<Example caption="Example of the Path.clone() method">
```js
({ Point, points, Path, paths, part }) => {
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C);
paths.clone = paths.example
.clone()
.setClass("note lashed stroke-xl")
.attr("style", "stroke-opacity: 0.5");
return part
}
```
</Example>
```js
let { Point, points, Path, paths } = part.shorthand();
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C);
paths.clone = paths.example
.clone()
.attr("class", "note lashed stroke-l")
.attr("style", "stroke-opacity: 0.5");
```

View file

@ -1,29 +1,36 @@
---
title: close()
title: Path.close()
---
The `Path.close()` method closes a path by drawing a straight line from the current position to the path's start.
## Signature
```js
Path path.close()
```
Closes a path by drawing a straight line from the current position to the path's start.
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
<Example part="path_close">
Example of the Path.close() method
## Example
<Example caption="Example of the Path.close() method">
```js
({ Point, points, Path, paths, part }) => {
points.from = new Point(10, 20)
points.cp2 = new Point(60, 30)
points.to = new Point(90, 20)
paths.line = new Path()
.move(points.from)
._curve(points.cp2, points.to)
.close()
.reverse() // To keep text from being upside-down
.setText('Path._close()', 'text-sm right fill-note')
return part
}
```
</Example>
```js
let { Point, points, Path, paths } = part.shorthand();
points.from = new Point(10, 20);
points.cp2 = new Point(60, 30);
points.to = new Point(90, 20);
paths.line = new Path()
.move(points.from)
._curve(points.cp2, points.to)
.close()
.reverse() // To keep text from being upside-down
.attr("data-text", "Path._close()")
.attr("data-text-class", "text-sm right fill-note");
```

View file

@ -1,28 +1,36 @@
---
title: curve()
title: Path.curve()
---
The `Path.curve()` method draws a cubic Bezier curve from the current position
via two control points to a given endpoint.
## Signature
```js
Path path.curve(Point cp1, Point cp2, Point to)
```
Draws a cubic Bezier curve from the current position via two control points to a given endpoint.
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
<Example part="path_curve">
Example of the Path.curve() method
## Example
<Example caption="Example of the Path.curve() method">
```js
({ Point, points, Path, paths, part }) => {
points.from = new Point(10, 20);
points.cp1 = new Point(40, 0);
points.cp2 = new Point(60, 40);
points.to = new Point(90, 20);
paths.line = new Path()
.move(points.from)
.curve(points.cp1, points.cp2, points.to)
.setText("Path.curve()", "text-sm center fill-note")
return part
}
```
</Example>
```js
let { Point, points, Path, paths } = part.shorthand();
points.from = new Point(10, 20);
points.cp1 = new Point(40, 0);
points.cp2 = new Point(60, 30);
points.to = new Point(90, 20);
paths.line = new Path()
.move(points.from)
.curve(points.cp1, points.cp2, points.to)
.attr("data-text", "Path.curve()")
.attr("data-text-class", "text-sm center fill-note");
```

View file

@ -1,35 +1,26 @@
---
title: "curve_()"
title: Path.curve_()
---
The `Path.curve_()` method draws a cubic Bezier curve from the current position
via two control points to a given endpoint. However, the end control point is
identical to the end point.
## Signature
```js
Path path.curve_(Point cp1, Point to)
```
Draws a cubic Bezier curve from the current position via two control points to a given endpoint.
However, the end control point is identical to the end point.
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
<Tip>
## Example
###### This method exists to save you some typing
Note that the two following calls yield the same result:
<Example caption="Example of the Path.curve\_() method">
```js
.curve(point1, point2, point2)
.curve_(point1, point2)
```
So the only purpose of this method is to save your some typing;
</Tip>
<Example part="path_curve_">
Example of the Path.curve\_() method
</Example>
```js
let { Point, points, Path, paths } = part.shorthand();
({ Point, points, Path, paths, part }) => {
points.from = new Point(10, 20);
points.cp1 = new Point(40, 0);
@ -38,6 +29,20 @@ Example of the Path.curve\_() method
paths.line = new Path()
.move(points.from)
.curve_(points.cp1, points.to)
.attr("data-text", "Path.curve_()")
.attr("data-text-class", "text-sm center fill-note");
.setText("Path.curve_()", "text-sm center fill-note")
.attr("data-text-dy", -1)
return part
}
```
</Example>
## Notes
The main purpose of this method is to save your some typing,
as the two following calls yield the same result:
```js
.curve(point1, point2, point2)
.curve_(point1, point2)
```

View file

@ -1,42 +1,49 @@
---
title: divide()
title: Path.divide()
---
The `Path.divide()` method breaks a path apart in an array of atomic paths. An
atomic path is a path that can't be divided further and is always made up of
one move + one drawing operation.
## Signature
```js
array path.divide()
```
Breaks a path apart in an array of atomic paths. An atomic path is a path that can't be divided further and is
always made up of one move + one drawing operation.
<Example part="path_divide">
Example of the Path.divide() method
</Example>
## Example
<Example caption="Example of the Path.divide() method">
```js
let { Point, points, Path, paths } = part.shorthand();
({ Point, points, Path, paths, part }) => {
points.A = new Point(55, 40);
points.B = new Point(10, 70);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 60);
points.CCp1 = new Point(50, -30);
points.D = new Point(50, 80);
points.DCp1 = new Point(140, 50);
points.A = new Point(55, 40);
points.B = new Point(10, 70);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 60);
points.CCp1 = new Point(50, -30);
points.D = new Point(50, 80);
points.DCp1 = new Point(140, 50);
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.curve(points.DCp1, points.DCp1, points.D)
.close();
let style = "stroke-width: 4; stroke-opacity: 0.5;";
let i = 0;
for (let p of paths.example.divide()) {
i++;
paths[i] = p
.attr("style", style)
.attr('style', `stroke: hsl(${i * 70}, 100%, 50%)`)
}
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.curve(points.DCp1, points.DCp1, points.D)
.close();
let style = "stroke-width: 4; stroke-opacity: 0.5;";
let i = 0;
for (let p of paths.example.divide()) {
i++;
paths[i] = p
.attr("style", style)
.attr("style", `stroke: hsl(${i * 70}, 100%, 50%)`);
return part
}
```
</Example>

View file

@ -1,5 +1,5 @@
---
title: edge()
title: Path.edge()
---
```js
@ -17,36 +17,37 @@ Returns the Point object at the edge of the path you specify. Edge must be one o
- `bottomLeft`
- `bottomRight`
<Example part="path_edge">
Example of the Path.edge() method
</Example>
<Example caption="Example of the Path.edge() method">
```js
let { Point, points, Path, paths, Snippet, snippets } = part.shorthand();
({ Point, points, Path, paths,snippets, Snippet, part }) => {
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
points.D = new Point(-60, 90);
points.E = new Point(90, 190);
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
points.D = new Point(-60, 90)
points.E = new Point(90, 190)
paths.demo = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.curve(points.E, points.D, points.A)
.close()
for (let i of [
"topLeft",
"topRight",
"bottomLeft",
"bottomRight",
"top",
"left",
"bottom",
"right"
]) snippets[i] = new Snippet("notch", paths.demo.edge(i))
paths.demo = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.curve(points.E, points.D, points.A)
.close();
for (let i of [
"topLeft",
"topRight",
"bottomLeft",
"bottomRight",
"top",
"left",
"bottom",
"right"
]) snippets[i] = new Snippet("notch", paths.demo.edge(i));
return part
}
```
</Example>

View file

@ -13,8 +13,13 @@ Path new Path();
A Path objects comes with the following properties:
- `render` : Set this to `false` to not render the path (exclude it from the output)
- `attributes` : An [Attributes](/reference/api/attributes) instance holding the path's attributes
- `attributes` : An [Attributes](/reference/api/attributes) instance holding
the path's attributes
- `hidden` : When this is `true` the path will be hidden (excluded it from the
output). See [Path.hide()](/reference/api/path/hide),
[Path.unhide()](/reference/api/path/unhide), and
[Path.setHidden()](/reference/api/path/sethidden) for various methods that
allow setting this in a chainable way.
In addition, a Path object exposes the following methods:

View file

@ -1,30 +1,38 @@
---
title: end()
title: Path.end()
---
The `Path.end()` method returns the Point object at the end of the path.
## Signature
```js
Point path.end()
```
Returns the Point object at the end of the path.
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
<Example part="path_end">
Example of the Path.end() method
## Example
<Example caption="Example of the Path.end() method">
```js
({ Point, points, Path, paths, snippets, Snippet, part }) => {
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
paths.demo = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
snippets.end = new Snippet("notch", paths.demo.end())
return part
}
```
</Example>
```js
let { Point, points, Path, paths, Snippet, snippets } = part.shorthand();
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
paths.demo = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C);
snippets.end = new Snippet("notch", paths.demo.end());
```

View file

@ -1,37 +1,33 @@
---
title: hide()
title: Path.hide()
---
The `Path.hide()` hides the path so it does not appear in the output.
## Signature
```js
Path path.attr(
string name,
mixed value,
bool overwrite = false
)
Path path.hide()
```
This `Path.attr()` method calls `this.attributes.add()` under the hood, but returns the Path object.
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
This allows you to chain different calls together as in the example below.
## Example
If the third parameter is set to `true` it will call `this.attributes.set()` instead, thereby overwriting the value of the attribute.
<Example caption="Example of the Path.hide() method">
```js
({ Point, points, Path, paths, part }) => {
<Example part="path_attr">
Example of the Path.attr() method
points.top = new Point(50, 0)
points.left = new Point (20,50)
points.right = new Point (80,50)
paths.a = new Path().move(points.top).line(points.right).setText('a')
paths.b = new Path().move(points.right).line(points.left).setText('b').hide()
paths.c = new Path().move(points.left).line(points.top).setText('c')
return part
}
```
</Example>
```js
let { Point, points, Path, paths } = part.shorthand();
points.B = new Point(10, 50);
points.BCp2 = new Point(40, 10);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, 90);
paths.example = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.attr("class", "canvas")
.attr("data-text", "freesewingIsMadeByJoostDeCockAndContributors")
.attr("data-text-class", "text-xs center");
```

View file

@ -1,38 +1,51 @@
---
title: insop()
title: Path.insop()
---
The `Path.insop()` method injects a Path into the [`noop`
operation](/reference/api/path/noop) with id `id`.
## Signature
```js
Path path.insop(string id, Path path)
```
Injects a Path into the [`noop` operation](/reference/api/path/noop) with id `id`.
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
<Tip compact>This is often used to insert darts into a path</Tip>
<Example part="path_noop">
Example of the Path.noop() method
<Example caption="Example of the Path.insop() method">
```js
({ Point, points, Path, paths, part }) => {
points.left = new Point(10,10)
points.dartLeft = new Point(40, 10)
points.dartTip = new Point(50, 50)
points.dartRight = new Point(60, 10)
points.right = new Point(90, 10)
paths.withoutDart = new Path()
.move(points.left)
.line(points.dartLeft)
.noop('dart')
.line(points.right)
paths.withDart = paths.withoutDart
.clone()
.insop(
'dart',
new Path()
.line(points.dartTip)
.line(points.dartRight)
)
.attr('style', 'stroke-width: 2px; stroke-opacity: 0.5; stroke: orange;')
return part
}
```
</Example>
```js
points.left = new Point(10,10)
points.dartLeft = new Point(40, 10)
points.dartTip = new Point(50, 50)
points.dartRight = new Point(60, 10)
points.right = new Point(90, 10)
## Notes
paths.withoutDart = new Path()
.move(points.left)
.line(points.dartLeft)
.noop('dart')
.line(points.right)
This is often used to insert darts into a path.
paths.withDart = paths.without
.insop(
'dart',
new Path()
.line(points.dartTip)
.line(points.dartRight)
)
.attr('style', 'stroke-width: 2px; stroke-opacity: 0.5; stroke: orange;')
```

View file

@ -1,59 +1,61 @@
---
title: intersects()
title: Path.intersects()
---
```
array|false path.intersects(Path path)
```
The `Path.intersects()` method returns the Point object(s) where the path
intersects with a path you pass it.
Returns the Point object(s) where the path intersects with a path you pass it.
## Signature
<Tip>
```
array|false path.intersects(Path path)
```
###### Use the intersection methods in Utils whenever possible
<Example caption="Example of the Path.intersects() method">
```js
({ Point, points, Path, paths, snippets, Snippet, getId, part }) => {
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
points.D = new Point(50, 130);
points.DCp1 = new Point(150, 30);
points._A = new Point(55, 40);
points._B = new Point(0, 55);
points._BCp2 = new Point(40, -20);
points._C = new Point(90, 40);
points._CCp1 = new Point(50, -30);
points._D = new Point(40, 120);
points._DCp1 = new Point(180, 40);
paths.demo1 = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.curve(points.DCp1, points.DCp1, points.D);
paths.demo2 = new Path()
.move(points._A)
.line(points._B)
.curve(points._BCp2, points._CCp1, points._C)
.curve(points._DCp1, points._DCp1, points._D);
for (let p of paths.demo1.intersects(paths.demo2)) {
snippets[getId()] = new Snippet('notch', p)
}
return part
}
```
</Example>
## Notes
This is an expensive (read: slow) method that you should only use when you don't know
in advance in what segment of your path the intersection will occur.
If you do know, use one of the intersection methods in [Utils](/reference/api/utils).
</Tip>
<Example part="path_intersects">
Example of the Path.intersects() method
</Example>
```js
let { Point, points, Path, paths, Snippet, snippets } = part.shorthand();
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
points.D = new Point(50, 130);
points.DCp1 = new Point(150, 30);
points._A = new Point(55, 40);
points._B = new Point(0, 55);
points._BCp2 = new Point(40, -20);
points._C = new Point(90, 40);
points._CCp1 = new Point(50, -30);
points._D = new Point(40, 120);
points._DCp1 = new Point(180, 40);
paths.demo1 = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.curve(points.DCp1, points.DCp1, points.D);
paths.demo2 = new Path()
.move(points._A)
.line(points._B)
.curve(points._BCp2, points._CCp1, points._C)
.curve(points._DCp1, points._DCp1, points._D);
for (let p of paths.demo1.intersects(paths.demo2)) {
snippets[part.getId()] = new Snippet("notch", p);
}
```

View file

@ -1,43 +1,50 @@
---
title: intersectsX()
title: Path.intersectsX()
---
The `Path.intersectsX()` method returns the Point object(s) where the path
intersects with a given X-value.
## Signature
```js
array|false path.intersectsX(float x)
```
Returns the Point object(s) where the path intersects with a given X-value.
<Example part="path_intersectsx">
Example of the Path.intersectsX() method
</Example>
## Example
<Example caption="Example of the Path.intersectsX() method">
```js
let { Point, points, Path, paths, Snippet, snippets } = part.shorthand();
({ Point, points, Path, paths, snippets, Snippet, getId, part }) => {
points.A = new Point(95, 50);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
points.D = new Point(50, 130);
points.DCp1 = new Point(150, 30);
points.A = new Point(95, 50);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
points.D = new Point(50, 130);
points.DCp1 = new Point(150, 30);
points.top = new Point(60, -10);
points.bot = new Point(60, 140);
paths.line = new Path()
.move(points.top)
.line(points.bot)
.attr("class", "lining dashed");
paths.demo = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.curve(points.DCp1, points.DCp1, points.D);
for (let p of paths.demo.intersectsX(60)) {
snippets[getId()] = new Snippet("notch", p);
}
points.top = new Point(60, -10);
points.bot = new Point(60, 140);
paths.line = new Path()
.move(points.top)
.line(points.bot)
.attr("class", "lining dashed");
paths.demo = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.curve(points.DCp1, points.DCp1, points.D);
for (let p of paths.demo.intersectsX(60)) {
snippets[part.getId()] = new Snippet("notch", p);
return part
}
```
</Example>

View file

@ -1,43 +1,50 @@
---
title: intersectsY()
title: Path.intersectsY()
---
The `Path.intersectsY()` method returns the Point object(s) where the path
intersects with a given Y-value.
## Signature
```js
array|false path.intersectsY(float y)
```
Returns the Point object(s) where the path intersects with a given Y-value.
<Example part="path_intersectsy">
Example of the Path.intersectsY() method
</Example>
## Example
<Example caption="Example of the Path.intersectsY() method">
```js
let { Point, points, Path, paths, Snippet, snippets } = part.shorthand();
({ Point, points, Path, paths, snippets, Snippet, getId, part }) => {
points.A = new Point(55, 40);
points.B = new Point(10, 70);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 60);
points.CCp1 = new Point(50, -30);
points.D = new Point(50, 80);
points.DCp1 = new Point(140, 50);
points.A = new Point(55, 40);
points.B = new Point(10, 70);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 60);
points.CCp1 = new Point(50, -30);
points.D = new Point(50, 80);
points.DCp1 = new Point(140, 50);
points.top = new Point(10, 58);
points.bot = new Point(130, 58);
points.top = new Point(10, 58);
points.bot = new Point(130, 58);
paths.line = new Path()
.move(points.top)
.line(points.bot)
.attr("class", "lining dashed");
paths.line = new Path()
.move(points.top)
.line(points.bot)
.attr("class", "lining dashed");
paths.demo = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.curve(points.DCp1, points.DCp1, points.D);
paths.demo = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.curve(points.DCp1, points.DCp1, points.D);
for (let p of paths.demo.intersectsY(58)) {
snippets[part.getId()] = new Snippet("notch", p);
for (let p of paths.demo.intersectsY(58)) {
snippets[getId()] = new Snippet("notch", p);
}
return part
}
```
</Example>

View file

@ -1,44 +1,49 @@
---
title: join()
title: Path.join()
---
The `Path.join()` method joins this path with another path.
## Signature
```js
Path path.join(path other)
```
Joins this path with another path.
## Examples
<Warning>
<Example caption="Example of the Path.join() method">
```js
({ Point, points, Path, paths, part }) => {
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
paths.path1 = new Path()
.move(points.A)
.line(points.B)
.setClass("various")
paths.path2 = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.setClass("note")
paths.joint = paths.path1
.join(paths.path2)
.setClass("lining dotted")
return part
}
```
</Example>
## Notes
You cannot join a closed path to another path
</Warning>
<Example part="path_join">
Example of the Path.join() method
</Example>
```js
let { Point, points, Path, paths } = part.shorthand();
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
paths.path1 = new Path()
.move(points.A)
.line(points.B)
.attr("class", "various");
paths.path2 = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.attr("class", "canvas");
paths.joint = paths.path1
.join(paths.path2)
.attr("class", "note lashed stroke-l")
.attr("style", "stoke-opacity: 0.5");
```

View file

@ -1,43 +1,49 @@
---
title: length()
title: Path.length()
---
The `Path.length()` method returns the length of the path.
## Signature
```js
float path.length()
```
Returns the length of the path.
## Example
<Example part="path_length">
Example of the Path.length() method
<Example caption="Example of the Path.length() method">
```js
({ Point, points, Path, paths, macro, part }) => {
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C);
macro("pd", {
path: paths.example,
d: -20
});
macro("pd", {
path: new Path().move(points.B).line(points.A),
d: 10
});
macro("pd", {
path: new Path().move(points.B).curve(points.BCp2, points.CCp1, points.C),
d: -10
});
return part
}
```
</Example>
```js
let { Point, points, Path, paths, macro } = part.shorthand();
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C);
macro("pd", {
path: paths.example,
d: -20
});
macro("pd", {
path: new Path().move(points.B).line(points.A),
d: 10
});
macro("pd", {
path: new Path().move(points.B).curve(points.BCp2, points.CCp1, points.C),
d: -10
});
```

View file

@ -1,26 +1,34 @@
---
title: line()
title: Path.line()
---
The `Path.line()` method draws a straight line from the current position to a
given point.
## Signature
```js
Path path.line(Point to)
```
Draws a straight line from the current position to a given point.
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
<Example part="path_line">
Example of the Path.line() method
## Example
<Example caption="Example of the Path.line() method">
```js
({ Point, points, Path, paths, part }) => {
points.from = new Point(10, 10)
points.to = new Point(90, 10)
paths.line = new Path()
.move(points.from)
.line(points.to)
.setText("Path.line()", "text-sm center fill-note")
return part
}
```
</Example>
```js
let { Point, points, Path, paths } = part.shorthand();
points.from = new Point(10, 10);
points.to = new Point(90, 10);
paths.line = new Path()
.move(points.from)
.line(points.to)
.attr("data-text", "Path.line()")
.attr("data-text-class", "text-sm center fill-note");
```

View file

@ -1,18 +1,36 @@
---
title: move()
title: Path.move()
---
The `Path.move()` method moves to a given point without drawing a line.
## Signature
```js
Path path.move(Point to)
```
Moves to a given point without drawing a line.
## Example
<Tip>
###### Always start your path with a move
<Example caption="Example of the Path.move() method">
```js
({ Point, points, Path, paths, part }) => {
When drawing a path, you must always start with a `move()` call,
points.to = new Point(50, 20)
.setText("Path.move()", "text-xs fill-note center")
paths.noline = new Path().move(points.to)
return part
}
```
</Example>
## Notes
When drawing a path, **you must always start with a `move()` call**,
followed by your `line()` and/or `curve()` calls
and an optional `close()` call.
@ -23,21 +41,5 @@ paths.example = new Path()
.move(points.a)
.curve(points.b, points.c, points.d)
.line(points.e)
.close();
```
</Tip>
<Example part="path_move">
Example of the Path.move() method
</Example>
```js
let { Point, points, Path, paths } = part.shorthand();
points.to = new Point(50, 20)
.attr("data-text", "Path.move()")
.attr("data-text-class", "text-xs fill-note");
paths.noline = new Path().move(points.to);
.close()
```

View file

@ -1,41 +1,49 @@
---
title: noop()
title: Path.noop()
---
The `Path.noop()` method adds a placeholder path operation.
A `noop` operation does nothing, but is intended to be replaced later
with [`Path.insop()`](/reference/api/path/insop).
## Signature
```js
Path path.noop(string id)
```
Adds a placeholder path operation.
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
A `noop` operation does nothing, but is intended to be replaced later
with [`Path.insop()`](/reference/api/path/insop).
## Example
<Tip compact>This is often used to insert darts into a path</Tip>
<Example caption="Example of the Path.noop() method">
```js
({ Point, points, Path, paths, part }) => {
<Example part="path_noop">
Example of the Path.noop() method
points.left = new Point(10,10)
points.dartLeft = new Point(40, 10)
points.dartTip = new Point(50, 50)
points.dartRight = new Point(60, 10)
points.right = new Point(90, 10)
paths.withoutDart = new Path()
.move(points.left)
.line(points.dartLeft)
.noop('dart')
.line(points.right)
paths.withDart = paths.withoutDart
.clone()
.insop(
'dart',
new Path()
.line(points.dartTip)
.line(points.dartRight)
)
.attr('style', 'stroke-width: 2px; stroke-opacity: 0.5; stroke: orange;')
return part
}
```
</Example>
```js
points.left = new Point(10,10)
points.dartLeft = new Point(40, 10)
points.dartTip = new Point(50, 50)
points.dartRight = new Point(60, 10)
points.right = new Point(90, 10)
paths.withoutDart = new Path()
.move(points.left)
.line(points.dartLeft)
.noop('dart')
.line(points.right)
paths.withDart = paths.without
.insop(
'dart',
new Path()
.line(points.dartTip)
.line(points.dartRight)
)
.attr('style', 'stroke-width: 2px; stroke-opacity: 0.5; stroke: orange;')
```

View file

@ -1,46 +1,53 @@
---
title: offset()
title: Path.offset()
---
The `Path.offset()` method returns a new Path that is offset by distance from
the original path.
## Signature
```js
Path path.offset(float distance)
```
Returns a new Path that is offset by distance from the original path.
## Example
<Example part="path_offset">
Example of the Path.offset() method
<Example caption="Example of the Path.offset() method">
```js
({ Point, points, Path, paths, part }) => {
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.line(points.A)
.close();
paths.offset = paths.example
.offset(-10)
.attr("class", "interfacing");
paths.lineOffset = new Path()
.move(points.A)
.line(points.B)
.offset(-5)
.attr("class", "various");
paths.curveOffset = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.offset(-5)
.attr("class", "canvas");
return part
}
```
</Example>
```js
let { Point, points, Path, paths } = part.shorthand();
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.line(points.A)
.close();
paths.offset = paths.example
.offset(-10)
.attr("class", "interfacing");
paths.lineOffset = new Path()
.move(points.A)
.line(points.B)
.offset(-5)
.attr("class", "various");
paths.curveOffset = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.offset(-5)
.attr("class", "canvas");
```

View file

@ -1,42 +1,49 @@
---
title: reverse()
title: Path.reverse()
---
The `Path.reverse()` returns a path that is the reversed version of this path.
As in, start becomes end, and end becomes start.
## Signature
```js
Path path.reverse()
Path path.reverse(bool cloneAttributes=false)
```
Returns a path that is the reversed version of this path. As in, start becomes end, and end becomes start.
If you pass a truthy value to this method, it will return a deep clone of the
path, including its attributes. By default, it will return a shallow
copy, whithout the attributes.
<Note>
## Example
<Example caption="Example of the Path.reverse() method">
```js
({ Point, points, Path, paths, part }) => {
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
paths.example = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.setText("FreeSewing rocks", "text-xs fill-note center")
paths.reverse = paths.example.reverse(true)
return part
}
```
</Example>
## Notes
The reversed path is a shallow copy.
It will in other words not inherit the attributes of the original path.
If you want a deep copy, including the attributes, use `Path.clone().reverse()`.
If you want a deep copy, including the attributes, use `Path.reverse(true)`.
</Note>
<Example part="path_reverse">
Example of the Path.reverse() method
</Example>
```js
let { Point, points, Path, paths } = part.shorthand();
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
paths.example = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.attr("data-text", "freesewingIsMadeByJoostDeCockAndContributors")
.attr("data-text-class", "text-xs fill-note");
paths.reverse = paths.example
.reverse()
.attr("data-text", "freesewingIsMadeByJoostDeCockAndContributors")
.attr("data-text-class", "text-xs fill-lining");
```

View file

@ -1,37 +1,50 @@
---
title: roughLength()
title: Path.roughLength()
---
The `Path.roughLength()` method returns a (very) rough estimate of the path's length.
## Signature
```js
Path path.attr(
string name,
mixed value,
bool overwrite = false
)
Number path.roughLength()
```
This `Path.attr()` method calls `this.attributes.add()` under the hood, but returns the Path object.
## Example
This allows you to chain different calls together as in the example below.
<Example caption="Example of the Path.attr() method">
```js
({ Point, points, Path, paths, macro, units, part }) => {
If the third parameter is set to `true` it will call `this.attributes.set()` instead, thereby overwriting the value of the attribute.
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(120, 30);
points.CCp1 = new Point(50, -30);
paths.example = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C);
macro("pd", {
path: paths.example,
d: -10,
text: `Path.roughLength() = ${units(paths.example.roughLength())}`
})
macro("pd", {
path: paths.example,
d: 10,
text: `Path.length() = ${units(paths.example.length())}`
});
<Example part="path_attr">
Example of the Path.attr() method
return part
}
```
</Example>
```js
let { Point, points, Path, paths } = part.shorthand();
## Notes
points.B = new Point(10, 50);
points.BCp2 = new Point(40, 10);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, 90);
The `Path.roughLength()` is not intended to give an estimate that is accurate, but rather differentiatate between paths that are a few millimeter long, or meters long.
paths.example = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.attr("class", "canvas")
.attr("data-text", "freesewingIsMadeByJoostDeCockAndContributors")
.attr("data-text-class", "text-xs center");
```
It calculates the length without *walking the (cubic) Bezier curve* making it very fast and very inaccurate (for curves).
It is typically used to determine how much precision to apply when walking a curve.

View file

@ -1,37 +1,43 @@
---
title: setClass()
title: Path.setClass()
---
The `Path.setClass()` method sets the CSS class(es) of the path.
## Signature
```js
Path path.attr(
string name,
mixed value,
bool overwrite = false
)
Path path.setClass(string className)
```
This `Path.attr()` method calls `this.attributes.add()` under the hood, but returns the Path object.
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
This allows you to chain different calls together as in the example below.
## Example
If the third parameter is set to `true` it will call `this.attributes.set()` instead, thereby overwriting the value of the attribute.
<Example caption="Example of the Path.setClass() method">
```js
({ Point, points, Path, paths, part }) => {
<Example part="path_attr">
Example of the Path.attr() method
points.from = new Point(5, 10)
points.to = new Point(95, 10)
paths.line = new Path()
.move(points.from)
.line(points.to)
.setClass('note dashed')
return part
}
```
</Example>
## Notes
The main purpose of this method is to save your some typing,
as the two following calls yield the same result:
```js
let { Point, points, Path, paths } = part.shorthand();
points.B = new Point(10, 50);
points.BCp2 = new Point(40, 10);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, 90);
paths.example = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.attr("class", "canvas")
.attr("data-text", "freesewingIsMadeByJoostDeCockAndContributors")
.attr("data-text-class", "text-xs center");
path.attr('class', 'fabric', true)
path.addClass('fabric')
```

View file

@ -1,37 +1,34 @@
---
title: setHidden()
title: Path.setHidden()
---
The `Path.setHidden()` method either hides or unhides the path depending on the
value you pass it.
## Signature
```js
Path path.attr(
string name,
mixed value,
bool overwrite = false
)
Path path.setHidden(bool hidden = false)
```
This `Path.attr()` method calls `this.attributes.add()` under the hood, but returns the Path object.
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
This allows you to chain different calls together as in the example below.
## Example
If the third parameter is set to `true` it will call `this.attributes.set()` instead, thereby overwriting the value of the attribute.
<Example caption="Example of the Path.setHidden() method">
```js
({ Point, points, Path, paths, part }) => {
<Example part="path_attr">
Example of the Path.attr() method
points.top = new Point(50, 0)
points.left = new Point (20,50)
points.right = new Point (80,50)
paths.a = new Path().move(points.top).line(points.right).setText('a')
paths.b = new Path().move(points.right).line(points.left).setText('b').setHidden(true)
paths.c = new Path().move(points.left).line(points.top).setText('c')
return part
}
```
</Example>
```js
let { Point, points, Path, paths } = part.shorthand();
points.B = new Point(10, 50);
points.BCp2 = new Point(40, 10);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, 90);
paths.example = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.attr("class", "canvas")
.attr("data-text", "freesewingIsMadeByJoostDeCockAndContributors")
.attr("data-text-class", "text-xs center");
```

View file

@ -1,12 +0,0 @@
---
title: setRender()
---
```js
Path path.setRender(bool render)
```
This is a chainable method to sets the `path.render` property.
If you set it to `false` your path will not be rendered.
<Fixme>Add example</Fixme>

View file

@ -0,0 +1,48 @@
---
title: Path.setText()
---
The `Path.addText()` method set text on the path.
## Signature
```js
Path path.setText(string text, string className)
```
The second argument will optionally be used to set the CSS class for the text.
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
## Example
<Example caption="Example of the Path.setText() method">
```js
({ Point, points, Path, paths, part }) => {
points.from = new Point(5, 10)
points.to = new Point(95, 10)
paths.line = new Path()
.move(points.from)
.line(points.to)
.addText('FreeSewing rocks')
return part
}
```
</Example>
## Notes
The main purpose of this method is to save your some typing,
as the two following calls yield the same result:
```js
path.attr('data-text', 'Hello')
path.setText('Hello')
```
The difference with [Path.addText()](/reference/api/path/addtext) is that this
method will overwrite existing text on the path, whereas `Path.addText()` will
add to the existing text.

View file

@ -1,54 +1,55 @@
---
title: shiftAlong()
title: Path.shiftAlong()
---
```js
Point path.shiftAlong(float distance[, int stepsPerMm=25])
```
The `Path.shiftAlong()` method returns a point that lies at distance travelled
along the path.
Returns a point that lies at distance travelled along the path.
<Example part="path_shiftalong">
Example of the Path.shiftAlong() method
</Example>
## Signature
```js
let { Point, points, Path, paths, Snippet, snippets } = part.shorthand();
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C);
points.x1 = paths.example
.shiftAlong(20)
.attr("data-text", "2cm")
.attr("data-text-class", "center fill-note")
.attr("data-text-lineheight", 6);
points.x2 = paths.example
.shiftAlong(90)
.attr("data-text", "9cm")
.attr("data-text-class", "center fill-note")
.attr("data-text-lineheight", 6);
snippets.x1 = new Snippet("notch", points.x1);
snippets.x2 = new Snippet("notch", points.x2);
Point path.shiftAlong(float distance[, int stepsPerMm=10])
```
<Note>
##### The second parameter is optional
The second parameter controls the precision by which the path will be _walked_.
By default, we'll divide it into 25 steps per mm.
By default, we'll divide it into 10 steps per mm.
If you don't need that precision, you can pass a lower number.
But for most cases, you can just ignore it.
If you need more precision, you can pass a higher number.
For most cases, the default will be fine.
</Note>
## Example
<Example caption="Example of the Path.shiftAlong() method">
```js
({ Point, points, Path, paths, Snippet, snippets, part }) => {
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C);
points.x1 = paths.example
.shiftAlong(20)
.attr("data-text", "2cm")
.attr("data-text-class", "center fill-note")
.attr("data-text-lineheight", 6);
points.x2 = paths.example
.shiftAlong(90)
.attr("data-text", "9cm")
.attr("data-text-class", "center fill-note")
.attr("data-text-lineheight", 6);
snippets.x1 = new Snippet("notch", points.x1);
snippets.x2 = new Snippet("notch", points.x2);
return part
}
```
</Example>

View file

@ -1,54 +1,52 @@
---
title: shiftFractionAlong()
title: Path.shiftFractionAlong()
---
The `Path.shiftFractionAlong()` returns a point that lies at fraction of the
length of the path travelled along the path.
## Signature
```js
Point path.shiftFractionAlong(float fraction[, int stepsPerMm=25])
```
Returns a point that lies at fraction of the length of the path travelled along the path.
<Example part="path_shiftfractionalong">
Example of the Path.shiftFractionAlong() method
</Example>
```js
let { Point, points, Path, paths, Snippet, snippets } = part.shorthand();
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C);
points.x1 = paths.example
.shiftFractionAlong(0.2)
.attr("data-text", "msg_20")
.attr("data-text-class", "center")
.attr("data-text-lineheight", 6);
points.x2 = paths.example
.shiftFractionAlong(0.9)
.attr("data-text", "msg_90")
.attr("data-text-class", "center")
.attr("data-text-lineheight", 6);
snippets.xl = new Snippet("notch", points.x1);
snippets.x2 = new Snippet("notch", points.x2);
```
<Note>
##### The second parameter is optional
The second parameter controls the precision by which the path will be _walked_.
By default, we'll divide it into 25 steps per mm.
By default, we'll divide it into 10 steps per mm.
If you don't need that precision, you can pass a lower number.
But for most cases, you can just ignore it.
If you need more precision, you can pass a higher number.
For most cases, the default will be fine.
## Example
<Example caption="Example of the Path.shiftFractionAlong() method">
```js
({ Point, points, Path, paths, Snippet, snippets, part }) => {
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C);
points.x1 = paths.example
.shiftFractionAlong(0.2)
.setText("0.2", "center")
points.x2 = paths.example
.shiftFractionAlong(0.9)
.setText("0.9", "center")
snippets.xl = new Snippet("notch", points.x1);
snippets.x2 = new Snippet("notch", points.x2);
return part
}
```
</Example>
</Note>

View file

@ -1,37 +1,41 @@
---
title: smurve()
title: Path.smurve()
---
The `Path.smurve()` method draws a smooth curve from the current point via a control point to an endpoint.
A smooth curve means it will use the reflection of the end control point of the previous curve.
## Signature
```js
Path path.attr(
string name,
mixed value,
bool overwrite = false
)
Path path.smurve(Point cp2, Point end)
```
This `Path.attr()` method calls `this.attributes.add()` under the hood, but returns the Path object.
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
This allows you to chain different calls together as in the example below.
## Example
If the third parameter is set to `true` it will call `this.attributes.set()` instead, thereby overwriting the value of the attribute.
<Example caption="Example of the Path.smurve() method">
```js
({ Point, points, Path, paths, part }) => {
<Example part="path_attr">
Example of the Path.attr() method
points.aFrom = new Point(10, 10);
points.aCp1 = new Point(40, 40);
points.aCp2 = new Point(70, -20);
points.aTo = new Point(100, 10);
points.bCp2 = new Point(50,50)
points.bTo = new Point(10,50)
paths.smurve = new Path()
.move(points.aFrom)
.curve(points.aCp1, points.aCp2,points.aTo)
.smurve(points.bCp2, points.bTo)
.reverse() // Puts text at the end
.setText('Path.smurve()')
return part
}
```
</Example>
```js
let { Point, points, Path, paths } = part.shorthand();
points.B = new Point(10, 50);
points.BCp2 = new Point(40, 10);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, 90);
paths.example = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.attr("class", "canvas")
.attr("data-text", "freesewingIsMadeByJoostDeCockAndContributors")
.attr("data-text-class", "text-xs center");
```

View file

@ -1,37 +1,42 @@
---
title: smurve_()
title: Path.smurve_()
---
The `Path.smurve_()` method draws a smooth curve from the current point an endpoint.
In addition, the end point's control point lies on top of the end point.
A smooth curve means it will use the reflection of the end control point of the previous curve.
## Signature
```js
Path path.attr(
string name,
mixed value,
bool overwrite = false
)
Path path.smurve_(Point cp2, Point end)
```
This `Path.attr()` method calls `this.attributes.add()` under the hood, but returns the Path object.
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
This allows you to chain different calls together as in the example below.
## Example
If the third parameter is set to `true` it will call `this.attributes.set()` instead, thereby overwriting the value of the attribute.
<Example caption="Example of the Path.smurve_() method">
```js
({ Point, points, Path, paths, part }) => {
<Example part="path_attr">
Example of the Path.attr() method
points.aFrom = new Point(10, 10);
points.aCp1 = new Point(40, 40);
points.aCp2 = new Point(70, -20);
points.aTo = new Point(100, 10);
points.bTo = new Point(10,50)
paths.smurve = new Path()
.move(points.aFrom)
.curve(points.aCp1, points.aCp2,points.aTo)
.smurve_(points.bTo)
.reverse() // Puts text at the end
.setText('Path.smurve()')
return part
}
```
</Example>
```js
let { Point, points, Path, paths } = part.shorthand();
points.B = new Point(10, 50);
points.BCp2 = new Point(40, 10);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, 90);
paths.example = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.attr("class", "canvas")
.attr("data-text", "freesewingIsMadeByJoostDeCockAndContributors")
.attr("data-text-class", "text-xs center");
```

View file

@ -1,42 +1,49 @@
---
title: split
title: Path.split()
---
The `Path.split()` method splits a path in two halves, on a point along that
path that you pass it.
## Signature
```js
array path.split(Point splitPoint)
```
Splits a path in two halves, on a point along that path that you pass it.
<Example part="path_split">
Example of the Path.split() method
</Example>
## Example
<Example caption="Example of the Path.split() method">
```js
let { Point, points, Path, paths, Snippet, snippets } = part.shorthand();
({ Point, points, Path, paths, snippets, Snippet, part }) => {
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
points.D = new Point(50, 130);
points.DCp1 = new Point(150, 30);
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
points.D = new Point(50, 130);
points.DCp1 = new Point(150, 30);
paths.demo = new Path()
.move(points.D)
.curve(points.DCp1, points.DCp1, points.C)
.curve(points.CCp1, points.BCp2, points.B)
.line(points.A);
points.split = paths.demo.shiftFractionAlong(0.75);
snippets.split = new Snippet("notch", points.split);
let style = "stroke-width: 3; stroke-opacity: 0.5;";
let halves = paths.demo.split(points.split);
for (let i in halves) {
paths[i] = halves[i]
.attr("style", style)
.attr("style", `stroke: hsl(${i * 70}, 100%, 50%)`);
}
paths.demo = new Path()
.move(points.D)
.curve(points.DCp1, points.DCp1, points.C)
.curve(points.CCp1, points.BCp2, points.B)
.line(points.A);
points.split = paths.demo.shiftFractionAlong(0.75);
snippets.split = new Snippet("notch", points.split);
let style = "stroke-width: 3; stroke-opacity: 0.5;";
let halves = paths.demo.split(points.split);
for (let i in halves) {
paths[i] = halves[i]
.attr("style", style)
.attr("style", `stroke: hsl(${i * 70}, 100%, 50%)`);
return part
}
```
</Example>

View file

@ -1,30 +1,38 @@
---
title: start()
title: Path.start()
---
The `Path.start()` method returns the Point object at the start of the path.
## Signature
```js
Point path.start()
```
Returns the Point object at the start of the path.
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
<Example part="path_start">
Example of the Path.start() method
## Example
<Example caption="Example of the Path.start() method">
```js
({ Point, points, Path, paths, snippets, Snippet, part }) => {
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
paths.demo = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
snippets.end = new Snippet("notch", paths.demo.start())
return part
}
```
</Example>
```js
let { Point, points, Path, paths, Snippet, snippets } = part.shorthand();
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
paths.example = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C);
snippets.start = new Snippet("notch", paths.example.start());
```

View file

@ -1,45 +1,51 @@
---
title: translate()
title: Path.translate()
---
The `Path.translate()` method returns a path with
[a translate transform](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform#Translate)
applied.
## Example
```js
Path path.translate(float deltaX, float deltaY)
```
Returns a path with
[a translate transform](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform#Translate)
applied.
## Example
<Example part="path_translate">
Example of the Path.translate() method
<Example caption="Example of the Path.translate() method">
```js
({ Point, points, Path, paths, part, macro }) => {
points.A = new Point(45, 60)
points.B = new Point(10, 30)
points.BCp2 = new Point(40, 20)
points.C = new Point(90, 30)
points.CCp1 = new Point(50, -30)
paths.A = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C)
paths.B = paths.A.translate(60, 30)
points.step1 = points.B.shift(0, 60)
points.step2 = points.step1.shift(-90, 30)
macro("ld", {
from: points.B,
to: points.step1,
noStartMarker: true
})
macro("ld", {
from: points.step1,
to: points.step2,
noStartMarker: true
})
return part
}
```
</Example>
```js
let { Point, points, Path, paths, macro } = part.shorthand();
points.A = new Point(45, 60);
points.B = new Point(10, 30);
points.BCp2 = new Point(40, 20);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, -30);
paths.A = new Path()
.move(points.A)
.line(points.B)
.curve(points.BCp2, points.CCp1, points.C);
paths.B = paths.A.translate(60, 30);
points.step1 = points.B.shift(0, 60);
points.step2 = points.step1.shift(-90, 30);
macro("ld", {
from: points.B,
to: points.step1,
noStartMarker: true
});
macro("ld", {
from: points.step1,
to: points.step2,
noStartMarker: true
});
```

View file

@ -1,18 +1,68 @@
---
title: trim()
title: Path.trim()
---
The `Path.trim()` method Returns a new Path that is this path with overlapping
parts removed.
```js
Path path.trim()
```
Returns a new Path that is this path with overlapping parts removed.
<Example caption="Example of the Path.trim() method">
```js
({ Point, points, Path, paths, part }) => {
points.center = new Point(0, 0)
points.base = new Point(0, 10)
points.tip = new Point(0, 50)
points.tipCpRight = new Point(30, 50)
points.tipCpLeft = new Point(-30, 50)
paths.example = new Path().move(points.base)
for (let i = 0; i < 4; i++) {
points["base" + i] = points.base.rotate(60 * i, points.center)
points["tip" + i] = points.tip.rotate(60 * i, points.center)
points["tipCpRight" + i] = points.tipCpRight.rotate(60 * i, points.center)
points["tipCpLeft" + i] = points.tipCpLeft.rotate(60 * i, points.center)
if (i < 2) {
paths.example
.line(points["base" + i])
.curve(points["base" + i], points["tipCpLeft" + i], points["tip" + i])
.curve(
points["tipCpRight" + i],
points["base" + i],
points["base" + i]
);
} else {
paths.example
.line(points["base" + i])
.line(points["tip" + i])
.line(points["tipCpRight" + i])
.line(points["base" + i])
}
}
paths.offset = paths.example
.offset(10)
.setClass("lining dotted stroke-sm")
paths.trimmed = paths.offset
.trim()
.setClass("various stroke-xl")
.attr("style", "stroke-opacity: 0.5;")
return part
}
```
</Example>
## Notes
This method is typically used when [Path.offset()](/reference/api/path/offset) caused some overlap.
<Warning>
###### Use sparsely or performance will suffer
However, use this sparsely or performance will suffer.
This method is recursive and complex, and the performance penalty for using
it on a long/complex path will be significant.
@ -22,54 +72,3 @@ To limit the impact of path.trim(), follow this approach:
- construct a minimal path that contains the overlap
- trim it
- now join it to the rest of your path
You can see an example of this
[in the front part of the Bruce pattern](https://github.com/freesewing/freesewing/blob/3ca5d0edfe54c7ac20aaf3af2f3544aee72f9b99/designs/bruce/src/front.js#L158).
</Warning>
<Example part="path_trim">
Example of the Path.trim() method
</Example>
```js
let { Point, points, Path, paths } = part.shorthand();
points.center = new Point(0, 0);
points.base = new Point(0, 10);
points.tip = new Point(0, 50);
points.tipCpRight = new Point(30, 50);
points.tipCpLeft = new Point(-30, 50);
paths.example = new Path().move(points.base);
for (let i = 0; i < 4; i++) {
points["base" + i] = points.base.rotate(60 * i, points.center);
points["tip" + i] = points.tip.rotate(60 * i, points.center);
points["tipCpRight" + i] = points.tipCpRight.rotate(60 * i, points.center);
points["tipCpLeft" + i] = points.tipCpLeft.rotate(60 * i, points.center);
if (i < 2) {
paths.example
.line(points["base" + i])
.curve(points["base" + i], points["tipCpLeft" + i], points["tip" + i])
.curve(
points["tipCpRight" + i],
points["base" + i],
points["base" + i]
);
} else {
paths.example
.line(points["base" + i])
.line(points["tip" + i])
.line(points["tipCpRight" + i])
.line(points["base" + i]);
}
}
paths.offset = paths.example
.offset(10)
.attr("class", "lining dotted stroke-sm");
paths.trimmed = paths.offset
.trim()
.attr("class", "various stroke-xl")
.attr("style", "stroke-opacity: 0.5;");
```

View file

@ -1,37 +1,35 @@
---
title: unhide()
title: Path.unhide()
---
The `Path.unhide()` method unhides the path so it does appears in the output.
By default, paths are not hidden. So you should only call this on path previously hidden via `Path.hide()`.
## Signature
```js
Path path.attr(
string name,
mixed value,
bool overwrite = false
)
Path path.unhide()
```
This `Path.attr()` method calls `this.attributes.add()` under the hood, but returns the Path object.
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
This allows you to chain different calls together as in the example below.
## Example
If the third parameter is set to `true` it will call `this.attributes.set()` instead, thereby overwriting the value of the attribute.
<Example caption="Example of the Path.unhide() method">
```js
({ Point, points, Path, paths, part }) => {
<Example part="path_attr">
Example of the Path.attr() method
points.top = new Point(50, 0)
points.left = new Point (20,50)
points.right = new Point (80,50)
paths.a = new Path().move(points.top).line(points.right).setText('a')
paths.b = new Path().move(points.right).line(points.left).setText('b').hide().unhide()
paths.c = new Path().move(points.left).line(points.top).setText('c')
return part
}
```
</Example>
```js
let { Point, points, Path, paths } = part.shorthand();
points.B = new Point(10, 50);
points.BCp2 = new Point(40, 10);
points.C = new Point(90, 30);
points.CCp1 = new Point(50, 90);
paths.example = new Path()
.move(points.B)
.curve(points.BCp2, points.CCp1, points.C)
.attr("class", "canvas")
.attr("data-text", "freesewingIsMadeByJoostDeCockAndContributors")
.attr("data-text-class", "text-xs center");
```

View file

@ -2,11 +2,11 @@
title: Point.addCircle()
---
Adds a circle to a Point. Under the hood, this will call `Point.attr()` as circles
are added by setting attributes. Refer to [Drawing circles](/howtos/code/drawing-circles) for
more details.
The `Point.addCircle()` method adds a circle to a Point. Under the hood, this
will call `Point.attr()` as circles are added by setting attributes. Refer to
[Drawing circles](/howtos/code/drawing-circles) for more details.
## Point.addCircle() signature
## Signature
```js
Point point.addCircle(
@ -15,12 +15,9 @@ Point point.addCircle(
)
```
## Point.addCircle() example
<Example part="point_addcircle">
Examples of Point.addCircle(), compare this to [Point.setCircle](/reference/api/point/setcircle)
</Example>
## Example
<Example caption="Examples of Point.addCircle(), compare this to [Point.setCircle](/reference/api/point/setcircle)">
```js
({ Point, points, part }) => {
points.a = new Point(30, 10)
@ -41,4 +38,6 @@ Examples of Point.addCircle(), compare this to [Point.setCircle](/reference/api/
return part
}
```
</Example>

View file

@ -2,11 +2,11 @@
title: Point.addText()
---
Adds text on a Point. Under the hood, this will call `Point.attr()` as text
is added by setting attributes. Refer to [Adding text](/howtos/code/adding-text) for
more details.
The `Point.addText()` method adds text on a Point. Under the hood, this will
call `Point.attr()` as text is added by setting attributes. Refer to [Adding
text](/howtos/code/adding-text) for more details.
## Point.addText() signature
## Signature
```js
Point point.addText(
@ -15,21 +15,28 @@ Point point.addText(
)
```
## Point.addText() example
<Example part="point_addtext">
Examples of Point.addText(), compare this to [Point.setText](/reference/api/point/settext)
</Example>
## Example
<Example caption="Examples of Point.addText(), compare this to [Point.setText](/reference/api/point/settext)">
```js
({ Point, points, part }) => {
({ Point, points, Path, paths, part }) => {
points.anchor = new Point(100, 25)
.addText('supportFreesewingBecomeAPatron', 'center')
.addText('please?')
.addText('FreeSewing')
.addText('rocks')
// Avoid the text getting cropped
paths.hidden = new Path()
.move(points.anchor)
.line(points.anchor.shift(0, 80))
.addClass('hidden')
return part
}
```
</Example>
<Tip compact>Remember to [use translation keys, not text](/guides/best-practices/use-translation-keys)</Tip>
## Notes
Remember to [use translation keys, not text](/guides/best-practices/use-translation-keys)

View file

@ -2,33 +2,36 @@
title: Point.angle()
---
A point's `angle()` method returns the angle (in degrees) between this point and
the point passed into the method. An angle of 0° points to the right, and the angle increases counterclockwise.
The `Point.angle()` method returns the angle (in degrees) between this point
and the point passed into the method. An angle of 0° points to the right, and
the angle increases counterclockwise.
## Point.angle() signature
## Signature
```js
float point.angle(Point pointB)
```
## Point.angle() Example
## Example
<Example part="point_angle">
An example of the Point.angle() method
<Example caption="An example of the Point.angle() method">
```js
({ Point, points, Path, paths, part }) => {
points.sun = new Point(10, 5);
points.moon = points.sun.shift(-15, 70);
points.text = points.sun
.shiftFractionTowards(points.moon, 0.8)
.attr("data-text", points.sun.angle(points.moon)+"°")
.attr("data-text-class", "text-sm fill-note center");
paths.line = new Path()
.move(points.sun)
.line(points.moon)
.attr("class", "dashed");
return part
}
```
</Example>
```js
let { Point, points, Path, paths } = part.shorthand();
points.sun = new Point(10, 5);
points.moon = points.sun.shift(-15, 70);
points.text = points.sun
.shiftFractionTowards(points.moon, 0.8)
.attr("data-text", points.sun.angle(points.moon)+"°")
.attr("data-text-class", "text-sm fill-note center");
paths.line = new Path()
.move(points.sun)
.line(points.moon)
.attr("class", "dashed");
```

View file

@ -2,10 +2,11 @@
title: Point.attr()
---
Adds an attribute to the point, and returns the original point. Setting the third parameter
to `true` will replace the value of the attribute instead of adding it.
The `Point.attr()` method adds an attribute to the point, and returns the
original point. Setting the third parameter to `true` will replace the value of
the attribute instead of adding it.
## Point.attr() signature
## Signature
```js
Point point.attr(
@ -15,21 +16,29 @@ Point point.attr(
)
```
The `Point.attr()` method calls [`this.attributes.add()`](/reference/api/attributes/add/) under the hood, but returns the `Point` object.
This allows you to chain different calls together as in the example below.
If the third parameter is set to `true` it will call [`this.attributes.set()`](/reference/api/attributes/set/) instead, thereby overwriting the value of the attribute.
## Point.attr() example
<Example part="point_attr">
An example of the Point.attr() method
<Tip compact>This method is chainable as it returns the `Path` object</Tip>
## Example
<Example caption="An example of the Point.attr() method">
```js
({ Point, points, Path, paths, part }) => {
points.anchor = new Point(100, 25)
.attr('data-text', 'FreeSewing')
.attr('data-text', 'rocks')
// Avoid the text getting cropped
paths.hidden = new Path()
.move(points.anchor)
.line(points.anchor.shift(0, 80))
.addClass('hidden')
return part
}
```
</Example>
```js
let { Point, points } = part.shorthand();
points.anchor = new Point(100, 25)
.attr("data-text", "freesewingIsMadeByJoostDeCockAndContributors")
.attr("data-text-class", "center");
```

View file

@ -2,37 +2,45 @@
title: Point.clone()
---
Returns a new `Point` with the same coordinates and attributes as the original point.
The `Point.clone()` method returns a new `Point` with the same coordinates and
attributes as the original point.
## Point.clone() signature
## Signature
```js
Point point.clone()
```
<Note>
<Tip compact>This method is chainable as it returns the `Point` object</Tip>
###### Copy vs clone
The [`Point.copy()`](/reference/api/point/copy/) method will only copy the point's coordinates, whereas this
`Point.clone()` method will also copy its attributes.
</Note>
## Point.clone() example
<Example part="point_clone">
An example of the Point.clone() method
</Example>
## Example
<Example caption="An example of the Point.clone() method">
```js
let { Point, points, Snippet, snippets } = part.shorthand();
({ Point, points, Path, paths, Snippet, snippets, part }) => {
points.A = new Point(25, 25)
.attr("data-text", "Point A")
.attr("data-text-class", "text-xl")
.attr("data-text-fill-opacity", "0.5");
points.B = points.A.clone().attr("data-text", "Point B");
.setText("Point A", "text-xl")
.attr("data-text-fill-opacity", "0.5")
points.B = points.A.clone().setText("Point B")
snippets.x = new Snippet("notch", points.A);
snippets.x = new Snippet("notch", points.A)
// Avoid the text getting cropped
paths.hidden = new Path()
.move(new Point(20,10))
.move(new Point(75,30))
.addClass('hidden')
return part
}
```
</Example>
## Notes
The [`Point.copy()`](/reference/api/point/copy/) method will only copy the
point's coordinates, whereas this `Point.clone()` method will also copy its
attributes.

View file

@ -2,37 +2,43 @@
title: Point.copy()
---
A point's `copy()` method returns a new point with the same coordinates as the original point.
This method does _not_ copy any attributes the original point may have.
The `Point.copy()` method returns a new point with the same coordinates as the
original point. This method does _not_ copy any attributes the original point
may have.
## Point.copy() signature
## Signature
```js
Point point.copy()
```
<Note>
###### Copy vs clone
this `Point.copy()` method will only copy the point's coordinates.
To also copy the attributes, use [`Point.clone()`](/reference/api/point/clone/) instead.
</Note>
## Point.copy() example
<Example part="point_copy">
An example of the Point.copy() method
<Example caption="An example of the Point.copy() method">
```js
({ Point, points, Path, paths, Snippet, snippets, part }) => {
points.A = new Point(25, 25)
.setText("Point A", "text-xl")
.attr("data-text-fill-opacity", "0.5")
points.B = points.A.clone().setText("Point B")
snippets.x = new Snippet("notch", points.A)
// Avoid the text getting cropped
paths.hidden = new Path()
.move(new Point(20,10))
.move(new Point(75,30))
.addClass('hidden')
return part
}
```
</Example>
```js
let { Point, points, Snippet, snippets } = part.shorthand();
points.A = new Point(50, 25)
.attr("data-text", "Point A")
.attr("data-text-class", "text-xl");
points.B = points.A.copy().attr("data-text", "Point B");
## Notes
snippets.x = new Snippet("notch", points.A);
```
The `Point.copy()` method will only copy the point's coordinates.
To also copy the attributes, use [`Point.clone()`](/reference/api/point/clone/) instead.

View file

@ -2,35 +2,35 @@
title: Point.dist()
---
A point's `dist()` method returns the distance (in mm) between this point and the point you pass it.
The `Point.dist()` method returns the distance (in mm) between this point and
the point you pass it.
## Point.dist() signature
## Signature
```js
float point.dist(Point point)
```
## Point.dist() example
## Example
<Example part="point_dist">
An example of the Point.dist() method
<Example caption="An example of the Point.dist() method">
```js
({ Point, points, Path, paths, units, part }) => {
points.from = new Point(10, 10)
points.to = new Point(80, 70)
points.text = points.from
.shiftFractionTowards(points.to, 0.6)
.setText(units(points.from.dist(points.to)), 'text-sm fill-note center')
paths.line = new Path()
.move(points.from)
.line(points.to)
.attr('class', 'dashed')
return part
}
```
</Example>
```js
let { Point, points, Path, paths } = part.shorthand()
points.from = new Point(10, 10)
points.to = new Point(80, 70)
points.text = points.from
.shiftFractionTowards(points.to, 0.6)
.attr('data-text', points.from.dist(points.to) + 'mm')
.attr('data-text-class', 'text-sm fill-note center')
paths.line = new Path()
.move(points.from)
.line(points.to)
.attr('class', 'dashed')
return part
```

View file

@ -67,6 +67,20 @@ Path.prototype.addClass = function (className = false) {
return this
}
/**
* A chainable way to add text to a Path
*
* @param {string} text - The text to add to the Path
* @param {string} className - The CSS classes to apply to the text
* @return {Path} this - The Path instance
*/
Path.prototype.addText = function (text = '', className = false) {
this.attributes.add('data-text', text)
if (className) this.attributes.add('data-text-class', className)
return this
}
/**
* Returns the SVG pathstring for this path
*
@ -536,7 +550,7 @@ Path.prototype.offset = function (distance) {
*
* @return {object} reverse - A Path instance that is the reversed version of this Path
*/
Path.prototype.reverse = function () {
Path.prototype.reverse = function (cloneAttributes = false) {
let sections = []
let current
let closed = false
@ -555,6 +569,7 @@ Path.prototype.reverse = function () {
let rev = new Path().__withLog(this.log).move(current)
for (let section of sections.reverse()) rev.ops.push(section.ops[1])
if (closed) rev.close()
if (cloneAttributes) rev.attributes = this.attributes.clone()
return rev
}
@ -613,6 +628,19 @@ Path.prototype.setHidden = function (hidden = false) {
return this
}
/**
* A chainable way to set text on a Path
*
* @param {string} text - The text to add to the Path
* @param {string} className - The CSS classes to apply to the text
* @return {Path} this - The Path instance
*/
Path.prototype.setText = function (text = '', className = false) {
this.attributes.set('data-text', text)
if (className) this.attributes.set('data-text-class', className)
return this
}
/**
* Returns a point that lies at distance along this Path
*

View file

@ -149,8 +149,9 @@ Pattern.prototype.draft = function () {
this.setStores[set].log.error(
`Unable to draft pattern part __${partName}__. Part.draft() is not callable`
)
this.parts[set][partName].hidden =
this.parts[set][partName].hidden === true ? true : !this.__wants(partName, set)
// FIXME: THis won't work not that this is immutable
// But is it still needed?
// this.parts[set][partName].hidden === true ? true : !this.__wants(partName, set)
} else {
this.setStores[set].log.debug(
`Part \`${partName}\` is not needed. Skipping draft and setting hidden to \`true\``
@ -180,6 +181,7 @@ Pattern.prototype.getConfig = function () {
* @return {object} this - The Pattern instance
*/
Pattern.prototype.getRenderProps = function () {
this.store.log.info('Gathering render props')
// Run pre-render hook
let svg = new Svg(this)
svg.hooks = this.hooks
@ -193,15 +195,6 @@ Pattern.prototype.getRenderProps = function () {
props.height = this.height
props.autoLayout = this.autoLayout
props.settings = this.settings
props.logs = {
pattern: this.store.logs,
sets: this.setStores.map((store) => ({
debug: store.logs.debug,
info: store.logs.info,
error: store.logs.error,
warning: store.logs.warning,
})),
}
props.parts = []
for (const set of this.parts) {
const setParts = {}
@ -211,6 +204,10 @@ Pattern.prototype.getRenderProps = function () {
...set[p].asProps(),
store: this.setStores[set[p].set],
}
} else if (this.setStores[set?.set]) {
this.setStores[set.set].log.info(
`Part${p} is hidden in set ${set.set}. Not adding to render props`
)
}
}
props.parts.push(setParts)
@ -219,7 +216,16 @@ Pattern.prototype.getRenderProps = function () {
for (let s in this.stacks) {
if (!this.__isStackHidden(s)) {
props.stacks[s] = this.stacks[s].asProps()
}
} else this.store.log.info(`Stack ${s} is hidden. Skipping in render props.`)
}
props.logs = {
pattern: this.store.logs,
sets: this.setStores.map((store) => ({
debug: store.logs.debug,
info: store.logs.info,
error: store.logs.error,
warning: store.logs.warning,
})),
}
return props
@ -500,40 +506,48 @@ function getPluginName(plugin) {
* @return {Pattern} this - The Pattern instance
*/
Pattern.prototype.__addPartPlugins = function (part) {
if (!part.plugins) return this
if (!this.config.plugins) this.config.plugins = {}
const plugins = { ...this.config.plugins }
if (!part.plugins) return this
// Side-step immutability of the part object to ensure plugins is an array
let partPlugins = part.plugins
if (!Array.isArray(partPlugins)) partPlugins = [partPlugins]
for (const plugin of partPlugins) plugins[getPluginName(plugin)] = plugin
// Go through list of part plugins
for (let plugin of partPlugins) {
const name = getPluginName(plugin)
this.store.log.debug(
plugin.plugin
? `🔌 Resolved __${name}__ conditional plugin in \`${part.name}\``
: `🔌 Resolved __${name}__ plugin in \`${part.name}\``
)
// Handle [plugin, data] scenario
if (Array.isArray(plugin)) {
const pluginObj = { ...plugin[0], data: plugin[1] }
plugin = pluginObj
}
if (plugin.plugin)
this.store.log.debug(`🔌 Resolved __${name}__ conditional plugin in \`${part.name}\``)
else this.store.log.debug(`🔌 Resolved __${name}__ plugin in \`${part.name}\``)
// Do not overwrite an existing plugin with a conditional plugin unless it is also conditional
if (plugin.plugin && plugin.condition) {
if (!plugins[name]) {
plugins[name] = plugin
this.store.log.info(`Plugin \`${name}\` was conditionally added.`)
} else if (plugins[name]?.condition) {
plugins[name + '_'] = plugin
this.store.log.info(
`Plugin \`${name}\` was conditionally added again. Renaming to ${name}_.`
)
} else
this.store.log.info(
`Plugin \`${name}\` was requested conditionally, but is already added explicitly. Not loading.`
)
} else {
if (!plugins[name]) {
// New plugin, so we load it
plugins[name] = plugin
this.store.log.info(`Plugin \`${name}\` was added.`)
this.store.log.info(
plugin.condition
? `New plugin conditionally added: \`${name}\``
: `New plugin added: \`${name}\``
)
} else {
// Existing plugin, takes some more work
if (plugin.plugin && plugin.condition) {
// Multiple instances of the same plugin with different conditions
// will all be added, so we need to change the name.
if (plugins[name]?.condition) {
plugins[name + '_'] = plugin
this.store.log.info(
`Plugin \`${name}\` was conditionally added again. Renaming to ${name}_.`
)
} else
this.store.log.info(
`Plugin \`${name}\` was requested conditionally, but is already added explicitly. Not loading.`
)
}
}
}
@ -697,6 +711,7 @@ Pattern.prototype.__isPartHidden = function (partName) {
if (this.__designParts?.[partName]?.hideAll) return true
if (this.__mutated.partHide?.[partName]) return true
if (this.__mutated.partHideAll?.[partName]) return true
if (this.parts?.[this.activeSet]?.[partName]?.hidden) return true
return false
}
@ -722,6 +737,7 @@ Pattern.prototype.__isStackHidden = function (stackName) {
if (this.__designParts?.[partName]?.hideAll) return true
if (this.__mutated.partHide?.[partName]) return true
if (this.__mutated.partHideAll?.[partName]) return true
if (this.parts?.[this.activeSet]?.[partName]?.hidden) return true
}
return false
@ -1230,7 +1246,7 @@ Pattern.prototype.__resolveParts = function (count = 0, distance = 0) {
if (part.hideAll) this.__mutated.partHide[part.name] = true
// Inject (from)
if (part.from) {
if (part.hideDependencies || part.hideAll) {
if (part.hideDependencies || this.__mutated.partHideAll[name]) {
// Don't mutate the part, keep this info in the pattern object
this.__mutated.partHide[part.from.name] = true
this.__mutated.partHideAll[part.from.name] = true
@ -1249,9 +1265,6 @@ Pattern.prototype.__resolveParts = function (count = 0, distance = 0) {
this.__addDependency(name, part, dep)
}
} else {
if (part.hideDependencies) {
this.__mutated.partHide[part.after.name] = true
}
this.__mutated.partDistance[part.after.name] = distance
this.__designParts[part.after.name] = part.after
this.__addDependency(name, part, part.after)

View file

@ -50,6 +50,7 @@ Point.prototype.addText = function (text = '', className = false) {
return this.__check()
}
/**
* Returns the angle between this Point and that Point
*
@ -202,7 +203,7 @@ Point.prototype.setCircle = function (radius = false, className = false) {
}
/**
* A chainable way to add text to a Point
* A chainable way to set text on a Point
*
* @param {string} text - The text to add to the Point
* @param {string} className - The CSS classes to apply to the text

View file

@ -702,6 +702,9 @@ describe('Pattern', () => {
it('Should check whether created parts get the pattern context', () => {
let partContext
const plugin = {
name: 'example',
}
const part = {
name: 'test',
draft: ({ Point, paths, Path, part, context }) => {
@ -710,6 +713,7 @@ describe('Pattern', () => {
return part
},
plugins: [plugin],
}
const Pattern = new Design({ parts: [part], data: { name: 'test', version: '1' } })
const pattern = new Pattern()

View file

@ -4,12 +4,12 @@ export const plugin = {
name,
version,
hooks: {
preDraft: ({ settings }) => {
for (const set of settings) {
if (set.measurements) {
if (typeof set.measurements.bust === 'undefined') {
set.measurements.bust = set.measurements.chest
set.measurements.chest = set.measurements.highBust
preDraft: function ({ settings }) {
for (const i in settings) {
if (settings[i].measurements) {
if (typeof settings[i].measurements.bust === 'undefined') {
settings[i].measurements.bust = settings[i].measurements.chest
settings[i].measurements.chest = settings[i].measurements.highBust
}
}
}

View file

@ -4,55 +4,48 @@ export const plugin = {
name,
version,
macros: {
gore: function (so) {
gore: function (so, { points, paths, Path }) {
const from = so.from
const gores = so.gores
const radius = so.radius //radius of the sphere
const prefix = so.prefix
const extraLength = so.extraLength //the length of the straight section after a complete semisphere
this.points[prefix + 'p1'] = from.shift(0, (radius * Math.PI) / 2 + extraLength)
this.points[prefix + 'Cp1'] = this.points[prefix + 'p1'].shift(
points[prefix + 'p1'] = from.shift(0, (radius * Math.PI) / 2 + extraLength)
points[prefix + 'Cp1'] = points[prefix + 'p1'].shift(
180 - 180 / gores,
radius / 2 / Math.cos(Math.PI / gores)
)
this.points[prefix + 'p3'] = from.shift(90, (radius * Math.PI) / gores)
this.points[prefix + 'p2'] = this.points[prefix + 'p3'].shift(0, extraLength)
this.points[prefix + 'Cp2'] = this.points[prefix + 'p2'].shift(
0,
(radius * (Math.PI - 2)) / 2
)
points[prefix + 'p3'] = from.shift(90, (radius * Math.PI) / gores)
points[prefix + 'p2'] = points[prefix + 'p3'].shift(0, extraLength)
points[prefix + 'Cp2'] = points[prefix + 'p2'].shift(0, (radius * (Math.PI - 2)) / 2)
if (extraLength < 0) {
//top curve used to calculate the new points if extraLength < 0
this.paths.auxiliaryPath = new this.Path()
.move(this.points[prefix + 'p1'])
.curve(
this.points[prefix + 'Cp1'],
this.points[prefix + 'Cp2'],
this.points[prefix + 'p2']
)
paths.auxiliaryPath = new Path()
.move(points[prefix + 'p1'])
.curve(points[prefix + 'Cp1'], points[prefix + 'Cp2'], points[prefix + 'p2'])
.hide()
this.points[prefix + 'p2'] = this.paths.auxiliaryPath.intersectsX(0)[0] //the new point p2 is the one in which the auxiliary curve intersects x=0
this.paths.auxiliaryPath = this.paths.auxiliaryPath.split(this.points[prefix + 'p2'])[0] //the auxiliary curve is split
this.points[prefix + 'Cp1'] = this.paths.auxiliaryPath.ops[1].cp1 //the new control points are those of the new curve
this.points[prefix + 'Cp2'] = this.paths.auxiliaryPath.ops[1].cp2
this.points[prefix + 'p3'] = this.points[prefix + 'p2'].clone()
points[prefix + 'p2'] = paths.auxiliaryPath.intersectsX(0)[0] //the new point p2 is the one in which the auxiliary curve intersects x=0
paths.auxiliaryPath = paths.auxiliaryPath.split(points[prefix + 'p2'])[0] //the auxiliary curve is split
points[prefix + 'Cp1'] = paths.auxiliaryPath.ops[1].cp1 //the new control points are those of the new curve
points[prefix + 'Cp2'] = paths.auxiliaryPath.ops[1].cp2
points[prefix + 'p3'] = points[prefix + 'p2'].clone()
}
//the seam path is generated
this.paths[prefix + 'seam'] = new this.Path()
paths[prefix + 'seam'] = new Path()
.move(from)
.line(this.points[prefix + 'p1'])
.curve(this.points[prefix + 'Cp1'], this.points[prefix + 'Cp2'], this.points[prefix + 'p2'])
.line(this.points[prefix + 'p3'])
.line(points[prefix + 'p1'])
.curve(points[prefix + 'Cp1'], points[prefix + 'Cp2'], points[prefix + 'p2'])
.line(points[prefix + 'p3'])
.line(from)
.close()
.attr('class', so.class ? so.class : '')
if (so?.hidden) this.paths[prefix + 'seam'].hide()
else this.paths[prefix + 'seam'].unhide()
if (so?.hidden) paths[prefix + 'seam'].hide()
else paths[prefix + 'seam'].unhide()
},
},
}

View file

@ -8,7 +8,7 @@ describe('Gore Plugin Tests', () => {
it('Should create a default gore', () => {
const part = {
name: 'test',
draft: ({ Point, points, macro }) => {
draft: ({ Point, points, macro, part }) => {
points.anchorPoint = new Point(50, 50)
macro('gore', {
from: points.anchorPoint,
@ -17,12 +17,15 @@ describe('Gore Plugin Tests', () => {
extraLength: 0,
prefix: 'gore',
})
return part
},
plugins: [plugin],
}
const Test = new Design({ plugins: [plugin], parts: [part] })
const Test = new Design({ parts: [part] })
const pattern = new Test()
pattern.draft()
let c = pattern.parts.test.points
let c = pattern.parts[0].test.points
expect(round(c.gorep1.y)).to.equal(50)
expect(round(c.gorep2.x)).to.equal(50)
expect(round(c.gorep2.y)).to.equal(30.37)
@ -33,7 +36,7 @@ describe('Gore Plugin Tests', () => {
it('Should use a configurable number of gores', () => {
const part = {
name: 'test',
draft: ({ Point, points, macro }) => {
draft: ({ Point, points, macro, part }) => {
points.anchorPoint = new Point(50, 50)
macro('gore', {
from: points.anchorPoint,
@ -42,12 +45,15 @@ describe('Gore Plugin Tests', () => {
extraLength: 0,
prefix: 'gore',
})
return part
},
plugins: [plugin],
}
const Test = new Design({ plugins: [plugin], parts: [part] })
const Test = new Design({ parts: [part] })
const pattern = new Test()
pattern.draft()
let c = pattern.parts.test.points
let c = pattern.parts[0].test.points
expect(round(c.gorep1.x)).to.equal(89.27)
expect(round(c.gorep1.y)).to.equal(50)
expect(round(c.gorep2.x)).to.equal(50)
@ -59,7 +65,7 @@ describe('Gore Plugin Tests', () => {
it('Should use a configurable extra length', () => {
const part = {
name: 'test',
draft: ({ Point, points, macro }) => {
draft: ({ Point, points, macro, part }) => {
points.anchorPoint = new Point(50, 50)
macro('gore', {
from: points.anchorPoint,
@ -68,12 +74,15 @@ describe('Gore Plugin Tests', () => {
extraLength: 20,
prefix: 'gore',
})
return part
},
plugins: [plugin],
}
const Test = new Design({ plugins: [plugin], parts: [part] })
const Test = new Design({ parts: [part] })
const pattern = new Test()
pattern.draft()
let c = pattern.parts.test.points
let c = pattern.parts[0].test.points
expect(round(c.gorep1.x)).to.equal(109.27)
expect(round(c.gorep1.y)).to.equal(50)
expect(round(c.gorep2.x)).to.equal(70)
@ -85,7 +94,7 @@ describe('Gore Plugin Tests', () => {
it('Should use a configurable radius', () => {
const part = {
name: 'test',
draft: ({ Point, points, macro }) => {
draft: ({ Point, points, macro, part }) => {
points.anchorPoint = new Point(50, 50)
macro('gore', {
from: points.anchorPoint,
@ -94,12 +103,15 @@ describe('Gore Plugin Tests', () => {
extraLength: 0,
prefix: 'gore',
})
return part
},
plugins: [plugin],
}
const Test = new Design({ plugins: [plugin], parts: [part] })
const Test = new Design({ parts: [part] })
const pattern = new Test()
pattern.draft()
let c = pattern.parts.test.points
let c = pattern.parts[0].test.points
expect(round(c.gorep1.x)).to.equal(97.12)
expect(round(c.gorep1.y)).to.equal(50)
expect(round(c.gorep2.x)).to.equal(50)
@ -111,7 +123,7 @@ describe('Gore Plugin Tests', () => {
it('Should generate a seam path', () => {
const part = {
name: 'test',
draft: ({ Point, points, macro }) => {
draft: ({ Point, points, macro, part }) => {
points.anchorPoint = new Point(50, 50)
macro('gore', {
from: points.anchorPoint,
@ -120,12 +132,15 @@ describe('Gore Plugin Tests', () => {
extraLength: 0,
prefix: 'gore',
})
return part
},
plugins: [plugin],
}
const Test = new Design({ plugins: [plugin], parts: [part] })
const Test = new Design({ parts: [part] })
const pattern = new Test()
pattern.draft()
let c = pattern.parts.test.paths.goreseam.ops
let c = pattern.parts[0].test.paths.goreseam.ops
expect(round(c[1].to.x)).to.equal(89.27)
expect(round(c[1].to.y)).to.equal(50)
expect(round(c[2].to.x)).to.equal(50)

View file

@ -19,19 +19,18 @@ export const plugin = {
},
},
macros: {
grainline: function (so = {}) {
grainline: function (so = {}, { points, paths, Path, complete, setGrain }) {
if (so === false) {
delete this.points.grainlineFrom
delete this.points.grainlineTo
delete this.paths.grainline
this.setGrain(90) // Restoring default
delete points.grainlineFrom
delete points.grainlineTo
delete paths.grainline
setGrain(90) // Restoring default
return true
}
so = {
...dflts,
...so,
}
const { points, complete, setGrain } = this.shorthand()
// setGrain relies on plugin-cutlist
if (typeof setGrain === 'function') {
setGrain(so.from.angle(so.to))
@ -39,7 +38,7 @@ export const plugin = {
if (complete) {
points.grainlineFrom = so.from.shiftFractionTowards(so.to, 0.05)
points.grainlineTo = so.to.shiftFractionTowards(so.from, 0.05)
this.paths.grainline = new this.Path()
paths.grainline = new Path()
.move(points.grainlineFrom)
.line(points.grainlineTo)
.attr('class', 'note')

View file

@ -8,19 +8,22 @@ describe('Grainline Plugin Tests', () => {
it('Should run the default grainline macro', () => {
const part = {
name: 'test',
draft: ({ points, Point, macro }) => {
draft: ({ points, Point, macro, part }) => {
points.from = new Point(10, 20)
points.to = new Point(10, 230)
macro('grainline', {
from: points.from,
to: points.to,
})
return part
},
plugins: [plugin],
}
const Pattern = new Design({ plugins: [plugin], parts: [part] })
const Pattern = new Design({ parts: [part] })
const pattern = new Pattern()
pattern.draft()
const c = pattern.parts.test.paths.grainline
const c = pattern.parts[0].test.paths.grainline
expect(c.attributes.get('class')).to.equal('note')
expect(c.attributes.get('marker-start')).to.equal('url(#grainlineFrom)')
expect(c.attributes.get('marker-end')).to.equal('url(#grainlineTo)')

View file

@ -5,31 +5,31 @@ export const plugin = {
version,
hooks: {
preDraft: function ({ settings }) {
if (settings.measurements) {
if (
typeof settings.measurements.seatBack !== 'undefined' &&
typeof settings.measurements.seat !== 'undefined'
) {
settings.measurements.seatFront =
settings.measurements.seat - settings.measurements.seatBack
settings.measurements.seatBackArc = settings.measurements.seatBack / 2
settings.measurements.seatFrontArc = settings.measurements.seatFront / 2
}
if (
typeof settings.measurements.waist !== 'undefined' &&
typeof settings.measurements.waistBack !== 'undefined'
) {
settings.measurements.waistFront =
settings.measurements.waist - settings.measurements.waistBack
settings.measurements.waistBackArc = settings.measurements.waistBack / 2
settings.measurements.waistFrontArc = settings.measurements.waistFront / 2
}
if (
typeof settings.measurements.crossSeam !== 'undefined' &&
typeof settings.measurements.crossSeamFront !== 'undefined'
) {
settings.measurements.crossSeamBack =
settings.measurements.crossSeam - settings.measurements.crossSeamFront
for (const set of settings) {
if (set.measurements) {
if (
typeof set.measurements.seatBack !== 'undefined' &&
typeof set.measurements.seat !== 'undefined'
) {
set.measurements.seatFront = set.measurements.seat - set.measurements.seatBack
set.measurements.seatBackArc = set.measurements.seatBack / 2
set.measurements.seatFrontArc = set.measurements.seatFront / 2
}
if (
typeof set.measurements.waist !== 'undefined' &&
typeof set.measurements.waistBack !== 'undefined'
) {
set.measurements.waistFront = set.measurements.waist - set.measurements.waistBack
set.measurements.waistBackArc = set.measurements.waistBack / 2
set.measurements.waistFrontArc = set.measurements.waistFront / 2
}
if (
typeof set.measurements.crossSeam !== 'undefined' &&
typeof set.measurements.crossSeamFront !== 'undefined'
) {
set.measurements.crossSeamBack =
set.measurements.crossSeam - set.measurements.crossSeamFront
}
}
}
},

View file

@ -12,62 +12,47 @@ const measurements = {
crossSeam: 100,
crossSeamFront: 42,
}
const part = {
name: 'test',
draft: ({ points, Point }) => {
draft: ({ points, Point, part }) => {
points.from = new Point(10, 20)
points.to = new Point(10, 230)
return part
},
measurements: Object.keys(measurements),
plugins: [plugin],
}
const Pattern = new Design({ plugins: [plugin], parts: [part] })
const pattern = new Pattern({ measurements })
pattern.draft()
const Pattern = new Design({ parts: [part] })
describe('Measurements Plugin Tests', () => {
it('Should set the extra measurements', () => {
expect(pattern.settings.measurements.seatFront).to.equal(40)
expect(pattern.settings.measurements.seatFrontArc).to.equal(20)
expect(pattern.settings.measurements.seatBackArc).to.equal(30)
expect(pattern.settings.measurements.waistFront).to.equal(55)
expect(pattern.settings.measurements.waistFrontArc).to.equal(27.5)
expect(pattern.settings.measurements.crossSeamBack).to.equal(58)
const pattern = new Pattern({ measurements })
pattern.draft()
expect(pattern.settings[0].measurements.seatFront).to.equal(40)
expect(pattern.settings[0].measurements.seatFrontArc).to.equal(20)
expect(pattern.settings[0].measurements.seatBackArc).to.equal(30)
expect(pattern.settings[0].measurements.waistFront).to.equal(55)
expect(pattern.settings[0].measurements.waistFrontArc).to.equal(27.5)
expect(pattern.settings[0].measurements.crossSeamBack).to.equal(58)
})
it('Should calculate seatFront from seat and seatBack', function () {
const testPattern = new Design({
measurements: {},
plugins: [plugin],
})
const pattern = new testPattern()
const userMeasurements = { seat: 50, seatBack: 20 }
pattern.settings.measurements = userMeasurements
const pattern = new Pattern({ measurements: { seat: 50, seatBack: 20 } })
pattern.draft()
expect(pattern.settings.measurements.seatFront).to.equal(30)
expect(pattern.settings[0].measurements.seatFront).to.equal(30)
})
it('Should calculate waistFrontArc and waistBackArc from waist and waistBack', function () {
const testPattern = new Design({
measurements: {},
plugins: [plugin],
})
const pattern = new testPattern()
const userMeasurements = { waist: 50, waistBack: 20 }
pattern.settings.measurements = userMeasurements
const pattern = new Pattern({ measurements: { waist: 50, waistBack: 20 } })
pattern.draft()
expect(pattern.settings.measurements.waistFrontArc).to.equal(15)
expect(pattern.settings.measurements.waistBackArc).to.equal(10)
expect(pattern.settings[0].measurements.waistFrontArc).to.equal(15)
expect(pattern.settings[0].measurements.waistBackArc).to.equal(10)
})
it('Should calculate crossSeamBack from crossSeam and crossSeamFront', function () {
const testPattern = new Design({
measurements: {},
plugins: [plugin],
})
const pattern = new testPattern()
const userMeasurements = { crossSeam: 50, crossSeamFront: 20 }
pattern.settings.measurements = userMeasurements
const pattern = new Pattern({ measurements: { crossSeam: 50, crossSeamFront: 20 } })
pattern.draft()
expect(pattern.settings.measurements.crossSeamBack).to.equal(30)
expect(pattern.settings[0].measurements.crossSeamBack).to.equal(30)
})
})

View file

@ -7,7 +7,7 @@ const expect = chai.expect
describe('Mirror Plugin Tests', () => {
const part = {
name: 'test',
draft: ({ points, Point, macro, paths, Path }) => {
draft: ({ points, Point, macro, paths, Path, part }) => {
points.mirrorA = new Point(-100, -100)
points.mirrorB = new Point(100, 100)
points.a = new Point(10, 20)
@ -23,58 +23,61 @@ describe('Mirror Plugin Tests', () => {
macro('mirror', settings)
macro('mirror', { ...settings, prefix: 'test' })
macro('mirror', { ...settings, clone: false })
return part
},
plugins: [plugin],
}
const Pattern = new Design({ plugins: [plugin], parts: [part] })
const Pattern = new Design({ parts: [part] })
const pattern = new Pattern()
pattern.draft()
it('Should mirror points', () => {
expect(pattern.parts.test.points.mirroredA.x).to.equal(20)
expect(pattern.parts.test.points.mirroredA.y).to.equal(10)
expect(pattern.parts.test.points.mirroredB.x).to.equal(40)
expect(pattern.parts.test.points.mirroredB.y).to.equal(30)
expect(pattern.parts[0].test.points.mirroredA.x).to.equal(20)
expect(pattern.parts[0].test.points.mirroredA.y).to.equal(10)
expect(pattern.parts[0].test.points.mirroredB.x).to.equal(40)
expect(pattern.parts[0].test.points.mirroredB.y).to.equal(30)
})
it('Should mirror points with custom prefix', () => {
expect(pattern.parts.test.points.testA.x).to.equal(20)
expect(pattern.parts.test.points.testA.y).to.equal(10)
expect(pattern.parts.test.points.testB.x).to.equal(40)
expect(pattern.parts.test.points.testB.y).to.equal(30)
expect(pattern.parts[0].test.points.testA.x).to.equal(20)
expect(pattern.parts[0].test.points.testA.y).to.equal(10)
expect(pattern.parts[0].test.points.testB.x).to.equal(40)
expect(pattern.parts[0].test.points.testB.y).to.equal(30)
})
it('Should mirror points without cloning them', () => {
expect(pattern.parts.test.points.a.x).to.equal(20)
expect(pattern.parts.test.points.a.y).to.equal(10)
expect(pattern.parts.test.points.b.x).to.equal(40)
expect(pattern.parts.test.points.b.y).to.equal(30)
expect(pattern.parts[0].test.points.a.x).to.equal(20)
expect(pattern.parts[0].test.points.a.y).to.equal(10)
expect(pattern.parts[0].test.points.b.x).to.equal(40)
expect(pattern.parts[0].test.points.b.y).to.equal(30)
})
it('Should mirror paths', () => {
expect(pattern.parts.test.paths.mirroredTest.ops[0].to.x).to.equal(2)
expect(pattern.parts.test.paths.mirroredTest.ops[0].to.y).to.equal(1)
expect(pattern.parts.test.paths.mirroredTest.ops[1].cp1.x).to.equal(20)
expect(pattern.parts.test.paths.mirroredTest.ops[1].cp1.y).to.equal(10)
expect(pattern.parts.test.paths.mirroredTest.ops[1].cp2.x).to.equal(40)
expect(pattern.parts.test.paths.mirroredTest.ops[1].cp2.y).to.equal(30)
expect(pattern.parts.test.paths.mirroredTest.ops[1].to.x).to.equal(60)
expect(pattern.parts.test.paths.mirroredTest.ops[1].to.y).to.equal(50)
expect(pattern.parts[0].test.paths.mirroredTest.ops[0].to.x).to.equal(2)
expect(pattern.parts[0].test.paths.mirroredTest.ops[0].to.y).to.equal(1)
expect(pattern.parts[0].test.paths.mirroredTest.ops[1].cp1.x).to.equal(20)
expect(pattern.parts[0].test.paths.mirroredTest.ops[1].cp1.y).to.equal(10)
expect(pattern.parts[0].test.paths.mirroredTest.ops[1].cp2.x).to.equal(40)
expect(pattern.parts[0].test.paths.mirroredTest.ops[1].cp2.y).to.equal(30)
expect(pattern.parts[0].test.paths.mirroredTest.ops[1].to.x).to.equal(60)
expect(pattern.parts[0].test.paths.mirroredTest.ops[1].to.y).to.equal(50)
})
it('Should mirror paths with custom prefix', () => {
expect(pattern.parts.test.paths.testTest.ops[0].to.x).to.equal(2)
expect(pattern.parts.test.paths.testTest.ops[0].to.y).to.equal(1)
expect(pattern.parts.test.paths.testTest.ops[1].cp1.x).to.equal(20)
expect(pattern.parts.test.paths.testTest.ops[1].cp1.y).to.equal(10)
expect(pattern.parts.test.paths.testTest.ops[1].cp2.x).to.equal(40)
expect(pattern.parts.test.paths.testTest.ops[1].cp2.y).to.equal(30)
expect(pattern.parts.test.paths.testTest.ops[1].to.x).to.equal(60)
expect(pattern.parts.test.paths.testTest.ops[1].to.y).to.equal(50)
expect(pattern.parts[0].test.paths.testTest.ops[0].to.x).to.equal(2)
expect(pattern.parts[0].test.paths.testTest.ops[0].to.y).to.equal(1)
expect(pattern.parts[0].test.paths.testTest.ops[1].cp1.x).to.equal(20)
expect(pattern.parts[0].test.paths.testTest.ops[1].cp1.y).to.equal(10)
expect(pattern.parts[0].test.paths.testTest.ops[1].cp2.x).to.equal(40)
expect(pattern.parts[0].test.paths.testTest.ops[1].cp2.y).to.equal(30)
expect(pattern.parts[0].test.paths.testTest.ops[1].to.x).to.equal(60)
expect(pattern.parts[0].test.paths.testTest.ops[1].to.y).to.equal(50)
})
it('Should mirror paths without cloning them', () => {
expect(pattern.parts.test.paths.test.ops[0].to.x).to.equal(2)
expect(pattern.parts.test.paths.test.ops[0].to.y).to.equal(1)
expect(pattern.parts.test.paths.test.ops[1].cp1.x).to.equal(20)
expect(pattern.parts.test.paths.test.ops[1].cp1.y).to.equal(10)
expect(pattern.parts.test.paths.test.ops[1].cp2.x).to.equal(40)
expect(pattern.parts.test.paths.test.ops[1].cp2.y).to.equal(30)
expect(pattern.parts.test.paths.test.ops[1].to.x).to.equal(60)
expect(pattern.parts.test.paths.test.ops[1].to.y).to.equal(50)
expect(pattern.parts[0].test.paths.test.ops[0].to.x).to.equal(2)
expect(pattern.parts[0].test.paths.test.ops[0].to.y).to.equal(1)
expect(pattern.parts[0].test.paths.test.ops[1].cp1.x).to.equal(20)
expect(pattern.parts[0].test.paths.test.ops[1].cp1.y).to.equal(10)
expect(pattern.parts[0].test.paths.test.ops[1].cp2.x).to.equal(40)
expect(pattern.parts[0].test.paths.test.ops[1].cp2.y).to.equal(30)
expect(pattern.parts[0].test.paths.test.ops[1].to.x).to.equal(60)
expect(pattern.parts[0].test.paths.test.ops[1].to.y).to.equal(50)
})
})

View file

@ -9,8 +9,9 @@ const part = {
draft: ({ Point, snippets, Snippet }) => {
snippets.button = new Snippet('notch', new Point(10, 20))
},
plugins: [plugin],
}
const Pattern = new Design({ plugins: [plugin], parts: [part] })
const Pattern = new Design({ parts: [part] })
const pattern = new Pattern()
pattern.draft().render()
@ -26,11 +27,14 @@ describe('Notches Plugin Test', () => {
it('Draws a notch on an anchor point', () => {
const part = {
name: 'test',
draft: ({ Point, snippets, Snippet }) => {
draft: ({ Point, snippets, Snippet, part }) => {
snippets.button = new Snippet('notch', new Point(10, 20))
return part
},
plugins: [plugin],
}
const Pattern = new Design({ plugins: [plugin], parts: [part] })
const Pattern = new Design({ parts: [part] })
const pattern = new Pattern()
pattern.draft().render()
const c = pattern.svg

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