wip(core/brian/aaron): Support for 2022 style part inheritance
This is very rough around the edges, but it's kinda working, so I'm committing this now. What this enabled is the ability to extend a part by importing only that part and then just saying you want a part `from` the imported one. The imported part comes with all options, it does not currently come with all measurements. This also *follows* dependencies. For example in Brian, we never explicitly add the base and sleevecap parts, they are simply added automatically because other parts are buily *from* them. Best to look at the source code of designs/brian and designs/aaron to understand what's going on and how it is different.
This commit is contained in:
parent
ac9b616b99
commit
70bd946bdc
18 changed files with 1306 additions and 1109 deletions
|
@ -23,17 +23,22 @@ const options = {
|
|||
|
||||
// Different formats
|
||||
const formats = {
|
||||
cjs: "dist/index.js",
|
||||
esm: "dist/index.mjs",
|
||||
cjs: "js",
|
||||
esm: "mjs",
|
||||
}
|
||||
|
||||
// Let esbuild generate different formats
|
||||
let result
|
||||
(async () => {
|
||||
for (const [format, outfile] of Object.entries(formats)) {
|
||||
for (const [format, ext] of Object.entries(formats)) {
|
||||
// Regular build
|
||||
result = await esbuild
|
||||
.build({ ...options, outfile, format })
|
||||
.catch(() => process.exit(1))
|
||||
.build({ ...options, format, outfile: `dist/index.${ext}` })
|
||||
.catch(() => process.exit(1))
|
||||
// Config build
|
||||
await esbuild
|
||||
.build({ ...options, format, outfile: `dist/config.${ext}`, entryPoints: ['config/index.js'] })
|
||||
.catch(() => process.exit(1))
|
||||
}
|
||||
|
||||
if (process.env.VERBOSE) {
|
||||
|
@ -41,11 +46,13 @@ let result
|
|||
console.log(info)
|
||||
}
|
||||
|
||||
|
||||
// Also build a version that has all dependencies bundled
|
||||
// This makes it easy to run tests
|
||||
await esbuild
|
||||
.build({
|
||||
...options,
|
||||
entryPoints: ['src/index.js'],
|
||||
minify: false,
|
||||
sourcemap: false,
|
||||
outfile: 'tests/dist/index.mjs',
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { version } from '../package.json'
|
||||
import configHelpers from '@freesewing/config-helpers'
|
||||
const { pctBasedOn } = configHelpers
|
||||
|
||||
export default {
|
||||
export const info = {
|
||||
version,
|
||||
name: 'aaron',
|
||||
design: 'Joost De Cock',
|
||||
|
@ -22,7 +20,9 @@ export default {
|
|||
'lengthBonus',
|
||||
],
|
||||
},
|
||||
measurements: [
|
||||
}
|
||||
|
||||
export const measurements = [
|
||||
'biceps',
|
||||
'chest',
|
||||
'hpsToWaistBack',
|
||||
|
@ -31,43 +31,7 @@ export default {
|
|||
'shoulderSlope',
|
||||
'shoulderToShoulder',
|
||||
'hips',
|
||||
],
|
||||
optionalMeasurements: ['highBust'],
|
||||
dependencies: {
|
||||
front: 'base',
|
||||
back: 'front',
|
||||
},
|
||||
inject: {
|
||||
front: 'base',
|
||||
back: 'front',
|
||||
},
|
||||
hide: ['base'],
|
||||
options: {
|
||||
// Constants
|
||||
brianFitCollar: false,
|
||||
collarFactor: 4.8,
|
||||
acrossBackFactor: 0.97,
|
||||
backNeckCutout: 0.05,
|
||||
bicepsEase: 0.05,
|
||||
shoulderEase: 0,
|
||||
collarEase: 0,
|
||||
frontArmholeDeeper: 0,
|
||||
armholeDepthFactor: 0.6,
|
||||
shoulderSlopeReduction: 0,
|
||||
]
|
||||
|
||||
// Percentages
|
||||
armholeDrop: { pct: 10, min: 0, max: 75 },
|
||||
backlineBend: { pct: 50, min: 25, max: 100 },
|
||||
chestEase: { pct: 8, min: 0, max: 20, ...pctBasedOn('chest') },
|
||||
hipsEase: { pct: 8, min: 0, max: 20 },
|
||||
lengthBonus: { pct: 10, min: -20, max: 60 },
|
||||
necklineBend: { pct: 100, min: 40, max: 100 },
|
||||
necklineDrop: { pct: 20, min: 10, max: 35 },
|
||||
stretchFactor: { pct: 5, min: 0, max: 15 },
|
||||
shoulderStrapWidth: { pct: 15, min: 10, max: 40 },
|
||||
shoulderStrapPlacement: { pct: 40, min: 20, max: 80 },
|
||||
export const optionalMeasurements = ['highBust']
|
||||
|
||||
// draft for high bust
|
||||
draftForHighBust: { bool: false },
|
||||
},
|
||||
}
|
||||
|
|
28
designs/aaron/config/options.js
Normal file
28
designs/aaron/config/options.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
import configHelpers from '@freesewing/config-helpers'
|
||||
const { pctBasedOn } = configHelpers
|
||||
|
||||
export const brianFitCollar = false
|
||||
export const brianFitSleeve = false
|
||||
export const acrossBackFactor = 0.97
|
||||
export const backNeckCutout = 0.05
|
||||
export const bicepsEase = 0.05
|
||||
export const shoulderEase = 0
|
||||
export const collarEase = 0
|
||||
export const frontArmholeDeeper = 0
|
||||
export const armholeDepthFactor = 0.6
|
||||
export const shoulderSlopeReduction = 0
|
||||
|
||||
// Percentages
|
||||
export const armholeDrop = { pct: 10, min: 0, max: 75 }
|
||||
export const backlineBend = { pct: 50, min: 25, max: 100 }
|
||||
export const chestEase = { pct: 8, min: 0, max: 20, ...pctBasedOn('chest') }
|
||||
export const hipsEase = { pct: 8, min: 0, max: 20 }
|
||||
export const lengthBonus = { pct: 10, min: -20, max: 60 }
|
||||
export const necklineBend = { pct: 100, min: 40, max: 100 }
|
||||
export const necklineDrop = { pct: 20, min: 10, max: 35 }
|
||||
export const stretchFactor = { pct: 5, min: 0, max: 15 }
|
||||
export const shoulderStrapWidth = { pct: 15, min: 10, max: 40 }
|
||||
export const shoulderStrapPlacement = { pct: 40, min: 20, max: 80 }
|
||||
|
||||
// draft for high bust
|
||||
export const draftForHighBust = { bool: false }
|
|
@ -1,103 +1,108 @@
|
|||
import { dimensions } from './shared'
|
||||
import front from "./front.js"
|
||||
|
||||
export default function (part) {
|
||||
let {
|
||||
store,
|
||||
sa,
|
||||
Point,
|
||||
points,
|
||||
Path,
|
||||
paths,
|
||||
options,
|
||||
complete,
|
||||
paperless,
|
||||
macro,
|
||||
utils,
|
||||
units,
|
||||
measurements,
|
||||
} = part.shorthand()
|
||||
export default {
|
||||
from: front,
|
||||
name: 'back',
|
||||
draft: function (part) {
|
||||
const {
|
||||
store,
|
||||
sa,
|
||||
Point,
|
||||
points,
|
||||
Path,
|
||||
paths,
|
||||
options,
|
||||
complete,
|
||||
paperless,
|
||||
macro,
|
||||
utils,
|
||||
units,
|
||||
measurements,
|
||||
} = part.shorthand()
|
||||
|
||||
// Lower back neck a bit
|
||||
points.cbNeck.y = measurements.neck / 10
|
||||
// Lower back neck a bit
|
||||
points.cbNeck.y = measurements.neck / 10
|
||||
|
||||
points.strapLeftCp2 = utils.beamsIntersect(
|
||||
points.strapLeft,
|
||||
points.strapCenter.rotate(90, points.strapLeft),
|
||||
points.cbNeck,
|
||||
points.cbNeck.shift(0, 10)
|
||||
)
|
||||
points.strapLeftCp2 = utils.beamsIntersect(
|
||||
points.strapLeft,
|
||||
points.strapCenter.rotate(90, points.strapLeft),
|
||||
points.cbNeck,
|
||||
points.cbNeck.shift(0, 10)
|
||||
)
|
||||
|
||||
points.armholeCp2 = points.armhole.shiftFractionTowards(
|
||||
points.armholeCorner,
|
||||
options.backlineBend
|
||||
)
|
||||
points.strapRightCp1 = points.strapRight.shiftFractionTowards(
|
||||
points.armholeCorner,
|
||||
options.backlineBend
|
||||
)
|
||||
points.armholeCp2 = points.armhole.shiftFractionTowards(
|
||||
points.armholeCorner,
|
||||
options.backlineBend
|
||||
)
|
||||
points.strapRightCp1 = points.strapRight.shiftFractionTowards(
|
||||
points.armholeCorner,
|
||||
options.backlineBend
|
||||
)
|
||||
|
||||
points.anchor = points.cbNeck.clone()
|
||||
points.anchor = points.cbNeck.clone()
|
||||
|
||||
// Seamline
|
||||
paths.seam = new Path()
|
||||
.move(points.cbNeck)
|
||||
.line(points.cbHem)
|
||||
.line(points.hem)
|
||||
.curve_(points.hipsCp2, points.armhole)
|
||||
.curve(points.armholeCp2, points.strapRightCp1, points.strapRight)
|
||||
.line(points.strapLeft)
|
||||
.line(points.strapLeft)
|
||||
.curve(points.strapLeftCp2, points.cbNeck, points.cbNeck)
|
||||
.close()
|
||||
.attr('class', 'fabric')
|
||||
// Seamline
|
||||
paths.seam = new Path()
|
||||
.move(points.cbNeck)
|
||||
.line(points.cbHem)
|
||||
.line(points.hem)
|
||||
.curve_(points.hipsCp2, points.armhole)
|
||||
.curve(points.armholeCp2, points.strapRightCp1, points.strapRight)
|
||||
.line(points.strapLeft)
|
||||
.line(points.strapLeft)
|
||||
.curve(points.strapLeftCp2, points.cbNeck, points.cbNeck)
|
||||
.close()
|
||||
.attr('class', 'fabric')
|
||||
|
||||
// Complete pattern?
|
||||
if (complete) {
|
||||
let neckOpeningLength =
|
||||
new Path()
|
||||
.move(points.strapLeft)
|
||||
.curve(points.strapLeftCp2, points.cbNeck, points.cbNeck)
|
||||
.length() + store.get('frontNeckOpeningLength')
|
||||
let armholeLength =
|
||||
new Path()
|
||||
.move(points.armhole)
|
||||
.curve(points.armholeCp2, points.strapRightCp1, points.strapRight)
|
||||
.length() + store.get('frontArmholeLength')
|
||||
points.bindingAnchor = new Point(points.armhole.x / 4, points.armhole.y)
|
||||
.attr('data-text', 'cutTwoStripsToFinishTheArmholes')
|
||||
.attr('data-text', ':\n')
|
||||
.attr('data-text', `2x: ${units(sa * 6 || 60)} x ${units(armholeLength * 0.95 + 2 * sa)}`)
|
||||
.attr('data-text', '\n \n')
|
||||
.attr('data-text', 'cutOneStripToFinishTheNeckOpening')
|
||||
.attr('data-text', ':\n')
|
||||
.attr('data-text', 'width')
|
||||
.attr('data-text', ':')
|
||||
.attr(
|
||||
'data-text',
|
||||
`${units((sa || 10) * 6)} x ${units(neckOpeningLength * 2 * 0.95 + 2 * sa)}`
|
||||
)
|
||||
//.attr('data-text-class', 'text-sm')
|
||||
// Complete pattern?
|
||||
if (complete) {
|
||||
let neckOpeningLength =
|
||||
new Path()
|
||||
.move(points.strapLeft)
|
||||
.curve(points.strapLeftCp2, points.cbNeck, points.cbNeck)
|
||||
.length() + store.get('frontNeckOpeningLength')
|
||||
let armholeLength =
|
||||
new Path()
|
||||
.move(points.armhole)
|
||||
.curve(points.armholeCp2, points.strapRightCp1, points.strapRight)
|
||||
.length() + store.get('frontArmholeLength')
|
||||
points.bindingAnchor = new Point(points.armhole.x / 4, points.armhole.y)
|
||||
.attr('data-text', 'cutTwoStripsToFinishTheArmholes')
|
||||
.attr('data-text', ':\n')
|
||||
.attr('data-text', `2x: ${units(sa * 6 || 60)} x ${units(armholeLength * 0.95 + 2 * sa)}`)
|
||||
.attr('data-text', '\n \n')
|
||||
.attr('data-text', 'cutOneStripToFinishTheNeckOpening')
|
||||
.attr('data-text', ':\n')
|
||||
.attr('data-text', 'width')
|
||||
.attr('data-text', ':')
|
||||
.attr(
|
||||
'data-text',
|
||||
`${units((sa || 10) * 6)} x ${units(neckOpeningLength * 2 * 0.95 + 2 * sa)}`
|
||||
)
|
||||
//.attr('data-text-class', 'text-sm')
|
||||
|
||||
macro('cutonfold', {
|
||||
from: points.cfNeck,
|
||||
to: points.cfHem,
|
||||
grainline: true,
|
||||
})
|
||||
macro('cutonfold', {
|
||||
from: points.cfNeck,
|
||||
to: points.cfHem,
|
||||
grainline: true,
|
||||
})
|
||||
|
||||
macro('title', { at: points.title, nr: 2, title: 'back' })
|
||||
points.scaleboxAnchor = points.scalebox = points.title.shift(90, 100)
|
||||
macro('scalebox', { at: points.scalebox })
|
||||
macro('title', { at: points.title, nr: 2, title: 'back' })
|
||||
points.scaleboxAnchor = points.scalebox = points.title.shift(90, 100)
|
||||
macro('scalebox', { at: points.scalebox })
|
||||
}
|
||||
|
||||
// Paperless?
|
||||
if (paperless) {
|
||||
dimensions(macro, points, sa)
|
||||
macro('vd', {
|
||||
from: points.cbHem,
|
||||
to: points.cbNeck,
|
||||
x: points.cbHem.x - sa - 15,
|
||||
})
|
||||
}
|
||||
|
||||
return part
|
||||
}
|
||||
|
||||
// Paperless?
|
||||
if (paperless) {
|
||||
dimensions(macro, points, sa)
|
||||
macro('vd', {
|
||||
from: points.cbHem,
|
||||
to: points.cbNeck,
|
||||
x: points.cbHem.x - sa - 15,
|
||||
})
|
||||
}
|
||||
|
||||
return part
|
||||
}
|
||||
|
|
|
@ -1,169 +1,174 @@
|
|||
import { dimensions } from './shared'
|
||||
import { base } from '@freesewing/brian'
|
||||
|
||||
export default function (part) {
|
||||
let {
|
||||
utils,
|
||||
store,
|
||||
sa,
|
||||
Point,
|
||||
points,
|
||||
Path,
|
||||
paths,
|
||||
Snippet,
|
||||
snippets,
|
||||
options,
|
||||
measurements,
|
||||
complete,
|
||||
paperless,
|
||||
macro,
|
||||
} = part.shorthand()
|
||||
export default {
|
||||
from: base,
|
||||
name: 'front',
|
||||
draft: function (part) {
|
||||
const {
|
||||
utils,
|
||||
store,
|
||||
sa,
|
||||
Point,
|
||||
points,
|
||||
Path,
|
||||
paths,
|
||||
Snippet,
|
||||
snippets,
|
||||
options,
|
||||
measurements,
|
||||
complete,
|
||||
paperless,
|
||||
macro,
|
||||
} = part.shorthand()
|
||||
|
||||
// Hide Brian paths
|
||||
for (let key of Object.keys(paths)) paths[key].render = false
|
||||
// Hide Brian paths
|
||||
for (let key of Object.keys(paths)) paths[key].render = false
|
||||
|
||||
// Handle stretch
|
||||
for (let i in points) points[i].x = points[i].x * (1 - options.stretchFactor)
|
||||
// Handle stretch
|
||||
for (let i in points) points[i].x = points[i].x * (1 - options.stretchFactor)
|
||||
|
||||
// Clone cb (center back) into cf (center front)
|
||||
for (let key of ['Neck', 'Shoulder', 'Armhole', 'Hips', 'Hem']) {
|
||||
points[`cf${key}`] = points[`cb${key}`].clone()
|
||||
}
|
||||
|
||||
// Neckline
|
||||
points.cfNeck = points.cfNeck.shift(-90, options.necklineDrop * measurements.hpsToWaistBack)
|
||||
|
||||
// Strap
|
||||
points.strapCenter = points.neck.shiftFractionTowards(
|
||||
points.shoulder,
|
||||
options.shoulderStrapPlacement
|
||||
)
|
||||
points.strapLeft = points.strapCenter.shiftTowards(
|
||||
points.neck,
|
||||
points.neck.dist(points.shoulder) * options.shoulderStrapWidth
|
||||
)
|
||||
points.strapRight = points.strapLeft.rotate(180, points.strapCenter)
|
||||
points.necklineCorner = utils.beamsIntersect(
|
||||
points.strapLeft,
|
||||
points.strapRight.rotate(-90, points.strapLeft),
|
||||
points.cfNeck.shift(0, points.armholePitch.x / 4),
|
||||
points.cfNeck
|
||||
)
|
||||
points.strapLeftCp2 = points.strapLeft.shiftFractionTowards(
|
||||
points.necklineCorner,
|
||||
options.necklineBend
|
||||
)
|
||||
points.cfNeckCp1 = points.cfNeck.shiftFractionTowards(points.necklineCorner, options.necklineBend)
|
||||
|
||||
// This will come in handy
|
||||
store.set('armholeY', points.armhole.y * (1 + options.armholeDrop))
|
||||
|
||||
// Hips
|
||||
points.hips.x =
|
||||
((measurements.hips + options.hipsEase * measurements.hips) / 4) * (1 - options.stretchFactor)
|
||||
points.waist.x = points.hips.x // Because stretch
|
||||
|
||||
points.hipsCp2 = new Point(
|
||||
points.hips.x,
|
||||
store.get('armholeY') + (points.hips.y - store.get('armholeY')) / 2
|
||||
)
|
||||
|
||||
// Hem
|
||||
points.hem.x = points.hips.x
|
||||
|
||||
// Armhole
|
||||
points.armhole = utils.beamIntersectsY(
|
||||
points.armhole,
|
||||
points.hips,
|
||||
points.armhole.y * (1 + options.armholeDrop)
|
||||
)
|
||||
points.armholeCorner = utils.beamsIntersect(
|
||||
points.armhole,
|
||||
points.armhole.shift(180, 10),
|
||||
points.strapRight,
|
||||
points.strapLeft.rotate(90, points.strapRight)
|
||||
)
|
||||
points.armholeCp2 = points.armhole.shiftFractionTowards(points.armholeCorner, 0.5)
|
||||
points.strapRightCp1 = points.strapRight.shiftFractionTowards(points.armholeCorner, 0.5)
|
||||
|
||||
points.anchor = points.cfNeck.clone()
|
||||
|
||||
// Seamline
|
||||
paths.seam = new Path()
|
||||
.move(points.cfNeck)
|
||||
.line(points.cfHem)
|
||||
.line(points.hem)
|
||||
.curve_(points.hipsCp2, points.armhole)
|
||||
.curve(points.armholeCp2, points.strapRightCp1, points.strapRight)
|
||||
.line(points.strapLeft)
|
||||
.curve(points.strapLeftCp2, points.cfNeckCp1, points.cfNeck)
|
||||
.close()
|
||||
.attr('class', 'fabric')
|
||||
|
||||
// Store length of armhole and neck opening
|
||||
store.set(
|
||||
'frontArmholeLength',
|
||||
new Path()
|
||||
.move(points.armhole)
|
||||
.curve(points.armholeCp2, points.strapRightCp1, points.strapRight)
|
||||
.length()
|
||||
)
|
||||
store.set(
|
||||
'frontNeckOpeningLength',
|
||||
new Path()
|
||||
.move(points.strapLeft)
|
||||
.curve(points.cfNeckCp1, points.cfNeckCp1, points.cfNeck)
|
||||
.length()
|
||||
)
|
||||
|
||||
// Complete pattern?
|
||||
if (complete) {
|
||||
macro('cutonfold', {
|
||||
from: points.cfNeck,
|
||||
to: points.cfHem,
|
||||
grainline: true,
|
||||
})
|
||||
points.title = new Point(points.waist.x / 2, points.waist.y)
|
||||
macro('title', { at: points.title, nr: 1, title: 'front' })
|
||||
points.logo = points.title.shift(-90, 75)
|
||||
snippets.logo = new Snippet('logo', points.logo)
|
||||
|
||||
if (sa) {
|
||||
let saShoulder = new Path().move(points.strapRight).line(points.strapLeft).offset(sa)
|
||||
paths.saShoulder = new Path()
|
||||
.move(points.strapRight)
|
||||
.line(saShoulder.start())
|
||||
.join(saShoulder)
|
||||
.line(points.strapLeft)
|
||||
.attr('class', 'fabric sa')
|
||||
paths.sa = new Path()
|
||||
.move(points.cfHem)
|
||||
.line(points.cfHem)
|
||||
.join(
|
||||
new Path()
|
||||
.move(points.cfHem)
|
||||
.line(points.hem)
|
||||
.offset(sa * 2.5)
|
||||
)
|
||||
.join(
|
||||
new Path()
|
||||
.move(points.hem)
|
||||
.curve_(points.waist, points.armhole)
|
||||
.offset(sa)
|
||||
.line(points.armhole)
|
||||
)
|
||||
.attr('class', 'fabric sa')
|
||||
// Clone cb (center back) into cf (center front)
|
||||
for (let key of ['Neck', 'Shoulder', 'Armhole', 'Hips', 'Hem']) {
|
||||
points[`cf${key}`] = points[`cb${key}`].clone()
|
||||
}
|
||||
}
|
||||
|
||||
// Paperless?
|
||||
if (paperless) {
|
||||
dimensions(macro, points, sa)
|
||||
macro('vd', {
|
||||
from: points.cfHem,
|
||||
to: points.cfNeck,
|
||||
x: points.cfHem.x - sa - 15,
|
||||
})
|
||||
}
|
||||
// Neckline
|
||||
points.cfNeck = points.cfNeck.shift(-90, options.necklineDrop * measurements.hpsToWaistBack)
|
||||
|
||||
return part
|
||||
// Strap
|
||||
points.strapCenter = points.neck.shiftFractionTowards(
|
||||
points.shoulder,
|
||||
options.shoulderStrapPlacement
|
||||
)
|
||||
points.strapLeft = points.strapCenter.shiftTowards(
|
||||
points.neck,
|
||||
points.neck.dist(points.shoulder) * options.shoulderStrapWidth
|
||||
)
|
||||
points.strapRight = points.strapLeft.rotate(180, points.strapCenter)
|
||||
points.necklineCorner = utils.beamsIntersect(
|
||||
points.strapLeft,
|
||||
points.strapRight.rotate(-90, points.strapLeft),
|
||||
points.cfNeck.shift(0, points.armholePitch.x / 4),
|
||||
points.cfNeck
|
||||
)
|
||||
points.strapLeftCp2 = points.strapLeft.shiftFractionTowards(
|
||||
points.necklineCorner,
|
||||
options.necklineBend
|
||||
)
|
||||
points.cfNeckCp1 = points.cfNeck.shiftFractionTowards(points.necklineCorner, options.necklineBend)
|
||||
|
||||
// This will come in handy
|
||||
store.set('armholeY', points.armhole.y * (1 + options.armholeDrop))
|
||||
|
||||
// Hips
|
||||
points.hips.x =
|
||||
((measurements.hips + options.hipsEase * measurements.hips) / 4) * (1 - options.stretchFactor)
|
||||
points.waist.x = points.hips.x // Because stretch
|
||||
|
||||
points.hipsCp2 = new Point(
|
||||
points.hips.x,
|
||||
store.get('armholeY') + (points.hips.y - store.get('armholeY')) / 2
|
||||
)
|
||||
|
||||
// Hem
|
||||
points.hem.x = points.hips.x
|
||||
|
||||
// Armhole
|
||||
points.armhole = utils.beamIntersectsY(
|
||||
points.armhole,
|
||||
points.hips,
|
||||
points.armhole.y * (1 + options.armholeDrop)
|
||||
)
|
||||
points.armholeCorner = utils.beamsIntersect(
|
||||
points.armhole,
|
||||
points.armhole.shift(180, 10),
|
||||
points.strapRight,
|
||||
points.strapLeft.rotate(90, points.strapRight)
|
||||
)
|
||||
points.armholeCp2 = points.armhole.shiftFractionTowards(points.armholeCorner, 0.5)
|
||||
points.strapRightCp1 = points.strapRight.shiftFractionTowards(points.armholeCorner, 0.5)
|
||||
|
||||
points.anchor = points.cfNeck.clone()
|
||||
|
||||
// Seamline
|
||||
paths.seam = new Path()
|
||||
.move(points.cfNeck)
|
||||
.line(points.cfHem)
|
||||
.line(points.hem)
|
||||
.curve_(points.hipsCp2, points.armhole)
|
||||
.curve(points.armholeCp2, points.strapRightCp1, points.strapRight)
|
||||
.line(points.strapLeft)
|
||||
.curve(points.strapLeftCp2, points.cfNeckCp1, points.cfNeck)
|
||||
.close()
|
||||
.attr('class', 'fabric')
|
||||
|
||||
// Store length of armhole and neck opening
|
||||
store.set(
|
||||
'frontArmholeLength',
|
||||
new Path()
|
||||
.move(points.armhole)
|
||||
.curve(points.armholeCp2, points.strapRightCp1, points.strapRight)
|
||||
.length()
|
||||
)
|
||||
store.set(
|
||||
'frontNeckOpeningLength',
|
||||
new Path()
|
||||
.move(points.strapLeft)
|
||||
.curve(points.cfNeckCp1, points.cfNeckCp1, points.cfNeck)
|
||||
.length()
|
||||
)
|
||||
|
||||
// Complete pattern?
|
||||
if (complete) {
|
||||
macro('cutonfold', {
|
||||
from: points.cfNeck,
|
||||
to: points.cfHem,
|
||||
grainline: true,
|
||||
})
|
||||
points.title = new Point(points.waist.x / 2, points.waist.y)
|
||||
macro('title', { at: points.title, nr: 1, title: 'front' })
|
||||
points.logo = points.title.shift(-90, 75)
|
||||
snippets.logo = new Snippet('logo', points.logo)
|
||||
|
||||
if (sa) {
|
||||
let saShoulder = new Path().move(points.strapRight).line(points.strapLeft).offset(sa)
|
||||
paths.saShoulder = new Path()
|
||||
.move(points.strapRight)
|
||||
.line(saShoulder.start())
|
||||
.join(saShoulder)
|
||||
.line(points.strapLeft)
|
||||
.attr('class', 'fabric sa')
|
||||
paths.sa = new Path()
|
||||
.move(points.cfHem)
|
||||
.line(points.cfHem)
|
||||
.join(
|
||||
new Path()
|
||||
.move(points.cfHem)
|
||||
.line(points.hem)
|
||||
.offset(sa * 2.5)
|
||||
)
|
||||
.join(
|
||||
new Path()
|
||||
.move(points.hem)
|
||||
.curve_(points.waist, points.armhole)
|
||||
.offset(sa)
|
||||
.line(points.armhole)
|
||||
)
|
||||
.attr('class', 'fabric sa')
|
||||
}
|
||||
}
|
||||
|
||||
// Paperless?
|
||||
if (paperless) {
|
||||
dimensions(macro, points, sa)
|
||||
macro('vd', {
|
||||
from: points.cfHem,
|
||||
to: points.cfNeck,
|
||||
x: points.cfHem.x - sa - 15,
|
||||
})
|
||||
}
|
||||
|
||||
return part
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,33 @@
|
|||
// FreeSewing core library
|
||||
import freesewing from '@freesewing/core'
|
||||
import Brian from '@freesewing/brian'
|
||||
import plugins from '@freesewing/plugin-bundle'
|
||||
import plugin from '@freesewing/plugin-bust' // Note: conditional plugin
|
||||
import config from '../config'
|
||||
// FreeSewing Plugins
|
||||
import pluginBundle from '@freesewing/plugin-bundle'
|
||||
import bustPlugin from '@freesewing/plugin-bust' // Note: conditional plugin
|
||||
// Design config & options
|
||||
import { info, measurements, optionalMeasurements } from '../config/index'
|
||||
import * as options from '../config/options'
|
||||
// Design parts
|
||||
import back from './back'
|
||||
import front from './front'
|
||||
|
||||
// Parts
|
||||
import draftBack from './back'
|
||||
import draftFront from './front'
|
||||
|
||||
/* Check to see whether we should load the bust plugin
|
||||
* Only of the `draftForHighBust` options is set
|
||||
* AND the highBust measurement is available
|
||||
*/
|
||||
const condition = (settings = false) =>
|
||||
settings &&
|
||||
settings.options &&
|
||||
settings.options.draftForHighBust &&
|
||||
settings.measurements.highBust
|
||||
? true
|
||||
: false
|
||||
|
||||
// Create design
|
||||
const Aaron = new freesewing.Design(config, plugins, { plugin, condition })
|
||||
|
||||
// Attach draft methods to prototype
|
||||
Aaron.prototype.draftBase = function (part) {
|
||||
// Getting the base part from Brian
|
||||
return new Brian(this.settings).draftBase(part)
|
||||
}
|
||||
Aaron.prototype.draftFront = (part) => draftFront(part)
|
||||
Aaron.prototype.draftBack = (part) => draftBack(part)
|
||||
// Setup design
|
||||
const Aaron = new freesewing.Design({
|
||||
...info,
|
||||
measurements,
|
||||
optionalMeasurements,
|
||||
options: { ...options },
|
||||
parts: { back, front },
|
||||
plugins: pluginBundle,
|
||||
conditionalPlugins: {
|
||||
plugin: bustPlugin,
|
||||
condition: (settings=false) =>
|
||||
settings?.options?.draftForHighBust &&
|
||||
settings?.measurements?.highBust
|
||||
? true : false
|
||||
}
|
||||
})
|
||||
|
||||
// Named exports
|
||||
export { config, Aaron }
|
||||
|
||||
export { front, back, Aaron }
|
||||
// Default export
|
||||
export default Aaron
|
||||
|
|
|
@ -23,17 +23,22 @@ const options = {
|
|||
|
||||
// Different formats
|
||||
const formats = {
|
||||
cjs: "dist/index.js",
|
||||
esm: "dist/index.mjs",
|
||||
cjs: "js",
|
||||
esm: "mjs",
|
||||
}
|
||||
|
||||
// Let esbuild generate different formats
|
||||
let result
|
||||
(async () => {
|
||||
for (const [format, outfile] of Object.entries(formats)) {
|
||||
for (const [format, ext] of Object.entries(formats)) {
|
||||
// Regular build
|
||||
result = await esbuild
|
||||
.build({ ...options, outfile, format })
|
||||
.catch(() => process.exit(1))
|
||||
.build({ ...options, format, outfile: `dist/index.${ext}` })
|
||||
.catch(() => process.exit(1))
|
||||
// Config build
|
||||
await esbuild
|
||||
.build({ ...options, format, outfile: `dist/config.${ext}`, entryPoints: ['config/index.js'] })
|
||||
.catch(() => process.exit(1))
|
||||
}
|
||||
|
||||
if (process.env.VERBOSE) {
|
||||
|
@ -41,11 +46,13 @@ let result
|
|||
console.log(info)
|
||||
}
|
||||
|
||||
|
||||
// Also build a version that has all dependencies bundled
|
||||
// This makes it easy to run tests
|
||||
await esbuild
|
||||
.build({
|
||||
...options,
|
||||
entryPoints: ['src/index.js'],
|
||||
minify: false,
|
||||
sourcemap: false,
|
||||
outfile: 'tests/dist/index.mjs',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { version } from '../package.json'
|
||||
|
||||
export default {
|
||||
export const info = {
|
||||
version,
|
||||
name: 'brian',
|
||||
design: 'Joost De Cock',
|
||||
|
@ -52,74 +52,19 @@ export default {
|
|||
},
|
||||
],
|
||||
},
|
||||
measurements: [
|
||||
'biceps',
|
||||
'chest',
|
||||
'hpsToWaistBack',
|
||||
'waistToHips',
|
||||
'neck',
|
||||
'shoulderSlope',
|
||||
'shoulderToShoulder',
|
||||
'shoulderToWrist',
|
||||
'wrist',
|
||||
],
|
||||
optionalMeasurements: ['highBust'],
|
||||
dependencies: {
|
||||
back: 'base',
|
||||
front: 'back',
|
||||
sleevecap: 'front',
|
||||
sleeve: 'sleevecap',
|
||||
},
|
||||
inject: {
|
||||
back: 'base',
|
||||
front: 'back',
|
||||
sleeve: 'sleevecap',
|
||||
},
|
||||
hide: ['base', 'sleevecap'],
|
||||
options: {
|
||||
// Constants
|
||||
brianFitSleeve: true,
|
||||
brianFitCollar: true,
|
||||
collarFactor: 4.8,
|
||||
|
||||
// Percentages
|
||||
acrossBackFactor: { pct: 98, min: 93, max: 100 },
|
||||
armholeDepthFactor: { pct: 55, min: 50, max: 70 },
|
||||
backNeckCutout: { pct: 5, min: 2, max: 8 },
|
||||
bicepsEase: { pct: 15, min: 0, max: 50 },
|
||||
chestEase: { pct: 15, min: -4, max: 35 },
|
||||
collarEase: { pct: 5, min: 0, max: 10 },
|
||||
cuffEase: { pct: 20, min: 0, max: 200 },
|
||||
frontArmholeDeeper: { pct: 0.2, min: 0, max: 0.5 },
|
||||
lengthBonus: { pct: 0, min: -4, max: 60 },
|
||||
shoulderEase: { pct: 0, min: -2, max: 6 },
|
||||
shoulderSlopeReduction: { pct: 0, min: 0, max: 80 },
|
||||
// s3 is short for Shoulder Seam Shift
|
||||
s3Collar: { pct: 0, min: -100, max: 100 },
|
||||
s3Armhole: { pct: 0, min: -100, max: 100 },
|
||||
sleevecapEase: { pct: 0, min: 0, max: 10 },
|
||||
sleevecapTopFactorX: { pct: 50, min: 25, max: 75 },
|
||||
sleevecapTopFactorY: { pct: 45, min: 35, max: 125 },
|
||||
sleevecapBackFactorX: { pct: 60, min: 35, max: 65 },
|
||||
sleevecapBackFactorY: { pct: 33, min: 30, max: 65 },
|
||||
sleevecapFrontFactorX: { pct: 55, min: 35, max: 65 },
|
||||
sleevecapFrontFactorY: { pct: 33, min: 30, max: 65 },
|
||||
sleevecapQ1Offset: { pct: 1.7, min: 0, max: 7 },
|
||||
sleevecapQ2Offset: { pct: 3.5, min: 0, max: 7 },
|
||||
sleevecapQ3Offset: { pct: 2.5, min: 0, max: 7 },
|
||||
sleevecapQ4Offset: { pct: 1, min: 0, max: 7 },
|
||||
sleevecapQ1Spread1: { pct: 10, min: 4, max: 20 },
|
||||
sleevecapQ1Spread2: { pct: 15, min: 4, max: 20 },
|
||||
sleevecapQ2Spread1: { pct: 15, min: 4, max: 20 },
|
||||
sleevecapQ2Spread2: { pct: 10, min: 4, max: 20 },
|
||||
sleevecapQ3Spread1: { pct: 10, min: 4, max: 20 },
|
||||
sleevecapQ3Spread2: { pct: 8, min: 4, max: 20 },
|
||||
sleevecapQ4Spread1: { pct: 7, min: 4, max: 20 },
|
||||
sleevecapQ4Spread2: { pct: 6.3, min: 4, max: 20 },
|
||||
sleeveWidthGuarantee: { pct: 90, min: 25, max: 100 },
|
||||
sleeveLengthBonus: { pct: 0, min: -40, max: 10 },
|
||||
|
||||
// draft for high bust
|
||||
draftForHighBust: { bool: false },
|
||||
},
|
||||
}
|
||||
|
||||
export const measurements = [
|
||||
'biceps',
|
||||
'chest',
|
||||
'hpsToWaistBack',
|
||||
'waistToHips',
|
||||
'neck',
|
||||
'shoulderSlope',
|
||||
'shoulderToShoulder',
|
||||
'shoulderToWrist',
|
||||
'wrist',
|
||||
]
|
||||
|
||||
export const optionalMeasurements = ['highBust']
|
||||
|
||||
|
|
94
designs/brian/config/options.js
Normal file
94
designs/brian/config/options.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
// Constants
|
||||
export const brianFitSleeve = true
|
||||
export const brianFitCollar = true
|
||||
export const collarFactor = 4.8
|
||||
|
||||
// Percentages
|
||||
export const acrossBackFactor = { pct: 98, min: 93, max: 100 }
|
||||
export const armholeDepthFactor = { pct: 55, min: 50, max: 70 }
|
||||
export const backNeckCutout = { pct: 5, min: 2, max: 8 }
|
||||
export const bicepsEase = { pct: 15, min: 0, max: 50 }
|
||||
export const chestEase = { pct: 15, min: -4, max: 35 }
|
||||
export const collarEase = { pct: 5, min: 0, max: 10 }
|
||||
export const cuffEase = { pct: 20, min: 0, max: 200 }
|
||||
export const frontArmholeDeeper = { pct: 0.2, min: 0, max: 0.5 }
|
||||
export const lengthBonus = { pct: 0, min: -4, max: 60 }
|
||||
export const shoulderEase = { pct: 0, min: -2, max: 6 }
|
||||
export const shoulderSlopeReduction = { pct: 0, min: 0, max: 80 }
|
||||
|
||||
// s3 is short for Shoulder Seam Shift
|
||||
export const s3Collar = { pct: 0, min: -100, max: 100 }
|
||||
export const s3Armhole = { pct: 0, min: -100, max: 100 }
|
||||
|
||||
// Sleevecap
|
||||
export const sleevecapEase = { pct: 0, min: 0, max: 10 }
|
||||
export const sleevecapTopFactorX = { pct: 50, min: 25, max: 75 }
|
||||
export const sleevecapTopFactorY = { pct: 45, min: 35, max: 125 }
|
||||
export const sleevecapBackFactorX = { pct: 60, min: 35, max: 65 }
|
||||
export const sleevecapBackFactorY = { pct: 33, min: 30, max: 65 }
|
||||
export const sleevecapFrontFactorX = { pct: 55, min: 35, max: 65 }
|
||||
export const sleevecapFrontFactorY = { pct: 33, min: 30, max: 65 }
|
||||
export const sleevecapQ1Offset = { pct: 1.7, min: 0, max: 7 }
|
||||
export const sleevecapQ2Offset = { pct: 3.5, min: 0, max: 7 }
|
||||
export const sleevecapQ3Offset = { pct: 2.5, min: 0, max: 7 }
|
||||
export const sleevecapQ4Offset = { pct: 1, min: 0, max: 7 }
|
||||
export const sleevecapQ1Spread1 = { pct: 10, min: 4, max: 20 }
|
||||
export const sleevecapQ1Spread2 = { pct: 15, min: 4, max: 20 }
|
||||
export const sleevecapQ2Spread1 = { pct: 15, min: 4, max: 20 }
|
||||
export const sleevecapQ2Spread2 = { pct: 10, min: 4, max: 20 }
|
||||
export const sleevecapQ3Spread1 = { pct: 10, min: 4, max: 20 }
|
||||
export const sleevecapQ3Spread2 = { pct: 8, min: 4, max: 20 }
|
||||
export const sleevecapQ4Spread1 = { pct: 7, min: 4, max: 20 }
|
||||
export const sleevecapQ4Spread2 = { pct: 6.3, min: 4, max: 20 }
|
||||
// Sleeve
|
||||
export const sleeveWidthGuarantee = { pct: 90, min: 25, max: 100 }
|
||||
export const sleeveLengthBonus = { pct: 0, min: -40, max: 10 }
|
||||
|
||||
// Draft for high bust
|
||||
export const draftForHighBust = { bool: false }
|
||||
|
||||
// Helper objects for per-part options
|
||||
export const _base = {
|
||||
brianFitSleeve,
|
||||
brianFitCollar,
|
||||
collarFactor,
|
||||
acrossBackFactor,
|
||||
armholeDepthFactor,
|
||||
backNeckCutout,
|
||||
bicepsEase,
|
||||
chestEase,
|
||||
collarEase,
|
||||
cuffEase,
|
||||
frontArmholeDeeper,
|
||||
lengthBonus,
|
||||
shoulderEase,
|
||||
shoulderSlopeReduction,
|
||||
s3Collar,
|
||||
s3Armhole,
|
||||
draftForHighBust,
|
||||
}
|
||||
export const _sleevecap = {
|
||||
sleevecapEase,
|
||||
sleevecapTopFactorX,
|
||||
sleevecapTopFactorY,
|
||||
sleevecapBackFactorX,
|
||||
sleevecapBackFactorY,
|
||||
sleevecapFrontFactorX,
|
||||
sleevecapFrontFactorY,
|
||||
sleevecapQ1Offset,
|
||||
sleevecapQ2Offset,
|
||||
sleevecapQ3Offset,
|
||||
sleevecapQ4Offset,
|
||||
sleevecapQ1Spread1,
|
||||
sleevecapQ1Spread2,
|
||||
sleevecapQ2Spread1,
|
||||
sleevecapQ2Spread2,
|
||||
sleevecapQ3Spread1,
|
||||
sleevecapQ3Spread2,
|
||||
sleevecapQ4Spread1,
|
||||
sleevecapQ4Spread2,
|
||||
sleeveWidthGuarantee,
|
||||
}
|
||||
export const _sleeve = { sleeveLengthBonus }
|
||||
|
||||
|
|
@ -1,184 +1,189 @@
|
|||
import * as shared from './shared'
|
||||
import base from './base'
|
||||
|
||||
export default (part) => {
|
||||
let {
|
||||
store,
|
||||
sa,
|
||||
points,
|
||||
Path,
|
||||
paths,
|
||||
Snippet,
|
||||
snippets,
|
||||
complete,
|
||||
paperless,
|
||||
macro,
|
||||
options,
|
||||
utils,
|
||||
} = part.shorthand()
|
||||
export default {
|
||||
from: base,
|
||||
name: 'back',
|
||||
draft: (part) => {
|
||||
const {
|
||||
store,
|
||||
sa,
|
||||
points,
|
||||
Path,
|
||||
paths,
|
||||
Snippet,
|
||||
snippets,
|
||||
complete,
|
||||
paperless,
|
||||
macro,
|
||||
options,
|
||||
utils,
|
||||
} = part.shorthand()
|
||||
|
||||
points.anchor = points.hps.clone()
|
||||
points.anchor = points.hps.clone()
|
||||
|
||||
// Adapt the shoulder seam according to the relevant options
|
||||
// Note: s3 stands for Shoulder Seam Shift
|
||||
// Don't bother with less than 10% as that's just asking for trouble
|
||||
if (options.s3Collar < 0.1 && options.s3Collar > -0.1) {
|
||||
points.s3CollarSplit = points.hps
|
||||
paths.backCollar = new Path()
|
||||
.move(points.hps)
|
||||
.curve_(points.neckCp2, points.cbNeck)
|
||||
.setRender(false)
|
||||
} else if (options.s3Collar > 0) {
|
||||
// Shift shoulder seam forward on the collar side
|
||||
points.s3CollarSplit = utils.curveIntersectsY(
|
||||
points.hps,
|
||||
points.mirroredNeckCp2Front,
|
||||
points.mirroredCfNeckCp1,
|
||||
points.mirroredCfNeck,
|
||||
store.get('s3CollarMaxFront') * -1 * options.s3Collar
|
||||
)
|
||||
paths.backCollar = new Path()
|
||||
.move(points.hps)
|
||||
._curve(points.mirroredNeckCp2Front, points.mirroredCfNeckCp1, points.mirroredCfNeck)
|
||||
.split(points.s3CollarSplit)[0]
|
||||
.reverse()
|
||||
.join(new Path().move(points.hps).curve_(points.neckCp2, points.cbNeck))
|
||||
.setRender(false)
|
||||
} else if (options.s3Collar < 0) {
|
||||
// Shift shoulder seam backward on the collar side
|
||||
points.s3CollarSplit = utils.curveIntersectsY(
|
||||
points.hps,
|
||||
points.neckCp2,
|
||||
points.cbNeck,
|
||||
points.cbNeck,
|
||||
store.get('s3CollarMaxBack') * -1 * options.s3Collar
|
||||
)
|
||||
paths.backCollar = new Path()
|
||||
.move(points.cbNeck)
|
||||
._curve(points.neckCp2, points.neck)
|
||||
.split(points.s3CollarSplit)[0]
|
||||
.reverse()
|
||||
.setRender(false)
|
||||
}
|
||||
// Don't bother with less than 10% as that's just asking for trouble
|
||||
if (options.s3Armhole < 0.1 && options.s3Armhole > -0.1) {
|
||||
points.s3ArmholeSplit = points.shoulder
|
||||
paths.backArmhole = new Path()
|
||||
.move(points.armholePitch)
|
||||
.curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder)
|
||||
.setRender(false)
|
||||
} else if (options.s3Armhole > 0) {
|
||||
// Shift shoulder seam forward on the armhole side
|
||||
points.s3ArmholeSplit = utils.curveIntersectsY(
|
||||
points.shoulder,
|
||||
points.mirroredShoulderCp1,
|
||||
points.mirroredFrontArmholePitchCp2,
|
||||
points.mirroredFrontArmholePitch,
|
||||
store.get('s3ArmholeMax') * -1 * options.s3Armhole + points.shoulder.y
|
||||
)
|
||||
paths.backArmhole = new Path()
|
||||
.move(points.armholePitch)
|
||||
.curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder)
|
||||
.join(
|
||||
new Path()
|
||||
.move(points.shoulder)
|
||||
.curve(
|
||||
points.mirroredShoulderCp1,
|
||||
points.mirroredFrontArmholePitchCp2,
|
||||
points.mirroredFrontArmholePitch
|
||||
)
|
||||
.split(points.s3ArmholeSplit)[0]
|
||||
// Adapt the shoulder seam according to the relevant options
|
||||
// Note: s3 stands for Shoulder Seam Shift
|
||||
// Don't bother with less than 10% as that's just asking for trouble
|
||||
if (options.s3Collar < 0.1 && options.s3Collar > -0.1) {
|
||||
points.s3CollarSplit = points.hps
|
||||
paths.backCollar = new Path()
|
||||
.move(points.hps)
|
||||
.curve_(points.neckCp2, points.cbNeck)
|
||||
.setRender(false)
|
||||
} else if (options.s3Collar > 0) {
|
||||
// Shift shoulder seam forward on the collar side
|
||||
points.s3CollarSplit = utils.curveIntersectsY(
|
||||
points.hps,
|
||||
points.mirroredNeckCp2Front,
|
||||
points.mirroredCfNeckCp1,
|
||||
points.mirroredCfNeck,
|
||||
store.get('s3CollarMaxFront') * -1 * options.s3Collar
|
||||
)
|
||||
.setRender(false)
|
||||
} else if (options.s3Armhole < 0) {
|
||||
// Shift shoulder seam backward on the armhole side
|
||||
points.s3ArmholeSplit = utils.curveIntersectsY(
|
||||
points.shoulder,
|
||||
points.shoulderCp1,
|
||||
points.armholePitchCp2,
|
||||
points.armholePitch,
|
||||
store.get('s3ArmholeMax') * -1 * options.s3Armhole + points.shoulder.y
|
||||
)
|
||||
paths.backArmhole = new Path()
|
||||
.move(points.armholePitch)
|
||||
.curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder)
|
||||
.split(points.s3ArmholeSplit)[0]
|
||||
.setRender(false)
|
||||
}
|
||||
|
||||
// Seamline
|
||||
paths.saBase = new Path()
|
||||
.move(points.cbHem)
|
||||
.line(points.hem)
|
||||
.line(points.armhole)
|
||||
.curve(points.armholeCp2, points.armholeHollowCp1, points.armholeHollow)
|
||||
.curve(points.armholeHollowCp2, points.armholePitchCp1, points.armholePitch)
|
||||
.join(paths.backArmhole)
|
||||
.line(points.s3CollarSplit)
|
||||
.join(paths.backCollar)
|
||||
.setRender(false)
|
||||
paths.seam = new Path()
|
||||
.move(points.cbNeck)
|
||||
.line(points.cbHips)
|
||||
.join(paths.saBase)
|
||||
.attr('class', 'fabric')
|
||||
|
||||
// Store lengths to fit sleeve
|
||||
store.set('backArmholeLength', shared.armholeLength(points, Path))
|
||||
store.set('backArmholeToArmholePitch', shared.armholeToArmholePitch(points, Path))
|
||||
|
||||
// Complete pattern?
|
||||
if (complete) {
|
||||
macro('cutonfold', {
|
||||
from: points.cbNeck,
|
||||
to: points.cbHips,
|
||||
grainline: true,
|
||||
})
|
||||
|
||||
macro('title', { at: points.title, nr: 2, title: 'back' })
|
||||
snippets.armholePitchNotch = new Snippet('bnotch', points.armholePitch)
|
||||
paths.waist = new Path().move(points.cbWaist).line(points.waist).attr('class', 'help')
|
||||
if (sa) {
|
||||
paths.sa = paths.saBase
|
||||
.offset(sa)
|
||||
.attr('class', 'fabric sa')
|
||||
.line(points.cbNeck)
|
||||
.move(points.cbHips)
|
||||
paths.sa.line(paths.sa.start())
|
||||
paths.backCollar = new Path()
|
||||
.move(points.hps)
|
||||
._curve(points.mirroredNeckCp2Front, points.mirroredCfNeckCp1, points.mirroredCfNeck)
|
||||
.split(points.s3CollarSplit)[0]
|
||||
.reverse()
|
||||
.join(new Path().move(points.hps).curve_(points.neckCp2, points.cbNeck))
|
||||
.setRender(false)
|
||||
} else if (options.s3Collar < 0) {
|
||||
// Shift shoulder seam backward on the collar side
|
||||
points.s3CollarSplit = utils.curveIntersectsY(
|
||||
points.hps,
|
||||
points.neckCp2,
|
||||
points.cbNeck,
|
||||
points.cbNeck,
|
||||
store.get('s3CollarMaxBack') * -1 * options.s3Collar
|
||||
)
|
||||
paths.backCollar = new Path()
|
||||
.move(points.cbNeck)
|
||||
._curve(points.neckCp2, points.neck)
|
||||
.split(points.s3CollarSplit)[0]
|
||||
.reverse()
|
||||
.setRender(false)
|
||||
}
|
||||
// Don't bother with less than 10% as that's just asking for trouble
|
||||
if (options.s3Armhole < 0.1 && options.s3Armhole > -0.1) {
|
||||
points.s3ArmholeSplit = points.shoulder
|
||||
paths.backArmhole = new Path()
|
||||
.move(points.armholePitch)
|
||||
.curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder)
|
||||
.setRender(false)
|
||||
} else if (options.s3Armhole > 0) {
|
||||
// Shift shoulder seam forward on the armhole side
|
||||
points.s3ArmholeSplit = utils.curveIntersectsY(
|
||||
points.shoulder,
|
||||
points.mirroredShoulderCp1,
|
||||
points.mirroredFrontArmholePitchCp2,
|
||||
points.mirroredFrontArmholePitch,
|
||||
store.get('s3ArmholeMax') * -1 * options.s3Armhole + points.shoulder.y
|
||||
)
|
||||
paths.backArmhole = new Path()
|
||||
.move(points.armholePitch)
|
||||
.curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder)
|
||||
.join(
|
||||
new Path()
|
||||
.move(points.shoulder)
|
||||
.curve(
|
||||
points.mirroredShoulderCp1,
|
||||
points.mirroredFrontArmholePitchCp2,
|
||||
points.mirroredFrontArmholePitch
|
||||
)
|
||||
.split(points.s3ArmholeSplit)[0]
|
||||
)
|
||||
.setRender(false)
|
||||
} else if (options.s3Armhole < 0) {
|
||||
// Shift shoulder seam backward on the armhole side
|
||||
points.s3ArmholeSplit = utils.curveIntersectsY(
|
||||
points.shoulder,
|
||||
points.shoulderCp1,
|
||||
points.armholePitchCp2,
|
||||
points.armholePitch,
|
||||
store.get('s3ArmholeMax') * -1 * options.s3Armhole + points.shoulder.y
|
||||
)
|
||||
paths.backArmhole = new Path()
|
||||
.move(points.armholePitch)
|
||||
.curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder)
|
||||
.split(points.s3ArmholeSplit)[0]
|
||||
.setRender(false)
|
||||
}
|
||||
|
||||
// Add notches if the shoulder seam is shifted
|
||||
shared.s3Notches(part, 'bnotch')
|
||||
}
|
||||
// Seamline
|
||||
paths.saBase = new Path()
|
||||
.move(points.cbHem)
|
||||
.line(points.hem)
|
||||
.line(points.armhole)
|
||||
.curve(points.armholeCp2, points.armholeHollowCp1, points.armholeHollow)
|
||||
.curve(points.armholeHollowCp2, points.armholePitchCp1, points.armholePitch)
|
||||
.join(paths.backArmhole)
|
||||
.line(points.s3CollarSplit)
|
||||
.join(paths.backCollar)
|
||||
.setRender(false)
|
||||
paths.seam = new Path()
|
||||
.move(points.cbNeck)
|
||||
.line(points.cbHips)
|
||||
.join(paths.saBase)
|
||||
.attr('class', 'fabric')
|
||||
|
||||
// Paperless?
|
||||
if (paperless) {
|
||||
shared.dimensions(part, 'back')
|
||||
macro('hd', {
|
||||
from: points.cbHips,
|
||||
to: points.hips,
|
||||
y: points.hem.y + sa + 15,
|
||||
})
|
||||
macro('vd', {
|
||||
from: points.cbHem,
|
||||
to: points.cbWaist,
|
||||
x: points.cbHips.x - sa - 15,
|
||||
})
|
||||
macro('vd', {
|
||||
from: points.cbHem,
|
||||
to: points.cbNeck,
|
||||
x: points.cbHips.x - sa - 30,
|
||||
})
|
||||
macro('hd', {
|
||||
from: points.cbNeck,
|
||||
to: points.s3CollarSplit,
|
||||
y: points.s3CollarSplit.y - sa - 15,
|
||||
})
|
||||
macro('hd', {
|
||||
from: points.cbNeck,
|
||||
to: points.s3ArmholeSplit,
|
||||
y: points.s3CollarSplit.y - sa - 30,
|
||||
})
|
||||
}
|
||||
// Store lengths to fit sleeve
|
||||
store.set('backArmholeLength', shared.armholeLength(points, Path))
|
||||
store.set('backArmholeToArmholePitch', shared.armholeToArmholePitch(points, Path))
|
||||
|
||||
return part
|
||||
// Complete pattern?
|
||||
if (complete) {
|
||||
macro('cutonfold', {
|
||||
from: points.cbNeck,
|
||||
to: points.cbHips,
|
||||
grainline: true,
|
||||
})
|
||||
|
||||
macro('title', { at: points.title, nr: 2, title: 'back' })
|
||||
snippets.armholePitchNotch = new Snippet('bnotch', points.armholePitch)
|
||||
paths.waist = new Path().move(points.cbWaist).line(points.waist).attr('class', 'help')
|
||||
if (sa) {
|
||||
paths.sa = paths.saBase
|
||||
.offset(sa)
|
||||
.attr('class', 'fabric sa')
|
||||
.line(points.cbNeck)
|
||||
.move(points.cbHips)
|
||||
paths.sa.line(paths.sa.start())
|
||||
}
|
||||
|
||||
// Add notches if the shoulder seam is shifted
|
||||
shared.s3Notches(part, 'bnotch')
|
||||
}
|
||||
|
||||
// Paperless?
|
||||
if (paperless) {
|
||||
shared.dimensions(part, 'back')
|
||||
macro('hd', {
|
||||
from: points.cbHips,
|
||||
to: points.hips,
|
||||
y: points.hem.y + sa + 15,
|
||||
})
|
||||
macro('vd', {
|
||||
from: points.cbHem,
|
||||
to: points.cbWaist,
|
||||
x: points.cbHips.x - sa - 15,
|
||||
})
|
||||
macro('vd', {
|
||||
from: points.cbHem,
|
||||
to: points.cbNeck,
|
||||
x: points.cbHips.x - sa - 30,
|
||||
})
|
||||
macro('hd', {
|
||||
from: points.cbNeck,
|
||||
to: points.s3CollarSplit,
|
||||
y: points.s3CollarSplit.y - sa - 15,
|
||||
})
|
||||
macro('hd', {
|
||||
from: points.cbNeck,
|
||||
to: points.s3ArmholeSplit,
|
||||
y: points.s3CollarSplit.y - sa - 30,
|
||||
})
|
||||
}
|
||||
|
||||
return part
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,173 +1,181 @@
|
|||
export default (part) => {
|
||||
let {
|
||||
measurements,
|
||||
options,
|
||||
store,
|
||||
points,
|
||||
snippets,
|
||||
Point,
|
||||
Snippet,
|
||||
Path,
|
||||
paths,
|
||||
utils,
|
||||
complete,
|
||||
macro,
|
||||
} = part.shorthand()
|
||||
import { _base as options } from '../config/options.js'
|
||||
|
||||
store.set('shoulderEase', (measurements.shoulderToShoulder * options.shoulderEase) / 2)
|
||||
|
||||
// Center back (cb) vertical axis
|
||||
points.cbHps = new Point(0, 0)
|
||||
points.cbNeck = new Point(0, options.backNeckCutout * measurements.neck)
|
||||
points.cbWaist = new Point(0, measurements.hpsToWaistBack)
|
||||
points.cbHips = new Point(0, points.cbWaist.y + measurements.waistToHips)
|
||||
export default {
|
||||
name: 'base',
|
||||
hide: true,
|
||||
options,
|
||||
draft: (part) => {
|
||||
const {
|
||||
measurements,
|
||||
options,
|
||||
store,
|
||||
points,
|
||||
snippets,
|
||||
Point,
|
||||
Snippet,
|
||||
Path,
|
||||
paths,
|
||||
utils,
|
||||
complete,
|
||||
macro,
|
||||
} = part.shorthand()
|
||||
|
||||
// Shoulder line
|
||||
points.neck = new Point((measurements.neck * (1 + options.collarEase)) / options.collarFactor, 0)
|
||||
points.hps = points.neck.clone() // We started using HPS in many measurements
|
||||
// Shoulder point using shoulderSlope degree measurement
|
||||
points.shoulder = utils.beamsIntersect(
|
||||
points.hps,
|
||||
points.hps.shift(measurements.shoulderSlope * -1, 100),
|
||||
new Point(measurements.shoulderToShoulder / 2 + store.get('shoulderEase'), -100),
|
||||
new Point(measurements.shoulderToShoulder / 2 + store.get('shoulderEase'), 100)
|
||||
)
|
||||
// Determine armhole depth and cbShoulder independent of shoulder slope reduction
|
||||
points.cbShoulder = new Point(0, points.shoulder.y)
|
||||
points.cbArmhole = new Point(
|
||||
0,
|
||||
points.shoulder.y + measurements.biceps * (1 + options.bicepsEase) * options.armholeDepthFactor
|
||||
)
|
||||
store.set('shoulderEase', (measurements.shoulderToShoulder * options.shoulderEase) / 2)
|
||||
|
||||
// Now take shoulder slope reduction into account
|
||||
points.shoulder.y -= (points.shoulder.y - points.cbHps.y) * options.shoulderSlopeReduction
|
||||
// Shoulder should never be higher than HPS
|
||||
if (points.shoulder.y < points.cbHps.y) points.shoulder = new Point(points.shoulder.x, 0)
|
||||
// Center back (cb) vertical axis
|
||||
points.cbHps = new Point(0, 0)
|
||||
points.cbNeck = new Point(0, options.backNeckCutout * measurements.neck)
|
||||
points.cbWaist = new Point(0, measurements.hpsToWaistBack)
|
||||
points.cbHips = new Point(0, points.cbWaist.y + measurements.waistToHips)
|
||||
|
||||
points.cbHem = new Point(0, points.cbHips.y * (1 + options.lengthBonus))
|
||||
|
||||
// Side back (cb) vertical axis
|
||||
points.armhole = new Point((measurements.chest * (1 + options.chestEase)) / 4, points.cbArmhole.y)
|
||||
points.waist = new Point(points.armhole.x, points.cbWaist.y)
|
||||
points.hips = new Point(points.armhole.x, points.cbHips.y)
|
||||
points.hem = new Point(points.armhole.x, points.cbHem.y)
|
||||
|
||||
// Armhhole
|
||||
points.armholePitch = new Point(
|
||||
(measurements.shoulderToShoulder * options.acrossBackFactor) / 2 +
|
||||
store.get('shoulderEase') / 2,
|
||||
points.shoulder.y + points.shoulder.dy(points.armhole) / 2
|
||||
)
|
||||
// Set both an front and back armhole pitch point
|
||||
// but keep 'armholePitch' for backwards compatibility
|
||||
points.backArmholePitch = points.armholePitch.clone()
|
||||
points.frontArmholePitch = points.armholePitch.clone() // will be overwritten below
|
||||
// Armhole hollow
|
||||
points._tmp1 = new Point(points.armholePitch.x, points.armhole.y)
|
||||
points._tmp2 = points._tmp1.shift(45, 10)
|
||||
points._tmp3 = utils.beamsIntersect(
|
||||
points._tmp1,
|
||||
points._tmp2,
|
||||
points.armhole,
|
||||
points.armholePitch
|
||||
)
|
||||
points.armholeHollow = points._tmp1.shiftFractionTowards(points._tmp3, 0.5)
|
||||
points.armholeCp2 = points.armhole.shift(180, points._tmp1.dx(points.armhole) / 4)
|
||||
points.armholeHollowCp1 = points.armholeHollow.shift(
|
||||
-45,
|
||||
points.armholeHollow.dy(points.armhole) / 2
|
||||
)
|
||||
points.armholeHollowCp2 = points.armholeHollow.shift(
|
||||
135,
|
||||
points.armholePitch.dx(points.armholeHollow)
|
||||
)
|
||||
points.armholePitchCp1 = points.armholePitch.shift(
|
||||
-90,
|
||||
points.armholePitch.dy(points.armholeHollow) / 2
|
||||
)
|
||||
points.backArmholePitchCp1 = points.armholePitchCp1.clone()
|
||||
points.frontArmholePitchCp1 = points.armholePitchCp1.clone() // will be overwritten below
|
||||
points.armholePitchCp2 = points.armholePitch.shift(
|
||||
90,
|
||||
points.shoulder.dy(points.armholePitch) / 2
|
||||
)
|
||||
points.backArmholePitchCp2 = points.armholePitchCp2.clone()
|
||||
points.frontArmholePitchCp2 = points.armholePitchCp2.clone() // will be overwritten below
|
||||
points.shoulderCp1 = points.shoulder
|
||||
.shiftTowards(points.neck, points.shoulder.dy(points.armholePitch) / 5)
|
||||
.rotate(90, points.shoulder)
|
||||
|
||||
// Neck opening (back)
|
||||
points._tmp4 = points.neck.shiftTowards(points.shoulder, 10).rotate(-90, points.neck)
|
||||
points.neckCp2 = utils.beamIntersectsY(points.neck, points._tmp4, points.cbNeck.y)
|
||||
|
||||
// Fit collar
|
||||
points.cfNeck = points.neck.rotate(-90, new Point(0, 0))
|
||||
let target = measurements.neck * (1 + options.collarEase)
|
||||
let delta = 0
|
||||
let run = 0
|
||||
do {
|
||||
run++
|
||||
points.cfNeck = points.cfNeck.shift(90, delta / 3)
|
||||
points.frontNeckCpEdge = utils.beamsIntersect(
|
||||
points.neck,
|
||||
points.neckCp2,
|
||||
points.cfNeck,
|
||||
new Point(20, points.cfNeck.y)
|
||||
// Shoulder line
|
||||
points.neck = new Point((measurements.neck * (1 + options.collarEase)) / options.collarFactor, 0)
|
||||
points.hps = points.neck.clone() // We started using HPS in many measurements
|
||||
// Shoulder point using shoulderSlope degree measurement
|
||||
points.shoulder = utils.beamsIntersect(
|
||||
points.hps,
|
||||
points.hps.shift(measurements.shoulderSlope * -1, 100),
|
||||
new Point(measurements.shoulderToShoulder / 2 + store.get('shoulderEase'), -100),
|
||||
new Point(measurements.shoulderToShoulder / 2 + store.get('shoulderEase'), 100)
|
||||
)
|
||||
// Determine armhole depth and cbShoulder independent of shoulder slope reduction
|
||||
points.cbShoulder = new Point(0, points.shoulder.y)
|
||||
points.cbArmhole = new Point(
|
||||
0,
|
||||
points.shoulder.y + measurements.biceps * (1 + options.bicepsEase) * options.armholeDepthFactor
|
||||
)
|
||||
points.cfNeckCp1 = points.cfNeck.shiftFractionTowards(points.frontNeckCpEdge, 0.55)
|
||||
points.neckCp2Front = points.neck.shiftFractionTowards(points.frontNeckCpEdge, 0.65)
|
||||
paths.neckOpening = new Path()
|
||||
.move(points.cfNeck)
|
||||
.curve(points.cfNeckCp1, points.neckCp2Front, points.neck)
|
||||
.curve(points.neckCp2, points.cbNeck, points.cbNeck)
|
||||
.attr('class', 'dashed stroke-xl various')
|
||||
delta = paths.neckOpening.length() * 2 - target
|
||||
} while (Math.abs(delta) > 1 && options.brianFitCollar && run < 10)
|
||||
delete paths.neckOpening
|
||||
|
||||
// Anchor point for sampling
|
||||
points.gridAnchor = points.cbHem
|
||||
// Now take shoulder slope reduction into account
|
||||
points.shoulder.y -= (points.shoulder.y - points.cbHps.y) * options.shoulderSlopeReduction
|
||||
// Shoulder should never be higher than HPS
|
||||
if (points.shoulder.y < points.cbHps.y) points.shoulder = new Point(points.shoulder.x, 0)
|
||||
|
||||
/*
|
||||
* People would like to have the option to shift the shoulder seam
|
||||
* See https://github.com/freesewing/freesewing/issues/642
|
||||
* So let's make the people happy
|
||||
*/
|
||||
// Front armhole is a bit deeper, add those points
|
||||
let deeper = measurements.chest * options.frontArmholeDeeper
|
||||
for (const p of ['', 'Cp1', 'Cp2']) {
|
||||
points[`frontArmholePitch${p}`] = points[`armholePitch${p}`].shift(180, deeper)
|
||||
points.cbHem = new Point(0, points.cbHips.y * (1 + options.lengthBonus))
|
||||
|
||||
// Side back (cb) vertical axis
|
||||
points.armhole = new Point((measurements.chest * (1 + options.chestEase)) / 4, points.cbArmhole.y)
|
||||
points.waist = new Point(points.armhole.x, points.cbWaist.y)
|
||||
points.hips = new Point(points.armhole.x, points.cbHips.y)
|
||||
points.hem = new Point(points.armhole.x, points.cbHem.y)
|
||||
|
||||
// Armhhole
|
||||
points.armholePitch = new Point(
|
||||
(measurements.shoulderToShoulder * options.acrossBackFactor) / 2 +
|
||||
store.get('shoulderEase') / 2,
|
||||
points.shoulder.y + points.shoulder.dy(points.armhole) / 2
|
||||
)
|
||||
// Set both an front and back armhole pitch point
|
||||
// but keep 'armholePitch' for backwards compatibility
|
||||
points.backArmholePitch = points.armholePitch.clone()
|
||||
points.frontArmholePitch = points.armholePitch.clone() // will be overwritten below
|
||||
// Armhole hollow
|
||||
points._tmp1 = new Point(points.armholePitch.x, points.armhole.y)
|
||||
points._tmp2 = points._tmp1.shift(45, 10)
|
||||
points._tmp3 = utils.beamsIntersect(
|
||||
points._tmp1,
|
||||
points._tmp2,
|
||||
points.armhole,
|
||||
points.armholePitch
|
||||
)
|
||||
points.armholeHollow = points._tmp1.shiftFractionTowards(points._tmp3, 0.5)
|
||||
points.armholeCp2 = points.armhole.shift(180, points._tmp1.dx(points.armhole) / 4)
|
||||
points.armholeHollowCp1 = points.armholeHollow.shift(
|
||||
-45,
|
||||
points.armholeHollow.dy(points.armhole) / 2
|
||||
)
|
||||
points.armholeHollowCp2 = points.armholeHollow.shift(
|
||||
135,
|
||||
points.armholePitch.dx(points.armholeHollow)
|
||||
)
|
||||
points.armholePitchCp1 = points.armholePitch.shift(
|
||||
-90,
|
||||
points.armholePitch.dy(points.armholeHollow) / 2
|
||||
)
|
||||
points.backArmholePitchCp1 = points.armholePitchCp1.clone()
|
||||
points.frontArmholePitchCp1 = points.armholePitchCp1.clone() // will be overwritten below
|
||||
points.armholePitchCp2 = points.armholePitch.shift(
|
||||
90,
|
||||
points.shoulder.dy(points.armholePitch) / 2
|
||||
)
|
||||
points.backArmholePitchCp2 = points.armholePitchCp2.clone()
|
||||
points.frontArmholePitchCp2 = points.armholePitchCp2.clone() // will be overwritten below
|
||||
points.shoulderCp1 = points.shoulder
|
||||
.shiftTowards(points.neck, points.shoulder.dy(points.armholePitch) / 5)
|
||||
.rotate(90, points.shoulder)
|
||||
|
||||
// Neck opening (back)
|
||||
points._tmp4 = points.neck.shiftTowards(points.shoulder, 10).rotate(-90, points.neck)
|
||||
points.neckCp2 = utils.beamIntersectsY(points.neck, points._tmp4, points.cbNeck.y)
|
||||
|
||||
// Fit collar
|
||||
points.cfNeck = points.neck.rotate(-90, new Point(0, 0))
|
||||
let target = measurements.neck * (1 + options.collarEase)
|
||||
let delta = 0
|
||||
let run = 0
|
||||
do {
|
||||
run++
|
||||
points.cfNeck = points.cfNeck.shift(90, delta / 3)
|
||||
points.frontNeckCpEdge = utils.beamsIntersect(
|
||||
points.neck,
|
||||
points.neckCp2,
|
||||
points.cfNeck,
|
||||
new Point(20, points.cfNeck.y)
|
||||
)
|
||||
points.cfNeckCp1 = points.cfNeck.shiftFractionTowards(points.frontNeckCpEdge, 0.55)
|
||||
points.neckCp2Front = points.neck.shiftFractionTowards(points.frontNeckCpEdge, 0.65)
|
||||
paths.neckOpening = new Path()
|
||||
.move(points.cfNeck)
|
||||
.curve(points.cfNeckCp1, points.neckCp2Front, points.neck)
|
||||
.curve(points.neckCp2, points.cbNeck, points.cbNeck)
|
||||
.attr('class', 'dashed stroke-xl various')
|
||||
delta = paths.neckOpening.length() * 2 - target
|
||||
} while (Math.abs(delta) > 1 && options.brianFitCollar && run < 10)
|
||||
delete paths.neckOpening
|
||||
|
||||
// Anchor point for sampling
|
||||
points.gridAnchor = points.cbHem
|
||||
|
||||
/*
|
||||
* People would like to have the option to shift the shoulder seam
|
||||
* See https://github.com/freesewing/freesewing/issues/642
|
||||
* So let's make the people happy
|
||||
*/
|
||||
// Front armhole is a bit deeper, add those points
|
||||
let deeper = measurements.chest * options.frontArmholeDeeper
|
||||
for (const p of ['', 'Cp1', 'Cp2']) {
|
||||
points[`frontArmholePitch${p}`] = points[`armholePitch${p}`].shift(180, deeper)
|
||||
}
|
||||
// Add points needed for the mirrored front&back neck/armhole path
|
||||
macro('mirror', {
|
||||
mirror: [points.hps, points.shoulder],
|
||||
points: [
|
||||
points.neckCp2Front,
|
||||
points.cfNeckCp1,
|
||||
points.cfNeck,
|
||||
points.cbNeck,
|
||||
points.neckCp2,
|
||||
points.frontArmholePitch,
|
||||
points.frontArmholePitchCp2,
|
||||
points.shoulderCp1,
|
||||
],
|
||||
clone: true,
|
||||
})
|
||||
|
||||
// How much space do we have to work with here?
|
||||
// s3 = ShoulderSeamShift
|
||||
store.set('s3CollarMaxFront', points.hps.dy(points.cfNeck) / 2)
|
||||
store.set('s3CollarMaxBack', points.hps.dy(points.cbNeck) / 2)
|
||||
store.set('s3ArmholeMax', points.shoulder.dy(points.frontArmholePitch) / 4)
|
||||
// Let's leave the actual splitting the curves for the front/back parts
|
||||
|
||||
// Complete pattern?
|
||||
if (complete) {
|
||||
points.title = new Point(points.armholePitch.x / 2, points.armholePitch.y)
|
||||
points.logo = points.title.shift(-90, 100)
|
||||
snippets.logo = new Snippet('logo', points.logo)
|
||||
}
|
||||
|
||||
return part
|
||||
}
|
||||
// Add points needed for the mirrored front&back neck/armhole path
|
||||
macro('mirror', {
|
||||
mirror: [points.hps, points.shoulder],
|
||||
points: [
|
||||
points.neckCp2Front,
|
||||
points.cfNeckCp1,
|
||||
points.cfNeck,
|
||||
points.cbNeck,
|
||||
points.neckCp2,
|
||||
points.frontArmholePitch,
|
||||
points.frontArmholePitchCp2,
|
||||
points.shoulderCp1,
|
||||
],
|
||||
clone: true,
|
||||
})
|
||||
|
||||
// How much space do we have to work with here?
|
||||
// s3 = ShoulderSeamShift
|
||||
store.set('s3CollarMaxFront', points.hps.dy(points.cfNeck) / 2)
|
||||
store.set('s3CollarMaxBack', points.hps.dy(points.cbNeck) / 2)
|
||||
store.set('s3ArmholeMax', points.shoulder.dy(points.frontArmholePitch) / 4)
|
||||
// Let's leave the actual splitting the curves for the front/back parts
|
||||
|
||||
// Complete pattern?
|
||||
if (complete) {
|
||||
points.title = new Point(points.armholePitch.x / 2, points.armholePitch.y)
|
||||
points.logo = points.title.shift(-90, 100)
|
||||
snippets.logo = new Snippet('logo', points.logo)
|
||||
}
|
||||
|
||||
return part
|
||||
}
|
||||
|
|
|
@ -1,193 +1,198 @@
|
|||
import * as shared from './shared'
|
||||
import back from './back'
|
||||
|
||||
export default (part) => {
|
||||
let {
|
||||
store,
|
||||
sa,
|
||||
Point,
|
||||
points,
|
||||
Path,
|
||||
paths,
|
||||
Snippet,
|
||||
snippets,
|
||||
options,
|
||||
complete,
|
||||
paperless,
|
||||
macro,
|
||||
utils,
|
||||
} = part.shorthand()
|
||||
export default {
|
||||
from: back,
|
||||
name: 'front',
|
||||
draft: (part) => {
|
||||
const {
|
||||
store,
|
||||
sa,
|
||||
Point,
|
||||
points,
|
||||
Path,
|
||||
paths,
|
||||
Snippet,
|
||||
snippets,
|
||||
options,
|
||||
complete,
|
||||
paperless,
|
||||
macro,
|
||||
utils,
|
||||
} = part.shorthand()
|
||||
|
||||
// Re-use points for deeper armhole at the front
|
||||
points.armholePitchCp1 = points.frontArmholePitchCp1
|
||||
points.armholePitch = points.frontArmholePitch
|
||||
points.armholePitchCp2 = points.frontArmholePitchCp2
|
||||
// Re-use points for deeper armhole at the front
|
||||
points.armholePitchCp1 = points.frontArmholePitchCp1
|
||||
points.armholePitch = points.frontArmholePitch
|
||||
points.armholePitchCp2 = points.frontArmholePitchCp2
|
||||
|
||||
// Adapt the shoulder line according to the relevant options
|
||||
// Don't bother with less than 10% as that's just asking for trouble
|
||||
if (options.s3Collar < 0.1 && options.s3Collar > -0.1) {
|
||||
points.s3CollarSplit = points.hps
|
||||
paths.frontCollar = new Path()
|
||||
.move(points.hps)
|
||||
.curve(points.neckCp2Front, points.cfNeckCp1, points.cfNeck)
|
||||
.setRender(false)
|
||||
} else if (options.s3Collar > 0) {
|
||||
// Shift shoulder seam forward on the collar side
|
||||
points.s3CollarSplit = utils.curveIntersectsY(
|
||||
points.hps,
|
||||
points.neckCp2Front,
|
||||
points.cfNeckCp1,
|
||||
points.cfNeck,
|
||||
store.get('s3CollarMaxFront') * options.s3Collar
|
||||
)
|
||||
paths.frontCollar = new Path()
|
||||
.move(points.hps)
|
||||
.curve(points.neckCp2Front, points.cfNeckCp1, points.cfNeck)
|
||||
.split(points.s3CollarSplit)[1]
|
||||
.setRender(false)
|
||||
} else if (options.s3Collar < 0) {
|
||||
// Shift shoulder seam backward on the collar side
|
||||
points.s3CollarSplit = utils.curveIntersectsY(
|
||||
points.mirroredCbNeck,
|
||||
points.mirroredCbNeck,
|
||||
points.mirroredNeckCp2,
|
||||
points.hps,
|
||||
store.get('s3CollarMaxBack') * options.s3Collar
|
||||
)
|
||||
paths.frontCollar = new Path()
|
||||
.move(points.hps)
|
||||
.curve_(points.mirroredNeckCp2, points.mirroredCbNeck)
|
||||
.split(points.s3CollarSplit)[0]
|
||||
.reverse()
|
||||
.join(new Path().move(points.hps).curve(points.neckCp2Front, points.cfNeckCp1, points.cfNeck))
|
||||
.setRender(false)
|
||||
}
|
||||
if (options.s3Armhole < 0.1 && options.s3Armhole > -0.1) {
|
||||
points.s3ArmholeSplit = points.shoulder
|
||||
paths.frontArmhole = new Path()
|
||||
.move(points.armholePitch)
|
||||
.curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder)
|
||||
.setRender(false)
|
||||
} else if (options.s3Armhole > 0) {
|
||||
// Shift shoulder seam forward on the armhole side
|
||||
points.s3ArmholeSplit = utils.curveIntersectsY(
|
||||
points.shoulder,
|
||||
points.shoulderCp1,
|
||||
points.armholePitchCp2,
|
||||
points.armholePitch,
|
||||
store.get('s3ArmholeMax') * options.s3Armhole + points.shoulder.y
|
||||
)
|
||||
paths.frontArmhole = new Path()
|
||||
.move(points.armholePitch)
|
||||
.curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder)
|
||||
.split(points.s3ArmholeSplit)[0]
|
||||
.setRender(false)
|
||||
} else if (options.s3Armhole < 0) {
|
||||
// Shift shoulder seam forward on the armhole side
|
||||
points.s3ArmholeSplit = utils.curveIntersectsY(
|
||||
points.shoulder,
|
||||
points.mirroredShoulderCp1,
|
||||
points.mirroredFrontArmholePitchCp2,
|
||||
points.mirroredFrontArmholePitch,
|
||||
store.get('s3ArmholeMax') * options.s3Armhole + points.shoulder.y
|
||||
)
|
||||
paths.frontArmhole = new Path()
|
||||
.move(points.armholePitch)
|
||||
.curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder)
|
||||
.join(
|
||||
new Path()
|
||||
.move(points.shoulder)
|
||||
.curve(
|
||||
points.mirroredShoulderCp1,
|
||||
points.mirroredFrontArmholePitchCp2,
|
||||
points.mirroredFrontArmholePitch
|
||||
)
|
||||
.split(points.s3ArmholeSplit)[0]
|
||||
// Adapt the shoulder line according to the relevant options
|
||||
// Don't bother with less than 10% as that's just asking for trouble
|
||||
if (options.s3Collar < 0.1 && options.s3Collar > -0.1) {
|
||||
points.s3CollarSplit = points.hps
|
||||
paths.frontCollar = new Path()
|
||||
.move(points.hps)
|
||||
.curve(points.neckCp2Front, points.cfNeckCp1, points.cfNeck)
|
||||
.setRender(false)
|
||||
} else if (options.s3Collar > 0) {
|
||||
// Shift shoulder seam forward on the collar side
|
||||
points.s3CollarSplit = utils.curveIntersectsY(
|
||||
points.hps,
|
||||
points.neckCp2Front,
|
||||
points.cfNeckCp1,
|
||||
points.cfNeck,
|
||||
store.get('s3CollarMaxFront') * options.s3Collar
|
||||
)
|
||||
.setRender(false)
|
||||
}
|
||||
|
||||
// Rename cb (center back) to cf (center front)
|
||||
for (let key of ['Shoulder', 'Armhole', 'Waist', 'Hips', 'Hem']) {
|
||||
points[`cf${key}`] = new Point(points[`cb${key}`].x, points[`cb${key}`].y)
|
||||
delete points[`cb${key}`]
|
||||
}
|
||||
// Front neckline points
|
||||
points.neckCp2 = new Point(points.neckCp2Front.x, points.neckCp2Front.y)
|
||||
|
||||
// Seamline
|
||||
paths.saBase = new Path()
|
||||
.move(points.cfHem)
|
||||
.line(points.hem)
|
||||
.line(points.armhole)
|
||||
.curve(points.armholeCp2, points.armholeHollowCp1, points.armholeHollow)
|
||||
.curve(points.armholeHollowCp2, points.armholePitchCp1, points.armholePitch)
|
||||
.join(paths.frontArmhole)
|
||||
.line(points.s3CollarSplit)
|
||||
.join(paths.frontCollar)
|
||||
|
||||
paths.saBase.render = false
|
||||
paths.seam = new Path()
|
||||
.move(points.cfNeck)
|
||||
.line(points.cfHem)
|
||||
.join(paths.saBase)
|
||||
.attr('class', 'fabric')
|
||||
|
||||
// Store lengths to fit sleeve
|
||||
store.set('frontArmholeLength', shared.armholeLength(points, Path))
|
||||
store.set('frontArmholeToArmholePitch', shared.armholeToArmholePitch(points, Path))
|
||||
|
||||
// Complete pattern?
|
||||
if (complete) {
|
||||
macro('cutonfold', {
|
||||
from: points.cfNeck,
|
||||
to: points.cfHips,
|
||||
grainline: true,
|
||||
})
|
||||
macro('title', { at: points.title, nr: 1, title: 'front' })
|
||||
snippets.armholePitchNotch = new Snippet('notch', points.armholePitch)
|
||||
paths.waist = new Path().move(points.cfWaist).line(points.waist).attr('class', 'help')
|
||||
if (sa) {
|
||||
paths.sa = paths.saBase
|
||||
.offset(sa)
|
||||
.attr('class', 'fabric sa')
|
||||
.line(points.cfNeck)
|
||||
.move(points.cfHips)
|
||||
paths.sa.line(paths.sa.start())
|
||||
paths.frontCollar = new Path()
|
||||
.move(points.hps)
|
||||
.curve(points.neckCp2Front, points.cfNeckCp1, points.cfNeck)
|
||||
.split(points.s3CollarSplit)[1]
|
||||
.setRender(false)
|
||||
} else if (options.s3Collar < 0) {
|
||||
// Shift shoulder seam backward on the collar side
|
||||
points.s3CollarSplit = utils.curveIntersectsY(
|
||||
points.mirroredCbNeck,
|
||||
points.mirroredCbNeck,
|
||||
points.mirroredNeckCp2,
|
||||
points.hps,
|
||||
store.get('s3CollarMaxBack') * options.s3Collar
|
||||
)
|
||||
paths.frontCollar = new Path()
|
||||
.move(points.hps)
|
||||
.curve_(points.mirroredNeckCp2, points.mirroredCbNeck)
|
||||
.split(points.s3CollarSplit)[0]
|
||||
.reverse()
|
||||
.join(new Path().move(points.hps).curve(points.neckCp2Front, points.cfNeckCp1, points.cfNeck))
|
||||
.setRender(false)
|
||||
}
|
||||
if (options.s3Armhole < 0.1 && options.s3Armhole > -0.1) {
|
||||
points.s3ArmholeSplit = points.shoulder
|
||||
paths.frontArmhole = new Path()
|
||||
.move(points.armholePitch)
|
||||
.curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder)
|
||||
.setRender(false)
|
||||
} else if (options.s3Armhole > 0) {
|
||||
// Shift shoulder seam forward on the armhole side
|
||||
points.s3ArmholeSplit = utils.curveIntersectsY(
|
||||
points.shoulder,
|
||||
points.shoulderCp1,
|
||||
points.armholePitchCp2,
|
||||
points.armholePitch,
|
||||
store.get('s3ArmholeMax') * options.s3Armhole + points.shoulder.y
|
||||
)
|
||||
paths.frontArmhole = new Path()
|
||||
.move(points.armholePitch)
|
||||
.curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder)
|
||||
.split(points.s3ArmholeSplit)[0]
|
||||
.setRender(false)
|
||||
} else if (options.s3Armhole < 0) {
|
||||
// Shift shoulder seam forward on the armhole side
|
||||
points.s3ArmholeSplit = utils.curveIntersectsY(
|
||||
points.shoulder,
|
||||
points.mirroredShoulderCp1,
|
||||
points.mirroredFrontArmholePitchCp2,
|
||||
points.mirroredFrontArmholePitch,
|
||||
store.get('s3ArmholeMax') * options.s3Armhole + points.shoulder.y
|
||||
)
|
||||
paths.frontArmhole = new Path()
|
||||
.move(points.armholePitch)
|
||||
.curve(points.armholePitchCp2, points.shoulderCp1, points.shoulder)
|
||||
.join(
|
||||
new Path()
|
||||
.move(points.shoulder)
|
||||
.curve(
|
||||
points.mirroredShoulderCp1,
|
||||
points.mirroredFrontArmholePitchCp2,
|
||||
points.mirroredFrontArmholePitch
|
||||
)
|
||||
.split(points.s3ArmholeSplit)[0]
|
||||
)
|
||||
.setRender(false)
|
||||
}
|
||||
|
||||
// Add notches if the shoulder seam is shifted
|
||||
shared.s3Notches(part, 'notch')
|
||||
}
|
||||
// Rename cb (center back) to cf (center front)
|
||||
for (let key of ['Shoulder', 'Armhole', 'Waist', 'Hips', 'Hem']) {
|
||||
points[`cf${key}`] = new Point(points[`cb${key}`].x, points[`cb${key}`].y)
|
||||
delete points[`cb${key}`]
|
||||
}
|
||||
// Front neckline points
|
||||
points.neckCp2 = new Point(points.neckCp2Front.x, points.neckCp2Front.y)
|
||||
|
||||
// Paperless?
|
||||
if (paperless) {
|
||||
shared.dimensions(part, 'front')
|
||||
macro('hd', {
|
||||
from: points.cfHips,
|
||||
to: points.hips,
|
||||
y: points.hem.y + sa + 15,
|
||||
})
|
||||
macro('vd', {
|
||||
from: points.cfHem,
|
||||
to: points.cfWaist,
|
||||
x: points.cfHips.x - sa - 15,
|
||||
})
|
||||
macro('vd', {
|
||||
from: points.cfHem,
|
||||
to: points.cfNeck,
|
||||
x: points.cfHips.x - sa - 30,
|
||||
})
|
||||
macro('hd', {
|
||||
from: points.cfNeck,
|
||||
to: points.s3CollarSplit,
|
||||
y: points.s3CollarSplit.y - sa - 15,
|
||||
})
|
||||
macro('hd', {
|
||||
from: points.cfNeck,
|
||||
to: points.s3ArmholeSplit,
|
||||
y: points.s3CollarSplit.y - sa - 30,
|
||||
})
|
||||
}
|
||||
// Seamline
|
||||
paths.saBase = new Path()
|
||||
.move(points.cfHem)
|
||||
.line(points.hem)
|
||||
.line(points.armhole)
|
||||
.curve(points.armholeCp2, points.armholeHollowCp1, points.armholeHollow)
|
||||
.curve(points.armholeHollowCp2, points.armholePitchCp1, points.armholePitch)
|
||||
.join(paths.frontArmhole)
|
||||
.line(points.s3CollarSplit)
|
||||
.join(paths.frontCollar)
|
||||
|
||||
return part
|
||||
paths.saBase.render = false
|
||||
paths.seam = new Path()
|
||||
.move(points.cfNeck)
|
||||
.line(points.cfHem)
|
||||
.join(paths.saBase)
|
||||
.attr('class', 'fabric')
|
||||
|
||||
// Store lengths to fit sleeve
|
||||
store.set('frontArmholeLength', shared.armholeLength(points, Path))
|
||||
store.set('frontArmholeToArmholePitch', shared.armholeToArmholePitch(points, Path))
|
||||
|
||||
// Complete pattern?
|
||||
if (complete) {
|
||||
macro('cutonfold', {
|
||||
from: points.cfNeck,
|
||||
to: points.cfHips,
|
||||
grainline: true,
|
||||
})
|
||||
macro('title', { at: points.title, nr: 1, title: 'front' })
|
||||
snippets.armholePitchNotch = new Snippet('notch', points.armholePitch)
|
||||
paths.waist = new Path().move(points.cfWaist).line(points.waist).attr('class', 'help')
|
||||
if (sa) {
|
||||
paths.sa = paths.saBase
|
||||
.offset(sa)
|
||||
.attr('class', 'fabric sa')
|
||||
.line(points.cfNeck)
|
||||
.move(points.cfHips)
|
||||
paths.sa.line(paths.sa.start())
|
||||
}
|
||||
|
||||
// Add notches if the shoulder seam is shifted
|
||||
shared.s3Notches(part, 'notch')
|
||||
}
|
||||
|
||||
// Paperless?
|
||||
if (paperless) {
|
||||
shared.dimensions(part, 'front')
|
||||
macro('hd', {
|
||||
from: points.cfHips,
|
||||
to: points.hips,
|
||||
y: points.hem.y + sa + 15,
|
||||
})
|
||||
macro('vd', {
|
||||
from: points.cfHem,
|
||||
to: points.cfWaist,
|
||||
x: points.cfHips.x - sa - 15,
|
||||
})
|
||||
macro('vd', {
|
||||
from: points.cfHem,
|
||||
to: points.cfNeck,
|
||||
x: points.cfHips.x - sa - 30,
|
||||
})
|
||||
macro('hd', {
|
||||
from: points.cfNeck,
|
||||
to: points.s3CollarSplit,
|
||||
y: points.s3CollarSplit.y - sa - 15,
|
||||
})
|
||||
macro('hd', {
|
||||
from: points.cfNeck,
|
||||
to: points.s3ArmholeSplit,
|
||||
y: points.s3CollarSplit.y - sa - 30,
|
||||
})
|
||||
}
|
||||
|
||||
return part
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,38 @@
|
|||
// FreeSewing core library
|
||||
import freesewing from '@freesewing/core'
|
||||
import plugins from '@freesewing/plugin-bundle'
|
||||
import plugin from '@freesewing/plugin-bust' // Note: conditional plugin
|
||||
import config from '../config'
|
||||
// Parts
|
||||
import draftBase from './base'
|
||||
import draftBack from './back'
|
||||
import draftFront from './front'
|
||||
import draftSleevecap from './sleevecap'
|
||||
import draftSleeve from './sleeve'
|
||||
// FreeSewing Plugins
|
||||
import pluginBundle from '@freesewing/plugin-bundle'
|
||||
import bustPlugin from '@freesewing/plugin-bust' // Note: conditional plugin
|
||||
// Design config & options
|
||||
import { info, measurements, optionalMeasurements } from '../config/index'
|
||||
//import * as options from '../config/options'
|
||||
// Design parts
|
||||
import back from './back'
|
||||
import front from './front'
|
||||
import sleeve from './sleeve'
|
||||
// These are only here to be exported
|
||||
import base from './base'
|
||||
import sleevecap from './sleevecap'
|
||||
|
||||
/* Check to see whether we should load the bust plugin
|
||||
* Only of the `draftForHighBust` options is set
|
||||
* AND the highBust measurement is available
|
||||
*/
|
||||
const condition = (settings = false) =>
|
||||
settings &&
|
||||
settings.options &&
|
||||
settings.options.draftForHighBust &&
|
||||
settings.measurements.highBust
|
||||
? true
|
||||
: false
|
||||
|
||||
// Create design
|
||||
const Brian = new freesewing.Design(config, plugins, { plugin, condition })
|
||||
|
||||
// Attach draft methods to prototype
|
||||
Brian.prototype.draftBase = draftBase
|
||||
Brian.prototype.draftBack = draftBack
|
||||
Brian.prototype.draftFront = draftFront
|
||||
Brian.prototype.draftSleevecap = draftSleevecap
|
||||
Brian.prototype.draftSleeve = draftSleeve
|
||||
// Setup design
|
||||
const Brian = new freesewing.Design({
|
||||
...info,
|
||||
measurements,
|
||||
optionalMeasurements,
|
||||
// options: { ...options },
|
||||
parts: { back, front, sleeve },
|
||||
plugins: pluginBundle,
|
||||
conditionalPlugins: {
|
||||
plugin: bustPlugin,
|
||||
condition: (settings=false) =>
|
||||
settings?.options?.draftForHighBust &&
|
||||
settings?.measurements?.highBust
|
||||
? true : false
|
||||
}
|
||||
})
|
||||
|
||||
// Named exports
|
||||
export { config, Brian }
|
||||
|
||||
export { back, front, sleeve, base, sleevecap }
|
||||
// Default export
|
||||
export default Brian
|
||||
|
|
|
@ -1,89 +1,103 @@
|
|||
export default (part) => {
|
||||
const {
|
||||
store,
|
||||
sa,
|
||||
measurements,
|
||||
options,
|
||||
Point,
|
||||
points,
|
||||
Path,
|
||||
paths,
|
||||
Snippet,
|
||||
snippets,
|
||||
complete,
|
||||
paperless,
|
||||
macro,
|
||||
} = part.shorthand()
|
||||
import sleevecap from './sleevecap'
|
||||
import { _sleeve as options } from '../config/options.js'
|
||||
|
||||
// Determine the sleeve length
|
||||
store.set('sleeveLength', measurements.shoulderToWrist * (1 + options.sleeveLengthBonus))
|
||||
points.sleeveTip = paths.sleevecap.edge('top')
|
||||
points.sleeveTop = new Point(0, points.sleeveTip.y) // Always in center
|
||||
export default {
|
||||
from: sleevecap,
|
||||
name: 'sleeve',
|
||||
options,
|
||||
draft: (part) => {
|
||||
const {
|
||||
store,
|
||||
sa,
|
||||
measurements,
|
||||
options,
|
||||
Point,
|
||||
points,
|
||||
Path,
|
||||
paths,
|
||||
Snippet,
|
||||
snippets,
|
||||
complete,
|
||||
paperless,
|
||||
macro,
|
||||
} = part.shorthand()
|
||||
|
||||
// Wrist
|
||||
points.centerWrist = points.sleeveTop.shift(-90, store.get('sleeveLength'))
|
||||
points.wristRight = points.centerWrist.shift(0, (measurements.wrist * (1 + options.cuffEase)) / 2)
|
||||
points.wristLeft = points.wristRight.rotate(180, points.centerWrist)
|
||||
// Remove things inherited
|
||||
macro('cutonfold', false)
|
||||
macro('rmad')
|
||||
delete paths.waist
|
||||
for (const key in snippets) delete snippets[key]
|
||||
|
||||
// Paths
|
||||
paths.sleevecap.render = false
|
||||
paths.seam = new Path()
|
||||
.move(points.bicepsLeft)
|
||||
.move(points.wristLeft)
|
||||
.move(points.wristRight)
|
||||
.line(points.bicepsRight)
|
||||
.join(paths.sleevecap)
|
||||
.close()
|
||||
.attr('class', 'fabric')
|
||||
// Determine the sleeve length
|
||||
store.set('sleeveLength', measurements.shoulderToWrist * (1 + options.sleeveLengthBonus))
|
||||
points.sleeveTip = paths.sleevecap.edge('top')
|
||||
points.sleeveTop = new Point(0, points.sleeveTip.y) // Always in center
|
||||
|
||||
// Anchor point for sampling
|
||||
points.gridAnchor = new Point(0, 0)
|
||||
// Wrist
|
||||
points.centerWrist = points.sleeveTop.shift(-90, store.get('sleeveLength'))
|
||||
points.wristRight = points.centerWrist.shift(0, (measurements.wrist * (1 + options.cuffEase)) / 2)
|
||||
points.wristLeft = points.wristRight.rotate(180, points.centerWrist)
|
||||
|
||||
// Complete pattern?
|
||||
if (complete) {
|
||||
points.logo = points.centerBiceps.shiftFractionTowards(points.centerWrist, 0.3)
|
||||
snippets.logo = new Snippet('logo', points.logo)
|
||||
macro('title', { at: points.centerBiceps, nr: 3, title: 'sleeve' })
|
||||
macro('grainline', { from: points.centerWrist, to: points.centerBiceps })
|
||||
points.scaleboxAnchor = points.scalebox = points.centerBiceps.shiftFractionTowards(
|
||||
points.centerWrist,
|
||||
0.5
|
||||
)
|
||||
macro('scalebox', { at: points.scalebox })
|
||||
// Paths
|
||||
paths.sleevecap.render = false
|
||||
paths.seam = new Path()
|
||||
.move(points.bicepsLeft)
|
||||
.move(points.wristLeft)
|
||||
.move(points.wristRight)
|
||||
.line(points.bicepsRight)
|
||||
.join(paths.sleevecap)
|
||||
.close()
|
||||
.attr('class', 'fabric')
|
||||
|
||||
points.frontNotch = paths.sleevecap.shiftAlong(store.get('frontArmholeToArmholePitch'))
|
||||
points.backNotch = paths.sleevecap.reverse().shiftAlong(store.get('backArmholeToArmholePitch'))
|
||||
snippets.frontNotch = new Snippet('notch', points.frontNotch)
|
||||
snippets.backNotch = new Snippet('bnotch', points.backNotch)
|
||||
if (sa) paths.sa = paths.seam.offset(sa).attr('class', 'fabric sa')
|
||||
// Anchor point for sampling
|
||||
points.gridAnchor = new Point(0, 0)
|
||||
|
||||
// Complete pattern?
|
||||
if (complete) {
|
||||
points.logo = points.centerBiceps.shiftFractionTowards(points.centerWrist, 0.3)
|
||||
snippets.logo = new Snippet('logo', points.logo)
|
||||
macro('title', { at: points.centerBiceps, nr: 3, title: 'sleeve' })
|
||||
macro('grainline', { from: points.centerWrist, to: points.centerBiceps })
|
||||
points.scaleboxAnchor = points.scalebox = points.centerBiceps.shiftFractionTowards(
|
||||
points.centerWrist,
|
||||
0.5
|
||||
)
|
||||
macro('scalebox', { at: points.scalebox })
|
||||
|
||||
points.frontNotch = paths.sleevecap.shiftAlong(store.get('frontArmholeToArmholePitch'))
|
||||
points.backNotch = paths.sleevecap.reverse().shiftAlong(store.get('backArmholeToArmholePitch'))
|
||||
snippets.frontNotch = new Snippet('notch', points.frontNotch)
|
||||
snippets.backNotch = new Snippet('bnotch', points.backNotch)
|
||||
if (sa) paths.sa = paths.seam.offset(sa).attr('class', 'fabric sa')
|
||||
}
|
||||
|
||||
// Paperless?
|
||||
if (paperless) {
|
||||
macro('vd', {
|
||||
from: points.wristLeft,
|
||||
to: points.bicepsLeft,
|
||||
x: points.bicepsLeft.x - sa - 15,
|
||||
})
|
||||
macro('vd', {
|
||||
from: points.wristLeft,
|
||||
to: points.sleeveTip,
|
||||
x: points.bicepsLeft.x - sa - 30,
|
||||
})
|
||||
macro('hd', {
|
||||
from: points.bicepsLeft,
|
||||
to: points.bicepsRight,
|
||||
y: points.sleeveTip.y - sa - 30,
|
||||
})
|
||||
macro('hd', {
|
||||
from: points.wristLeft,
|
||||
to: points.wristRight,
|
||||
y: points.wristLeft.y + sa + 30,
|
||||
})
|
||||
macro('pd', {
|
||||
path: paths.sleevecap.reverse(),
|
||||
d: -1 * sa - 15,
|
||||
})
|
||||
}
|
||||
return part
|
||||
}
|
||||
|
||||
// Paperless?
|
||||
if (paperless) {
|
||||
macro('vd', {
|
||||
from: points.wristLeft,
|
||||
to: points.bicepsLeft,
|
||||
x: points.bicepsLeft.x - sa - 15,
|
||||
})
|
||||
macro('vd', {
|
||||
from: points.wristLeft,
|
||||
to: points.sleeveTip,
|
||||
x: points.bicepsLeft.x - sa - 30,
|
||||
})
|
||||
macro('hd', {
|
||||
from: points.bicepsLeft,
|
||||
to: points.bicepsRight,
|
||||
y: points.sleeveTip.y - sa - 30,
|
||||
})
|
||||
macro('hd', {
|
||||
from: points.wristLeft,
|
||||
to: points.wristRight,
|
||||
y: points.wristLeft.y + sa + 30,
|
||||
})
|
||||
macro('pd', {
|
||||
path: paths.sleevecap.reverse(),
|
||||
d: -1 * sa - 15,
|
||||
})
|
||||
}
|
||||
return part
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import front from './front'
|
||||
import { _sleevecap as options } from '../config/options.js'
|
||||
|
||||
/** Calculates the differece between actual and optimal sleevecap length
|
||||
* Positive values mean sleevecap is longer than armhole
|
||||
*/
|
||||
|
@ -136,25 +139,31 @@ function draftSleevecap(part, run) {
|
|||
}
|
||||
}
|
||||
|
||||
export default (part) => {
|
||||
let { store, units, options, Point, points, paths, raise } = part.shorthand()
|
||||
export default {
|
||||
from: front,
|
||||
name: 'sleevecap',
|
||||
hide: true,
|
||||
options,
|
||||
draft: (part) => {
|
||||
const { store, units, options, Point, points, paths, raise } = part.shorthand()
|
||||
|
||||
store.set('sleeveFactor', 1)
|
||||
let run = 0
|
||||
let delta = 0
|
||||
do {
|
||||
draftSleevecap(part, run)
|
||||
delta = sleevecapDelta(store)
|
||||
sleevecapAdjust(store)
|
||||
run++
|
||||
raise.debug(`Fitting Brian sleevecap. Run ${run}: delta is ${units(delta)}`)
|
||||
} while (options.brianFitSleeve === true && run < 50 && Math.abs(sleevecapDelta(store)) > 2)
|
||||
store.set('sleeveFactor', 1)
|
||||
let run = 0
|
||||
let delta = 0
|
||||
do {
|
||||
draftSleevecap(part, run)
|
||||
delta = sleevecapDelta(store)
|
||||
sleevecapAdjust(store)
|
||||
run++
|
||||
raise.debug(`Fitting Brian sleevecap. Run ${run}: delta is ${units(delta)}`)
|
||||
} while (options.brianFitSleeve === true && run < 50 && Math.abs(sleevecapDelta(store)) > 2)
|
||||
|
||||
// Paths
|
||||
paths.sleevecap.attr('class', 'fabric')
|
||||
// Paths
|
||||
paths.sleevecap.attr('class', 'fabric')
|
||||
|
||||
// Anchor point for sampling
|
||||
points.gridAnchor = new Point(0, 0)
|
||||
// Anchor point for sampling
|
||||
points.gridAnchor = new Point(0, 0)
|
||||
|
||||
return part
|
||||
return part
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,17 @@ import Pattern from './pattern'
|
|||
* So it's sort of a super-constructor
|
||||
*/
|
||||
export default function Design(config, plugins = false, conditionalPlugins = false) {
|
||||
|
||||
// Add part options to config
|
||||
if (!config.options) config.options = {}
|
||||
if (config.parts) {
|
||||
for (const partName in config.parts) {
|
||||
if (config.parts[partName].options) {
|
||||
for (const optionName in config.parts[partName].options) {
|
||||
config.options[optionName] = config.parts[partName].options[optionName]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ensure all options have a hide() method
|
||||
config.options = optionsWithHide(config.options)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { macroName, sampleStyle, capitalize } from './utils'
|
||||
import { macroName, sampleStyle, capitalize, decoratePartDependency } from './utils'
|
||||
import Part from './part'
|
||||
import Point from './point'
|
||||
import Path from './path'
|
||||
|
@ -77,26 +77,12 @@ export default function Pattern(config = { options: {} }) {
|
|||
if (typeof this.config.dependencies === 'undefined') this.config.dependencies = {}
|
||||
if (typeof this.config.inject === 'undefined') this.config.inject = {}
|
||||
if (typeof this.config.hide === 'undefined') this.config.hide = []
|
||||
this.config.resolvedDependencies = this.resolveDependencies(this.config.dependencies)
|
||||
this.config.draftOrder = this.draftOrder(this.config.resolvedDependencies)
|
||||
|
||||
// Convert options
|
||||
for (let i in config.options) {
|
||||
let option = config.options[i]
|
||||
if (typeof option === 'object') {
|
||||
if (typeof option.pct !== 'undefined') this.settings.options[i] = option.pct / 100
|
||||
else if (typeof option.mm !== 'undefined') this.settings.options[i] = option.mm
|
||||
else if (typeof option.deg !== 'undefined') this.settings.options[i] = option.deg
|
||||
else if (typeof option.count !== 'undefined') this.settings.options[i] = option.count
|
||||
else if (typeof option.bool !== 'undefined') this.settings.options[i] = option.bool
|
||||
else if (typeof option.dflt !== 'undefined') this.settings.options[i] = option.dflt
|
||||
else {
|
||||
let err = 'Unknown option type: ' + JSON.stringify(option)
|
||||
this.raise.error(err)
|
||||
throw new Error(err)
|
||||
}
|
||||
} else {
|
||||
this.settings.options[i] = option
|
||||
this.addOptions(this.config.options)
|
||||
if (this.config.parts) {
|
||||
for (const partName in this.config.parts) {
|
||||
if (this.config.parts[partName].options) this.addOptions(this.config.parts[partName].options)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,6 +113,59 @@ export default function Pattern(config = { options: {} }) {
|
|||
}
|
||||
}
|
||||
|
||||
// Converts/adds options
|
||||
Pattern.prototype.addOptions = function(options={}) {
|
||||
for (let i in options) {
|
||||
const option = options[i]
|
||||
if (typeof option === 'object') {
|
||||
if (typeof option.pct !== 'undefined') this.settings.options[i] = option.pct / 100
|
||||
else if (typeof option.mm !== 'undefined') this.settings.options[i] = option.mm
|
||||
else if (typeof option.deg !== 'undefined') this.settings.options[i] = option.deg
|
||||
else if (typeof option.count !== 'undefined') this.settings.options[i] = option.count
|
||||
else if (typeof option.bool !== 'undefined') this.settings.options[i] = option.bool
|
||||
else if (typeof option.dflt !== 'undefined') this.settings.options[i] = option.dflt
|
||||
else {
|
||||
let err = 'Unknown option type: ' + JSON.stringify(option)
|
||||
this.raise.error(err)
|
||||
throw new Error(err)
|
||||
}
|
||||
} else {
|
||||
this.settings.options[i] = option
|
||||
}
|
||||
}
|
||||
|
||||
// Make it chainable
|
||||
return this
|
||||
}
|
||||
|
||||
/*
|
||||
* Defer some things that used to happen in the constructor to
|
||||
* facilitate late-stage adding of parts
|
||||
*/
|
||||
Pattern.prototype.init = function () {
|
||||
// Resolve all dependencies
|
||||
this.dependencies = this.config.dependencies
|
||||
this.inject = this.config.inject
|
||||
this.hide = this.config.hide
|
||||
if (Array.isArray(this.config.parts)) {
|
||||
this.resolvedDependencies = this.resolveDependencies(this.dependencies)
|
||||
}
|
||||
else if (typeof this.config.parts === 'object') {
|
||||
this.__parts = this.config.parts
|
||||
this.preresolveDependencies()
|
||||
this.resolvedDependencies = this.resolveDependencies(this.dependencies)
|
||||
}
|
||||
this.config.draftOrder = this.draftOrder(this.resolvedDependencies)
|
||||
|
||||
// Make all parts uniform
|
||||
for (const [key, value] of Object.entries(this.__parts)) {
|
||||
this.__parts[key] = decoratePartDependency(value)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
function snappedOption(option, pattern) {
|
||||
const conf = pattern.config.options[option]
|
||||
const abs = conf.toAbs(pattern.settings.options[option], pattern.settings)
|
||||
|
@ -191,10 +230,26 @@ Pattern.prototype.runHooks = function (hookName, data = false) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allows adding a part at run-time
|
||||
*/
|
||||
Pattern.prototype.addPart = function (part, name=false, key) {
|
||||
if (!part.draft) part = decoratePartDependency(part, givenName)
|
||||
if (typeof part?.draft === 'function') {
|
||||
this.__parts[part.name] = part
|
||||
}
|
||||
else this.raise.warning(`Cannot attach part ${name} because it is not a part`)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* The default draft method with pre- and postDraft hooks
|
||||
*/
|
||||
Pattern.prototype.draft = function () {
|
||||
// Late-stage initialization
|
||||
this.init()
|
||||
|
||||
if (this.is !== 'sample') {
|
||||
this.is = 'draft'
|
||||
this.cutList = {}
|
||||
|
@ -212,33 +267,49 @@ Pattern.prototype.draft = function () {
|
|||
}
|
||||
|
||||
this.runHooks('preDraft')
|
||||
for (let partName of this.config.draftOrder) {
|
||||
for (const partName of this.config.draftOrder) {
|
||||
// Create parts
|
||||
this.raise.debug(`Creating part \`${partName}\``)
|
||||
this.parts[partName] = new this.Part(partName)
|
||||
if (typeof this.config.inject[partName] === 'string') {
|
||||
// Handle inject/inheritance
|
||||
if (typeof this.inject[partName] === 'string') {
|
||||
this.raise.debug(
|
||||
`Injecting part \`${this.config.inject[partName]}\` into part \`${partName}\``
|
||||
`Injecting part \`${this.inject[partName]}\` into part \`${partName}\``
|
||||
)
|
||||
try {
|
||||
this.parts[partName].inject(this.parts[this.config.inject[partName]])
|
||||
this.parts[partName].inject(this.parts[this.inject[partName]])
|
||||
} catch (err) {
|
||||
this.raise.error([
|
||||
`Could not inject part \`${this.config.inject[partName]}\` into part \`${partName}\``,
|
||||
`Could not inject part \`${this.inject[partName]}\` into part \`${partName}\``,
|
||||
err,
|
||||
])
|
||||
}
|
||||
}
|
||||
if (this.needs(partName)) {
|
||||
let method = 'draft' + capitalize(partName)
|
||||
if (typeof this[method] !== 'function') {
|
||||
this.raise.error(`Method \`pattern.${method}\` is callable`)
|
||||
throw new Error('Method "' + method + '" on pattern object is not callable')
|
||||
// Draft part
|
||||
const method = 'draft' + capitalize(partName)
|
||||
if (typeof this.__parts?.[partName]?.draft === 'function') {
|
||||
// 2022 way - Part is contained in config
|
||||
try {
|
||||
this.parts[partName] = this.__parts[partName].draft(this.parts[partName])
|
||||
if (this.parts[partName].render) this.cutList[partName] = this.parts[partName].cut
|
||||
} catch (err) {
|
||||
this.raise.error([`Unable to draft part \`${partName}\``, err])
|
||||
}
|
||||
}
|
||||
try {
|
||||
this.parts[partName] = this[method](this.parts[partName])
|
||||
if (this.parts[partName].render ) this.cutList[partName] = this.parts[partName].cut
|
||||
} catch (err) {
|
||||
this.raise.error([`Unable to draft part \`${partName}\``, err])
|
||||
else if (typeof this[method] === 'function') {
|
||||
// Legacy way - Part is attached to the prototype
|
||||
this.raise.warning(`Adding parts to the prototype is deprecated and will be removed in FreeSewing v4 (part: \`${partName}\`)`)
|
||||
try {
|
||||
this.parts[partName] = this[method](this.parts[partName])
|
||||
if (this.parts[partName].render ) this.cutList[partName] = this.parts[partName].cut
|
||||
} catch (err) {
|
||||
this.raise.error([`Unable to draft part \`${partName}\``, err])
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.raise.error(`Unable to draft pattern. Part is not available in iether legacy or 2022`)
|
||||
throw new Error('Method "' + method + '" on pattern object is not callable')
|
||||
}
|
||||
if (typeof this.parts[partName] === 'undefined') {
|
||||
this.raise.error(
|
||||
|
@ -570,7 +641,7 @@ Pattern.prototype.draftOrder = function (graph = this.resolveDependencies()) {
|
|||
Pattern.prototype.resolveDependency = function (
|
||||
seen,
|
||||
part,
|
||||
graph = this.config.dependencies,
|
||||
graph = this.dependencies,
|
||||
deps = []
|
||||
) {
|
||||
if (typeof seen[part] === 'undefined') seen[part] = true
|
||||
|
@ -586,33 +657,52 @@ Pattern.prototype.resolveDependency = function (
|
|||
return deps
|
||||
}
|
||||
|
||||
/** Pre-Resolves part dependencies that are passed in 2022 style */
|
||||
Pattern.prototype.preresolveDependencies = function (count=0) {
|
||||
const len = Object.keys(this.__parts).length
|
||||
if (!this.__parts) return
|
||||
for (const [name, part] of Object.entries(this.__parts)) {
|
||||
if (part.from) {
|
||||
this.inject[name] = part.from.name
|
||||
if (typeof this.__parts[part.from.name] === 'undefined') {
|
||||
this.__parts[part.from.name] = decoratePartDependency(part.from)
|
||||
}
|
||||
}
|
||||
}
|
||||
const newlen = Object.keys(this.__parts).length
|
||||
|
||||
return (Object.keys(this.__parts).length > len)
|
||||
? this.preresolveDependencies()
|
||||
: this
|
||||
}
|
||||
|
||||
/** Resolves part dependencies into a flat array */
|
||||
Pattern.prototype.resolveDependencies = function (graph = this.config.dependencies) {
|
||||
for (let i in this.config.inject) {
|
||||
let dependency = this.config.inject[i]
|
||||
if (typeof this.config.dependencies[i] === 'undefined') this.config.dependencies[i] = dependency
|
||||
else if (this.config.dependencies[i] !== dependency) {
|
||||
if (typeof this.config.dependencies[i] === 'string') {
|
||||
this.config.dependencies[i] = [this.config.dependencies[i], dependency]
|
||||
} else if (Array.isArray(this.config.dependencies[i])) {
|
||||
if (this.config.dependencies[i].indexOf(dependency) === -1)
|
||||
this.config.dependencies[i].push(dependency)
|
||||
Pattern.prototype.resolveDependencies = function (graph = this.dependencies) {
|
||||
for (let i in this.inject) {
|
||||
let dependency = this.inject[i]
|
||||
if (typeof this.dependencies[i] === 'undefined') this.dependencies[i] = dependency
|
||||
else if (this.dependencies[i] !== dependency) {
|
||||
if (typeof this.dependencies[i] === 'string') {
|
||||
this.dependencies[i] = [this.dependencies[i], dependency]
|
||||
} else if (Array.isArray(this.dependencies[i])) {
|
||||
if (this.dependencies[i].indexOf(dependency) === -1)
|
||||
this.dependencies[i].push(dependency)
|
||||
} else {
|
||||
this.raise.error('Part dependencies should be a string or an array of strings')
|
||||
throw new Error('Part dependencies should be a string or an array of strings')
|
||||
}
|
||||
}
|
||||
// Parts both in the parts and dependencies array trip up the dependency resolver
|
||||
if (Array.isArray(this.config.parts)) {
|
||||
let pos = this.config.parts.indexOf(this.config.inject[i])
|
||||
if (pos !== -1) this.config.parts.splice(pos, 1)
|
||||
if (Array.isArray(this.__parts)) {
|
||||
let pos = this.__parts.indexOf(this.inject[i])
|
||||
if (pos !== -1) this.__parts.splice(pos, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Include parts outside the dependency graph
|
||||
if (Array.isArray(this.config.parts)) {
|
||||
for (let part of this.config.parts) {
|
||||
if (typeof this.config.dependencies[part] === 'undefined') this.config.dependencies[part] = []
|
||||
if (typeof this.dependencies[part] === 'undefined') this.dependencies[part] = []
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -632,15 +722,15 @@ Pattern.prototype.needs = function (partName) {
|
|||
if (typeof this.settings.only === 'undefined' || this.settings.only === false) return true
|
||||
else if (typeof this.settings.only === 'string') {
|
||||
if (this.settings.only === partName) return true
|
||||
if (Array.isArray(this.config.resolvedDependencies[this.settings.only])) {
|
||||
for (let dependency of this.config.resolvedDependencies[this.settings.only]) {
|
||||
if (Array.isArray(this.resolvedDependencies[this.settings.only])) {
|
||||
for (let dependency of this.resolvedDependencies[this.settings.only]) {
|
||||
if (dependency === partName) return true
|
||||
}
|
||||
}
|
||||
} else if (Array.isArray(this.settings.only)) {
|
||||
for (let part of this.settings.only) {
|
||||
if (part === partName) return true
|
||||
for (let dependency of this.config.resolvedDependencies[part]) {
|
||||
for (let dependency of this.resolvedDependencies[part]) {
|
||||
if (dependency === partName) return true
|
||||
}
|
||||
}
|
||||
|
@ -651,9 +741,11 @@ Pattern.prototype.needs = function (partName) {
|
|||
|
||||
/* Checks whether a part is hidden in the config */
|
||||
Pattern.prototype.isHidden = function (partName) {
|
||||
if (Array.isArray(this.config.hide)) {
|
||||
if (this.config.hide.indexOf(partName) !== -1) return true
|
||||
if (Array.isArray(this.hide)) {
|
||||
if (this.hide.indexOf(partName) !== -1) return true
|
||||
}
|
||||
// 2022 style
|
||||
if (this.__parts?.[partName]?.hide) return true
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -384,3 +384,7 @@ export const generatePartTransform = (x, y, rotate, flipX, flipY, part) => {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Makes sure an object passed to be attached as a part it not merely a method
|
||||
*/
|
||||
export const decoratePartDependency = (obj, name) => (typeof obj === 'function') ? { draft: obj, name } : obj
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue