1
0
Fork 0
freesewing/designs/sandy/src/skirt.mjs

299 lines
8.8 KiB
JavaScript
Raw Normal View History

2022-09-04 19:03:18 +02:00
import { pctBasedOn } from '@freesewing/core'
import { elastics } from '@freesewing/snapseries'
import { ringsectorPlugin } from '@freesewing/plugin-ringsector'
2019-07-12 10:04:42 +02:00
2022-09-11 16:39:52 +02:00
function sandySkirt({
utils,
store,
sa,
points,
Path,
paths,
Snippet,
snippets,
options,
measurements,
macro,
absoluteOptions,
part,
}) {
// Circumference of the top of the waistband, calculated from the waistbandPosition option
store.set(
2019-08-03 15:03:33 +02:00
'topCircumference',
options.waistbandPosition * measurements.hips +
(1 - options.waistbandPosition) * measurements.waist
2019-08-03 15:03:33 +02:00
)
// Circumference of the bottom of the waistband
2019-08-03 15:03:33 +02:00
if (options.waistbandShape === 'curved') {
// If the waistband is curved, the bottom circumference is calculated from the measurements
store.set(
2019-08-03 15:03:33 +02:00
'bottomCircumference',
store.get('topCircumference') +
(absoluteOptions.waistbandWidth * (measurements.hips - measurements.waist)) /
measurements.waistToHips
2019-08-03 15:03:33 +02:00
)
} else {
// If the waistband is straight, the bottom circumference is the same as the top circumference
2019-08-03 15:03:33 +02:00
store.set('bottomCircumference', store.get('topCircumference'))
}
2019-07-12 10:04:42 +02:00
// Overlap of the waistband
2019-08-03 15:03:33 +02:00
store.set('waistbandOverlap', store.get('topCircumference') * options.waistbandOverlap)
2019-07-12 10:04:42 +02:00
// The top circumference of the skirt corresponds to the bottom circumference of the waistband, plus the extraWaist option for gathering/pleating
2019-08-03 15:03:33 +02:00
store.set('skirtCircumference', store.get('bottomCircumference') * (1 + options.gathering))
// The length from the top of the skirt to the floor (max length available)
store.set(
2019-08-03 15:03:33 +02:00
'fullLength',
measurements.waistToFloor - measurements.waistToHips * options.waistbandPosition
2019-08-03 15:03:33 +02:00
)
2024-01-20 19:55:46 +01:00
let radiusWaist, angle
if (options.seamlessFullCircle) {
/**
* If the seamless full circle option is selected, the angle
* is 90º, and the radius of the waist arc is half than if
* it's not selected, because in this case the fabric is cut
* in a double fold
*/
2024-01-20 19:55:46 +01:00
angle = 90
radiusWaist = store.get('skirtCircumference') / utils.deg2rad(angle) / 4
} else {
/**
* If the seamless full circle option is not selected, the
* angle is calculated using the circlePercent option
*/
2024-01-20 19:55:46 +01:00
const totalAngle = 180 * options.circleRatio
angle = totalAngle / options.panels
radiusWaist = store.get('skirtCircumference') / utils.deg2rad(totalAngle) / 2
/**
* If the angle is too large, the seam allowance can fall out
* of the fold of the fabric, so we limit the angle to a
* maximum angle calculated so the seam allowance fits in the
* fabric
*/
2024-01-20 19:55:46 +01:00
if (angle > 90 && sa) {
const maxAngle = utils.rad2deg(Math.atan(radiusWaist / sa))
if (angle > 90 + maxAngle) angle = 90 + maxAngle
}
}
/**
* The radius of the hem arc is the radius of the waist
* arc with the length of the skirt added
*/
const radiusHem =
radiusWaist + store.get('fullLength') * options.lengthBonus - absoluteOptions.waistbandWidth
// Call the RingSector macro to draft the part
const ids = macro('ringsector', {
2024-01-20 19:55:46 +01:00
angle: angle,
insideRadius: radiusWaist,
outsideRadius: radiusHem,
rotate: true,
})
const pathId = ids.paths.path
paths.seam = paths[pathId].clone().addClass('fabric')
paths[pathId].hide()
/*
* Macros ensure they can be used more than once in a part, and will generate unique (and complex)
* point names. Since we're only calling the macro once here, we will simplify these names
*/
for (const [shortId, uid] of Object.entries(ids.points)) {
points[shortId] = points[uid].copy()
// Some points are rotated, we need those too
if (points[uid + 'Rotated']) points[shortId + 'Rotated'] = points[uid + 'Rotated'].copy()
}
// Anchor samples to the centre of the waist
2019-08-03 15:03:33 +02:00
points.gridAnchor = points.in2Flipped.clone()
2023-09-18 14:19:18 +02:00
if (sa) {
paths.hemBase = new Path()
2023-10-31 18:41:09 +00:00
.move(points.ex2Flipped)
.curve(points.ex2cFlipped, points.ex1cFlipped, points.ex1)
.curve(points.ex1c, points.ex2c, points.ex2)
.offset(store.get('fullLength') * options.lengthBonus * options.hemWidth)
2023-09-18 14:19:18 +02:00
paths.saBase = new Path()
2023-10-31 18:41:09 +00:00
.move(points.in2)
.curve(points.in2c, points.in1c, points.in1)
.curve(points.in1cFlipped, points.in2cFlipped, points.in2Flipped)
2023-10-31 18:41:09 +00:00
if (!options.seamlessFullCircle)
paths.saBase = new Path().move(points.ex2).line(points.ex2).join(paths.saBase)
paths.saBase = paths.saBase.offset(sa)
2023-09-18 14:19:18 +02:00
paths.hemBase.hide()
paths.saBase.hide()
2019-08-03 15:03:33 +02:00
2023-09-18 14:19:18 +02:00
if (options.seamlessFullCircle) {
paths.sa = new Path()
2023-10-31 18:41:09 +00:00
.move(points.ex2Flipped)
2023-09-18 14:19:18 +02:00
.line(paths.hemBase.start())
.join(paths.hemBase)
2023-10-31 18:41:09 +00:00
.line(points.ex2)
.move(points.in2)
.line(paths.saBase.start())
.join(paths.saBase)
.line(points.in2Flipped)
2023-09-18 14:19:18 +02:00
.attr('class', 'fabric sa')
} else {
paths.sa = new Path()
2023-10-31 18:41:09 +00:00
.move(points.ex2Flipped)
2023-09-18 14:19:18 +02:00
.line(paths.hemBase.start())
.join(paths.hemBase)
2023-10-31 18:41:09 +00:00
.line(paths.saBase.start())
.join(paths.saBase)
.line(points.in2Flipped)
2023-09-18 14:19:18 +02:00
.attr('class', 'fabric sa')
}
}
2023-09-18 14:19:18 +02:00
/*
* Annotations
*/
// Cutlist
2024-01-21 13:43:45 +01:00
store.cutlist.setCut({
cut: options.seamlessFullCircle ? 1 : Number(options.panels),
from: 'fabric',
onFold: true,
identical: true,
})
2023-09-18 14:19:18 +02:00
// Cutonfold
macro('cutonfold', {
from: points.in2Flipped,
to: points.ex2Flipped,
grainline: true,
})
if (options.seamlessFullCircle) {
macro('cutonfold', {
from: points.ex2,
to: points.in2,
2023-09-18 14:19:18 +02:00
id: 'double',
reverse: true,
2023-09-18 14:19:18 +02:00
})
}
// Logo
2023-10-31 18:41:09 +00:00
points.logo = points.in2FlippedRotated.shiftFractionTowards(
points.ex2FlippedRotated,
options.seamlessFullCircle ? 0.3 : 0.1
)
2023-09-18 14:19:18 +02:00
snippets.logo = new Snippet('logo', points.logo)
// Title
2023-10-31 18:41:09 +00:00
points.title = points.in2FlippedRotated.shiftFractionTowards(
points.ex2FlippedRotated,
options.seamlessFullCircle ? 0.5 : 0.25
)
2023-09-18 14:19:18 +02:00
macro('title', { at: points.title, nr: 1, title: 'skirt' })
// Scalebox
2023-10-31 18:41:09 +00:00
points.scalebox = points.in2FlippedRotated.shiftFractionTowards(
points.ex2FlippedRotated,
options.seamlessFullCircle ? 0.7 : 0.45
)
2023-09-18 14:19:18 +02:00
macro('scalebox', { at: points.scalebox })
// Notches
macro('sprinkle', {
snippet: 'notch',
on: ['in2', 'gridAnchor'],
2023-09-18 14:19:18 +02:00
})
snippets.center = new Snippet('bnotch', points.center)
// Dimensions
macro('vd', {
id: 'hLeft',
from: points.ex2Flipped,
to: points.in2Flipped,
x: points.ex2Flipped.x - sa - 15,
})
macro('vd', {
id: 'hToOpeningLeft',
from: points.in2Flipped,
to: points.center,
x: points.ex2Flipped.x - sa - 15,
})
macro('vd', {
id: 'hFull',
from: points.ex2Flipped,
to: points.center,
x: points.ex2Flipped.x - sa - 30,
})
2024-01-20 19:55:46 +01:00
if (angle !== 90) {
2019-08-03 15:03:33 +02:00
macro('vd', {
2023-09-18 14:19:18 +02:00
id: 'hTopToOpeningRight',
from: points.ex2,
to: points.in2,
x: angle > 90 ? points.in2.x - sa - 15 : points.ex2.x + sa + 15,
2019-08-03 15:03:33 +02:00
})
macro('vd', {
2023-09-18 14:19:18 +02:00
id: 'hOpeningRightToCenter',
from: points.in2,
2019-07-13 08:22:35 +02:00
to: points.center,
x: angle > 90 ? points.in2.x - sa - 15 : points.ex2.x + sa + 15,
2019-08-03 15:03:33 +02:00
})
macro('vd', {
2023-09-18 14:19:18 +02:00
id: 'hHemRightToCenter',
from: points.ex2,
2019-07-13 08:22:35 +02:00
to: points.center,
x: angle > 90 ? points.in2.x - sa - 30 : points.ex2.x + sa + 30,
})
macro('hd', {
id: 'wHemToOpeningRight',
from: points.ex2,
to: points.in2,
y: angle < 90 ? points.center.y - sa - 15 : points.ex2.y - sa - 15,
})
macro('hd', {
id: 'wOpeningRightToCenter',
from: points.center,
to: points.in2,
y: angle < 90 ? points.center.y - sa - 15 : points.ex2.y - sa - 15,
})
macro('hd', {
id: 'wHemToCenter',
from: points.center,
to: points.ex2,
y: angle < 90 ? points.center.y - sa - 30 : points.ex2.y - sa - 30,
2019-08-03 15:03:33 +02:00
})
}
2019-08-03 15:03:33 +02:00
return part
}
2022-09-04 19:03:18 +02:00
export const skirt = {
name: 'sandy.skirt',
measurements: ['waist', 'waistToFloor', 'waistToHips', 'hips'],
options: {
minimumOverlap: 15, // Lower than this and we don't draw a button
2022-09-05 20:41:19 -07:00
seamlessFullCircle: { bool: false, menu: 'construction' },
2022-09-11 16:39:52 +02:00
waistbandWidth: {
pct: 4,
min: 1,
max: 8,
snap: elastics,
...pctBasedOn('waistToFloor'),
menu: 'style',
},
2022-09-05 20:41:19 -07:00
waistbandPosition: { pct: 50, min: 0, max: 100, menu: 'fit' },
lengthBonus: { pct: 50, min: 10, max: 100, menu: 'style' },
circleRatio: { pct: 50, min: 20, max: 100, menu: 'style' },
waistbandOverlap: { pct: 3, min: 0, max: 15, menu: 'style' },
gathering: { pct: 0, min: 0, max: 200, menu: 'style' },
hemWidth: { pct: 2, min: 1, max: 10, menu: 'construction' },
2022-09-04 19:03:18 +02:00
waistbandShape: {
list: ['straight', 'curved'],
dflt: 'straight',
2022-09-05 20:41:19 -07:00
menu: 'fit',
2022-09-04 19:03:18 +02:00
},
2024-01-20 19:55:46 +01:00
panels: { count: 1, min: 1, max: 8, menu: 'construction' },
2022-09-04 19:03:18 +02:00
},
plugins: ringsectorPlugin,
2022-09-04 19:03:18 +02:00
draft: sandySkirt,
}