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,16 +23,21 @@ 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 })
|
||||
.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))
|
||||
}
|
||||
|
||||
|
@ -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,7 +1,11 @@
|
|||
import { dimensions } from './shared'
|
||||
import front from "./front.js"
|
||||
|
||||
export default function (part) {
|
||||
let {
|
||||
export default {
|
||||
from: front,
|
||||
name: 'back',
|
||||
draft: function (part) {
|
||||
const {
|
||||
store,
|
||||
sa,
|
||||
Point,
|
||||
|
@ -101,3 +105,4 @@ export default function (part) {
|
|||
|
||||
return part
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import { dimensions } from './shared'
|
||||
import { base } from '@freesewing/brian'
|
||||
|
||||
export default function (part) {
|
||||
let {
|
||||
export default {
|
||||
from: base,
|
||||
name: 'front',
|
||||
draft: function (part) {
|
||||
const {
|
||||
utils,
|
||||
store,
|
||||
sa,
|
||||
|
@ -167,3 +171,4 @@ export default function (part) {
|
|||
|
||||
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)
|
||||
// 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
|
||||
}
|
||||
Aaron.prototype.draftFront = (part) => draftFront(part)
|
||||
Aaron.prototype.draftBack = (part) => draftBack(part)
|
||||
})
|
||||
|
||||
// Named exports
|
||||
export { config, Aaron }
|
||||
|
||||
export { front, back, Aaron }
|
||||
// Default export
|
||||
export default Aaron
|
||||
|
|
|
@ -23,16 +23,21 @@ 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 })
|
||||
.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))
|
||||
}
|
||||
|
||||
|
@ -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,7 +52,9 @@ export default {
|
|||
},
|
||||
],
|
||||
},
|
||||
measurements: [
|
||||
}
|
||||
|
||||
export const measurements = [
|
||||
'biceps',
|
||||
'chest',
|
||||
'hpsToWaistBack',
|
||||
|
@ -62,64 +64,7 @@ export default {
|
|||
'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 },
|
||||
export const optionalMeasurements = ['highBust']
|
||||
|
||||
// draft for high bust
|
||||
draftForHighBust: { bool: false },
|
||||
},
|
||||
}
|
||||
|
|
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,7 +1,11 @@
|
|||
import * as shared from './shared'
|
||||
import base from './base'
|
||||
|
||||
export default (part) => {
|
||||
let {
|
||||
export default {
|
||||
from: base,
|
||||
name: 'back',
|
||||
draft: (part) => {
|
||||
const {
|
||||
store,
|
||||
sa,
|
||||
points,
|
||||
|
@ -182,3 +186,4 @@ export default (part) => {
|
|||
|
||||
return part
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
export default (part) => {
|
||||
let {
|
||||
import { _base as options } from '../config/options.js'
|
||||
|
||||
|
||||
export default {
|
||||
name: 'base',
|
||||
hide: true,
|
||||
options,
|
||||
draft: (part) => {
|
||||
const {
|
||||
measurements,
|
||||
options,
|
||||
store,
|
||||
|
@ -171,3 +178,4 @@ export default (part) => {
|
|||
|
||||
return part
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import * as shared from './shared'
|
||||
import back from './back'
|
||||
|
||||
export default (part) => {
|
||||
let {
|
||||
export default {
|
||||
from: back,
|
||||
name: 'front',
|
||||
draft: (part) => {
|
||||
const {
|
||||
store,
|
||||
sa,
|
||||
Point,
|
||||
|
@ -191,3 +195,4 @@ export default (part) => {
|
|||
|
||||
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,4 +1,11 @@
|
|||
export default (part) => {
|
||||
import sleevecap from './sleevecap'
|
||||
import { _sleeve as options } from '../config/options.js'
|
||||
|
||||
export default {
|
||||
from: sleevecap,
|
||||
name: 'sleeve',
|
||||
options,
|
||||
draft: (part) => {
|
||||
const {
|
||||
store,
|
||||
sa,
|
||||
|
@ -15,6 +22,12 @@ export default (part) => {
|
|||
macro,
|
||||
} = part.shorthand()
|
||||
|
||||
// Remove things inherited
|
||||
macro('cutonfold', false)
|
||||
macro('rmad')
|
||||
delete paths.waist
|
||||
for (const key in snippets) delete snippets[key]
|
||||
|
||||
// Determine the sleeve length
|
||||
store.set('sleeveLength', measurements.shoulderToWrist * (1 + options.sleeveLengthBonus))
|
||||
points.sleeveTip = paths.sleevecap.edge('top')
|
||||
|
@ -87,3 +100,4 @@ export default (part) => {
|
|||
}
|
||||
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,8 +139,13 @@ 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
|
||||
|
@ -158,3 +166,4 @@ export default (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,34 +267,50 @@ 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])
|
||||
}
|
||||
}
|
||||
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(
|
||||
`Result of \`pattern.${method}\` was \`undefined\`. Did you forget to return the \`Part\` object?`
|
||||
|
@ -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