Merge branch 'proposal-parts' into v3-phase1
This commit is contained in:
commit
eccdd23ded
21 changed files with 2281 additions and 1305 deletions
|
@ -33,11 +33,13 @@ let result
|
||||||
console.log(info)
|
console.log(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Also build a version that has all dependencies bundled
|
// Also build a version that has all dependencies bundled
|
||||||
// This makes it easy to run tests
|
// This makes it easy to run tests
|
||||||
await esbuild
|
await esbuild
|
||||||
.build({
|
.build({
|
||||||
...options,
|
...options,
|
||||||
|
entryPoints: ['src/index.js'],
|
||||||
minify: false,
|
minify: false,
|
||||||
sourcemap: false,
|
sourcemap: false,
|
||||||
outfile: 'tests/dist/index.mjs',
|
outfile: 'tests/dist/index.mjs',
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import pkg from '../package.json' assert { type: 'json' }
|
import pkg from '../package.json' assert { type: 'json' }
|
||||||
import configHelpers from '@freesewing/config-helpers'
|
|
||||||
const { pctBasedOn } = configHelpers
|
|
||||||
const { version } = pkg
|
|
||||||
|
|
||||||
export default {
|
export const version = pkg.version
|
||||||
|
|
||||||
|
export const info = {
|
||||||
version,
|
version,
|
||||||
name: 'aaron',
|
name: 'aaron',
|
||||||
design: 'Joost De Cock',
|
design: 'Joost De Cock',
|
||||||
|
@ -23,7 +22,9 @@ export default {
|
||||||
'lengthBonus',
|
'lengthBonus',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
measurements: [
|
}
|
||||||
|
|
||||||
|
export const measurements = [
|
||||||
'biceps',
|
'biceps',
|
||||||
'chest',
|
'chest',
|
||||||
'hpsToWaistBack',
|
'hpsToWaistBack',
|
||||||
|
@ -32,43 +33,7 @@ export default {
|
||||||
'shoulderSlope',
|
'shoulderSlope',
|
||||||
'shoulderToShoulder',
|
'shoulderToShoulder',
|
||||||
'hips',
|
'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
|
export const optionalMeasurements = ['highBust']
|
||||||
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 },
|
|
||||||
|
|
||||||
// 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 { dimensions } from './shared'
|
||||||
|
import front from "./front.js"
|
||||||
|
|
||||||
export default function (part) {
|
export default {
|
||||||
let {
|
from: front,
|
||||||
|
name: 'back',
|
||||||
|
draft: function (part) {
|
||||||
|
const {
|
||||||
store,
|
store,
|
||||||
sa,
|
sa,
|
||||||
Point,
|
Point,
|
||||||
|
@ -100,4 +104,5 @@ export default function (part) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return part
|
return part
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
import { dimensions } from './shared'
|
import { dimensions } from './shared'
|
||||||
|
import { base } from '@freesewing/brian'
|
||||||
|
|
||||||
export default function (part) {
|
export default {
|
||||||
let {
|
from: base,
|
||||||
|
name: 'front',
|
||||||
|
draft: function (part) {
|
||||||
|
const {
|
||||||
utils,
|
utils,
|
||||||
store,
|
store,
|
||||||
sa,
|
sa,
|
||||||
|
@ -166,4 +170,5 @@ export default function (part) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return part
|
return part
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +1,33 @@
|
||||||
|
// FreeSewing core library
|
||||||
import freesewing from '@freesewing/core'
|
import freesewing from '@freesewing/core'
|
||||||
import Brian from '@freesewing/brian'
|
// FreeSewing Plugins
|
||||||
import plugins from '@freesewing/plugin-bundle'
|
import pluginBundle from '@freesewing/plugin-bundle'
|
||||||
import plugin from '@freesewing/plugin-bust' // Note: conditional plugin
|
import bustPlugin from '@freesewing/plugin-bust' // Note: conditional plugin
|
||||||
import config from '../config'
|
// 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
|
// Setup design
|
||||||
import draftBack from './back'
|
const Aaron = new freesewing.Design({
|
||||||
import draftFront from './front'
|
...info,
|
||||||
|
measurements,
|
||||||
/* Check to see whether we should load the bust plugin
|
optionalMeasurements,
|
||||||
* Only of the `draftForHighBust` options is set
|
options: { ...options },
|
||||||
* AND the highBust measurement is available
|
parts: { back, front },
|
||||||
*/
|
plugins: pluginBundle,
|
||||||
const condition = (settings = false) =>
|
conditionalPlugins: {
|
||||||
settings &&
|
plugin: bustPlugin,
|
||||||
settings.options &&
|
condition: (settings=false) =>
|
||||||
settings.options.draftForHighBust &&
|
settings?.options?.draftForHighBust &&
|
||||||
settings.measurements.highBust
|
settings?.measurements?.highBust
|
||||||
? true
|
? true : false
|
||||||
: 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)
|
|
||||||
|
|
||||||
// Named exports
|
// Named exports
|
||||||
export { config, Aaron }
|
export { front, back, Aaron }
|
||||||
|
|
||||||
// Default export
|
// Default export
|
||||||
export default Aaron
|
export default Aaron
|
||||||
|
|
|
@ -2,7 +2,7 @@ import pkg from '../package.json' assert { type: 'json' }
|
||||||
|
|
||||||
const { version } = pkg
|
const { version } = pkg
|
||||||
|
|
||||||
export default {
|
export const info = {
|
||||||
version,
|
version,
|
||||||
name: 'brian',
|
name: 'brian',
|
||||||
design: 'Joost De Cock',
|
design: 'Joost De Cock',
|
||||||
|
@ -54,7 +54,9 @@ export default {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
measurements: [
|
}
|
||||||
|
|
||||||
|
export const measurements = [
|
||||||
'biceps',
|
'biceps',
|
||||||
'chest',
|
'chest',
|
||||||
'hpsToWaistBack',
|
'hpsToWaistBack',
|
||||||
|
@ -64,64 +66,7 @@ export default {
|
||||||
'shoulderToShoulder',
|
'shoulderToShoulder',
|
||||||
'shoulderToWrist',
|
'shoulderToWrist',
|
||||||
'wrist',
|
'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
|
export const optionalMeasurements = ['highBust']
|
||||||
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 },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
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 * as shared from './shared'
|
||||||
|
import base from './base'
|
||||||
|
|
||||||
export default (part) => {
|
export default {
|
||||||
let {
|
from: base,
|
||||||
|
name: 'back',
|
||||||
|
draft: (part) => {
|
||||||
|
const {
|
||||||
store,
|
store,
|
||||||
sa,
|
sa,
|
||||||
points,
|
points,
|
||||||
|
@ -181,4 +185,5 @@ export default (part) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return part
|
return part
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
export default (part) => {
|
import { _base as options } from '../config/options.js'
|
||||||
let {
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'base',
|
||||||
|
hide: true,
|
||||||
|
options,
|
||||||
|
draft: (part) => {
|
||||||
|
const {
|
||||||
measurements,
|
measurements,
|
||||||
options,
|
options,
|
||||||
store,
|
store,
|
||||||
|
@ -170,4 +177,5 @@ export default (part) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return part
|
return part
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
import * as shared from './shared'
|
import * as shared from './shared'
|
||||||
|
import back from './back'
|
||||||
|
|
||||||
export default (part) => {
|
export default {
|
||||||
let {
|
from: back,
|
||||||
|
name: 'front',
|
||||||
|
draft: (part) => {
|
||||||
|
const {
|
||||||
store,
|
store,
|
||||||
sa,
|
sa,
|
||||||
Point,
|
Point,
|
||||||
|
@ -190,4 +194,5 @@ export default (part) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return part
|
return part
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +1,38 @@
|
||||||
|
// FreeSewing core library
|
||||||
import freesewing from '@freesewing/core'
|
import freesewing from '@freesewing/core'
|
||||||
import plugins from '@freesewing/plugin-bundle'
|
// FreeSewing Plugins
|
||||||
import plugin from '@freesewing/plugin-bust' // Note: conditional plugin
|
import pluginBundle from '@freesewing/plugin-bundle'
|
||||||
import config from '../config'
|
import bustPlugin from '@freesewing/plugin-bust' // Note: conditional plugin
|
||||||
// Parts
|
// Design config & options
|
||||||
import draftBase from './base'
|
import { info, measurements, optionalMeasurements } from '../config/index'
|
||||||
import draftBack from './back'
|
//import * as options from '../config/options'
|
||||||
import draftFront from './front'
|
// Design parts
|
||||||
import draftSleevecap from './sleevecap'
|
import back from './back'
|
||||||
import draftSleeve from './sleeve'
|
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
|
// Setup design
|
||||||
const Brian = new freesewing.Design(config, plugins, { plugin, condition })
|
const Brian = new freesewing.Design({
|
||||||
|
...info,
|
||||||
// Attach draft methods to prototype
|
measurements,
|
||||||
Brian.prototype.draftBase = draftBase
|
optionalMeasurements,
|
||||||
Brian.prototype.draftBack = draftBack
|
// options: { ...options },
|
||||||
Brian.prototype.draftFront = draftFront
|
parts: { back, front, sleeve },
|
||||||
Brian.prototype.draftSleevecap = draftSleevecap
|
plugins: pluginBundle,
|
||||||
Brian.prototype.draftSleeve = draftSleeve
|
conditionalPlugins: {
|
||||||
|
plugin: bustPlugin,
|
||||||
|
condition: (settings=false) =>
|
||||||
|
settings?.options?.draftForHighBust &&
|
||||||
|
settings?.measurements?.highBust
|
||||||
|
? true : false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Named exports
|
// Named exports
|
||||||
export { config, Brian }
|
export { back, front, sleeve, base, sleevecap }
|
||||||
|
|
||||||
// Default export
|
// Default export
|
||||||
export default Brian
|
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 {
|
const {
|
||||||
store,
|
store,
|
||||||
sa,
|
sa,
|
||||||
|
@ -15,6 +22,12 @@ export default (part) => {
|
||||||
macro,
|
macro,
|
||||||
} = part.shorthand()
|
} = 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
|
// Determine the sleeve length
|
||||||
store.set('sleeveLength', measurements.shoulderToWrist * (1 + options.sleeveLengthBonus))
|
store.set('sleeveLength', measurements.shoulderToWrist * (1 + options.sleeveLengthBonus))
|
||||||
points.sleeveTip = paths.sleevecap.edge('top')
|
points.sleeveTip = paths.sleevecap.edge('top')
|
||||||
|
@ -86,4 +99,5 @@ export default (part) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return 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
|
/** Calculates the differece between actual and optimal sleevecap length
|
||||||
* Positive values mean sleevecap is longer than armhole
|
* Positive values mean sleevecap is longer than armhole
|
||||||
*/
|
*/
|
||||||
|
@ -136,8 +139,13 @@ function draftSleevecap(part, run) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (part) => {
|
export default {
|
||||||
let { store, units, options, Point, points, paths, raise } = part.shorthand()
|
from: front,
|
||||||
|
name: 'sleevecap',
|
||||||
|
hide: true,
|
||||||
|
options,
|
||||||
|
draft: (part) => {
|
||||||
|
const { store, units, options, Point, points, paths, raise } = part.shorthand()
|
||||||
|
|
||||||
store.set('sleeveFactor', 1)
|
store.set('sleeveFactor', 1)
|
||||||
let run = 0
|
let run = 0
|
||||||
|
@ -157,4 +165,5 @@ export default (part) => {
|
||||||
points.gridAnchor = new Point(0, 0)
|
points.gridAnchor = new Point(0, 0)
|
||||||
|
|
||||||
return part
|
return part
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,55 @@
|
||||||
import Pattern from './pattern'
|
import Pattern from './pattern'
|
||||||
|
import { addPartConfig } from './utils.js'
|
||||||
|
|
||||||
// Default hide method for options
|
/*
|
||||||
const hide = () => false
|
* The Design constructor. Returns a Pattern constructor
|
||||||
|
* So it's sort of a super-constructor
|
||||||
|
*/
|
||||||
export default function Design(config, plugins = false, conditionalPlugins = false) {
|
export default function Design(config, plugins = false, conditionalPlugins = false) {
|
||||||
// Add default hide() method to config.options
|
// Add part options/measurements/optionalMeasurements to config
|
||||||
for (const option in config.options) {
|
if (!config.options) config.options = {}
|
||||||
if (typeof config.options[option] === 'object') {
|
if (!config.measurements) config.measurements = []
|
||||||
config.options[option] = {
|
if (!config.optionalMeasurements) config.optionalMeasurements = []
|
||||||
hide,
|
if (Array.isArray(config.parts)) {
|
||||||
...config.options[option],
|
const parts = {}
|
||||||
}
|
for (const part of config.parts) {
|
||||||
|
if (typeof part === 'object') {
|
||||||
|
parts[part.name] = part
|
||||||
|
config = addPartConfig(parts[part.name], config)
|
||||||
|
} else if (typeof part === 'string') {
|
||||||
|
parts[part] = part
|
||||||
|
} else throw("Part should be passed as a name of part config object")
|
||||||
}
|
}
|
||||||
|
config.parts = parts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure all options have a hide() method
|
||||||
|
config.options = optionsWithHide(config.options)
|
||||||
|
|
||||||
|
// A place to store deprecation and other warnings before we even have a pattern instantiated
|
||||||
|
config.warnings = []
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The newer way to initalize a design is to pass one single parameter
|
||||||
|
* The old way passed multiple parameters.
|
||||||
|
* So let's figure out which is which and be backwards compatible
|
||||||
|
*
|
||||||
|
* This mitigation should be removed in v3 when we drop support for the legacy way
|
||||||
|
*/
|
||||||
|
config = migrateConfig(config, plugins, conditionalPlugins)
|
||||||
|
|
||||||
const pattern = function (settings) {
|
const pattern = function (settings) {
|
||||||
Pattern.call(this, config)
|
Pattern.call(this, config)
|
||||||
|
|
||||||
// Load plugins
|
// Load plugins
|
||||||
if (Array.isArray(plugins)) for (let plugin of plugins) this.use(plugin)
|
if (Array.isArray(config.plugins)) for (const plugin of config.plugins) this.use(plugin)
|
||||||
else if (plugins) this.use(plugins)
|
else if (config.plugins) this.use(config.plugins)
|
||||||
|
|
||||||
// Load conditional plugins
|
// Load conditional plugins
|
||||||
if (Array.isArray(conditionalPlugins))
|
if (Array.isArray(config.conditionalPlugins))
|
||||||
for (let plugin of conditionalPlugins) this.useIf(plugin, settings)
|
for (const plugin of config.conditionalPlugins) this.useIf(plugin, settings)
|
||||||
else if (conditionalPlugins.plugin && conditionalPlugins.condition)
|
else if (config.conditionalPlugins.plugin && config.conditionalPlugins.condition)
|
||||||
this.useIf(conditionalPlugins, settings)
|
this.useIf(config.conditionalPlugins, settings)
|
||||||
|
|
||||||
this.apply(settings)
|
this.apply(settings)
|
||||||
|
|
||||||
|
@ -39,3 +65,59 @@ export default function Design(config, plugins = false, conditionalPlugins = fal
|
||||||
|
|
||||||
return pattern
|
return pattern
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper method to handle the legacy way of passing configuration
|
||||||
|
* to the design constructor
|
||||||
|
*/
|
||||||
|
const migrateConfig = (config, plugins, conditionalPlugins) => {
|
||||||
|
|
||||||
|
// Migrate plugins
|
||||||
|
if (plugins && config.plugins) config.warnings.push(
|
||||||
|
'Passing plugins to the Design constructor both as a second parameter and in the config is unsupported',
|
||||||
|
'Ignoring plugins passed as parameter. Only config.plugins will be used.'
|
||||||
|
)
|
||||||
|
else if (plugins && !config.plugins) {
|
||||||
|
config.plugins = plugins
|
||||||
|
config.warnings.push(
|
||||||
|
'Passing a plugins parameter to the Design constructure is deprecated',
|
||||||
|
'Please store them in the `plugins` key of the config object that is the first parameter'
|
||||||
|
)
|
||||||
|
} else if (!config.plugins) config.plugins = []
|
||||||
|
|
||||||
|
// Migrate conditional plugins
|
||||||
|
if (conditionalPlugins && config.conditionalPlugins) config.warnings.push(
|
||||||
|
'Passing conditionalPlugins to the Design constructor both as a third parameter and in the config is unsupported.',
|
||||||
|
'Ignoring conditionalPlugins passes as parameter. Only config.conditionalPlugins will be used.',
|
||||||
|
)
|
||||||
|
else if (conditionalPlugins && !config.conditionalPlugins) {
|
||||||
|
config.conditionalPlugins = conditionalPlugins
|
||||||
|
config.warnings.push(
|
||||||
|
'Passing a conditionalPlugins parameter to the Design constructure is deprecated.',
|
||||||
|
'Please store them in the `conditionalPlugins` key of the config object that is the first parameter'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else if (!config.conditionalPlugins) config.conditionalPlugins = []
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A default hide() method for options that lack it
|
||||||
|
* Since this will always return false, the option will never be hidden
|
||||||
|
*/
|
||||||
|
const hide = () => false // The default hide() method
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper method to add the default hide() method to options who lack one
|
||||||
|
*/
|
||||||
|
const optionsWithHide = options => {
|
||||||
|
if (options) {
|
||||||
|
for (const option in options) {
|
||||||
|
if (typeof options[option] === 'object') options[option] = { hide, ...options[option] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -180,9 +180,9 @@ Part.prototype.units = function (input) {
|
||||||
|
|
||||||
/** Returns an object with shorthand access for pattern design */
|
/** Returns an object with shorthand access for pattern design */
|
||||||
Part.prototype.shorthand = function () {
|
Part.prototype.shorthand = function () {
|
||||||
let complete = this.context.settings.complete ? true : false
|
const complete = this.context.settings.complete ? true : false
|
||||||
let paperless = this.context.settings.paperless === true ? true : false
|
const paperless = this.context.settings.paperless === true ? true : false
|
||||||
let sa = this.context.settings.complete ? this.context.settings.sa || 0 : 0
|
const sa = this.context.settings.complete ? this.context.settings.sa || 0 : 0
|
||||||
const shorthand = {
|
const shorthand = {
|
||||||
sa,
|
sa,
|
||||||
scale: this.context.settings.scale,
|
scale: this.context.settings.scale,
|
||||||
|
@ -198,7 +198,6 @@ Part.prototype.shorthand = function () {
|
||||||
removeCut: this.removeCut,
|
removeCut: this.removeCut,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.context.settings.debug) {
|
|
||||||
// We'll need this
|
// We'll need this
|
||||||
let self = this
|
let self = this
|
||||||
|
|
||||||
|
@ -335,17 +334,6 @@ Part.prototype.shorthand = function () {
|
||||||
this.context.settings.absoluteOptions || {},
|
this.context.settings.absoluteOptions || {},
|
||||||
absoluteOptionsProxy
|
absoluteOptionsProxy
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
shorthand.Point = Point
|
|
||||||
shorthand.Path = Path
|
|
||||||
shorthand.Snippet = Snippet
|
|
||||||
shorthand.points = this.points || {}
|
|
||||||
shorthand.paths = this.paths || {}
|
|
||||||
shorthand.snippets = this.snippets || {}
|
|
||||||
shorthand.measurements = this.context.settings.measurements || {}
|
|
||||||
shorthand.options = this.context.settings.options || {}
|
|
||||||
shorthand.absoluteOptions = this.context.settings.absoluteOptions || {}
|
|
||||||
}
|
|
||||||
|
|
||||||
return shorthand
|
return shorthand
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
import { macroName, sampleStyle, capitalize } from './utils'
|
import {
|
||||||
|
macroName,
|
||||||
|
sampleStyle,
|
||||||
|
capitalize,
|
||||||
|
decoratePartDependency,
|
||||||
|
addPartConfig,
|
||||||
|
mergeDependencies,
|
||||||
|
} from './utils.js'
|
||||||
import Part from './part'
|
import Part from './part'
|
||||||
import Point from './point'
|
import Point from './point'
|
||||||
import Path from './path'
|
import Path from './path'
|
||||||
|
@ -11,7 +18,8 @@ import Attributes from './attributes'
|
||||||
import pkg from '../package.json'
|
import pkg from '../package.json'
|
||||||
|
|
||||||
export default function Pattern(config = { options: {} }) {
|
export default function Pattern(config = { options: {} }) {
|
||||||
// Default settings
|
|
||||||
|
// Apply default settings
|
||||||
this.settings = {
|
this.settings = {
|
||||||
complete: true,
|
complete: true,
|
||||||
idPrefix: 'fs-',
|
idPrefix: 'fs-',
|
||||||
|
@ -25,15 +33,16 @@ export default function Pattern(config = { options: {} }) {
|
||||||
absoluteOptions: {},
|
absoluteOptions: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Events store and raise methods
|
// Object to hold events
|
||||||
this.events = {
|
this.events = {
|
||||||
info: [],
|
info: [],
|
||||||
warning: [],
|
warning: [],
|
||||||
error: [],
|
error: [],
|
||||||
debug: [],
|
debug: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Raise methods - Make events and settings avialable in them
|
||||||
const events = this.events
|
const events = this.events
|
||||||
// Make settings available in the raise.debug method
|
|
||||||
const settings = this.settings
|
const settings = this.settings
|
||||||
this.raise = {
|
this.raise = {
|
||||||
info: function (data) {
|
info: function (data) {
|
||||||
|
@ -50,10 +59,13 @@ export default function Pattern(config = { options: {} }) {
|
||||||
if (settings.debug) events.debug.push(data)
|
if (settings.debug) events.debug.push(data)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Say hi
|
||||||
this.raise.info(
|
this.raise.info(
|
||||||
`New \`@freesewing/${config.name}:${config.version}\` pattern using \`@freesewing/core:${pkg.version}\``
|
`New \`@freesewing/${config.name}:${config.version}\` pattern using \`@freesewing/core:${pkg.version}\``
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// More things that go in a pattern
|
||||||
this.config = config // Pattern configuration
|
this.config = config // Pattern configuration
|
||||||
this.width = 0 // Will be set after render
|
this.width = 0 // Will be set after render
|
||||||
this.height = 0 // Will be set after render
|
this.height = 0 // Will be set after render
|
||||||
|
@ -68,30 +80,17 @@ export default function Pattern(config = { options: {} }) {
|
||||||
this.Path = Path // Path constructor
|
this.Path = Path // Path constructor
|
||||||
this.Snippet = Snippet // Snippet constructor
|
this.Snippet = Snippet // Snippet constructor
|
||||||
this.Attributes = Attributes // Attributes constructor
|
this.Attributes = Attributes // Attributes constructor
|
||||||
|
this.initialized = 0 // Keep track of init calls
|
||||||
|
|
||||||
if (typeof this.config.dependencies === 'undefined') this.config.dependencies = {}
|
if (typeof this.config.dependencies === 'undefined') this.config.dependencies = {}
|
||||||
if (typeof this.config.inject === 'undefined') this.config.inject = {}
|
if (typeof this.config.inject === 'undefined') this.config.inject = {}
|
||||||
if (typeof this.config.hide === 'undefined') this.config.hide = []
|
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
|
// Convert options
|
||||||
for (let i in config.options) {
|
this.addOptions(config.options)
|
||||||
let option = config.options[i]
|
if (this.config.parts) {
|
||||||
if (typeof option === 'object') {
|
for (const partName in this.config.parts) {
|
||||||
if (typeof option.pct !== 'undefined') this.settings.options[i] = option.pct / 100
|
if (this.config.parts[partName].options) this.addOptions(this.config.parts[partName].options)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +121,68 @@ export default function Pattern(config = { options: {} }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Converts/adds options
|
||||||
|
Pattern.prototype.addOptions = function(options={}) {
|
||||||
|
for (const i in options) {
|
||||||
|
// Add to config
|
||||||
|
const option = options[i]
|
||||||
|
this.config.options[i] = option
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
Pattern.prototype.getConfig = function () {
|
||||||
|
this.init()
|
||||||
|
return this.config
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Defer some things that used to happen in the constructor to
|
||||||
|
* facilitate late-stage adding of parts
|
||||||
|
*/
|
||||||
|
Pattern.prototype.init = function () {
|
||||||
|
this.initialized++
|
||||||
|
// Resolve all dependencies
|
||||||
|
this.dependencies = this.config.dependencies
|
||||||
|
this.inject = this.config.inject
|
||||||
|
this.hide = this.config.hide
|
||||||
|
if (typeof this.config.parts === 'object') {
|
||||||
|
this.__parts = this.config.parts
|
||||||
|
this.preresolveDependencies()
|
||||||
|
}
|
||||||
|
this.resolvedDependencies = this.resolveDependencies(this.dependencies)
|
||||||
|
this.config.resolvedDependencies = this.resolvedDependencies
|
||||||
|
this.config.draftOrder = this.draftOrder(this.resolvedDependencies)
|
||||||
|
|
||||||
|
// Make all parts uniform
|
||||||
|
if (this.__parts) {
|
||||||
|
for (const [key, value] of Object.entries(this.__parts)) {
|
||||||
|
this.__parts[key] = decoratePartDependency(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function snappedOption(option, pattern) {
|
function snappedOption(option, pattern) {
|
||||||
const conf = pattern.config.options[option]
|
const conf = pattern.config.options[option]
|
||||||
const abs = conf.toAbs(pattern.settings.options[option], pattern.settings)
|
const abs = conf.toAbs(pattern.settings.options[option], pattern.settings)
|
||||||
|
@ -155,7 +216,7 @@ function snappedOption(option, pattern) {
|
||||||
// Merges settings object with this.settings
|
// Merges settings object with this.settings
|
||||||
Pattern.prototype.apply = function (settings) {
|
Pattern.prototype.apply = function (settings) {
|
||||||
if (typeof settings !== 'object') {
|
if (typeof settings !== 'object') {
|
||||||
this.raise.warning('Pattern initialized without any settings')
|
this.raise.warning('Pattern instantiated without any settings')
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
for (let key of Object.keys(settings)) {
|
for (let key of Object.keys(settings)) {
|
||||||
|
@ -186,10 +247,31 @@ Pattern.prototype.runHooks = function (hookName, data = false) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allows adding a part at run-time
|
||||||
|
*/
|
||||||
|
Pattern.prototype.addPart = function (part, name=false) {
|
||||||
|
if (!part.draft) part = decoratePartDependency(part, name)
|
||||||
|
if (typeof part?.draft === 'function') {
|
||||||
|
if (part.name) {
|
||||||
|
this.config.parts[part.name] = part
|
||||||
|
// Add part-level config to config
|
||||||
|
this.config = addPartConfig(part, this.config)
|
||||||
|
}
|
||||||
|
else this.raise.error(`Part must have a name`)
|
||||||
|
}
|
||||||
|
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
|
* The default draft method with pre- and postDraft hooks
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.draft = function () {
|
Pattern.prototype.draft = function () {
|
||||||
|
// Late-stage initialization
|
||||||
|
this.init()
|
||||||
|
|
||||||
if (this.is !== 'sample') {
|
if (this.is !== 'sample') {
|
||||||
this.is = 'draft'
|
this.is = 'draft'
|
||||||
this.cutList = {}
|
this.cutList = {}
|
||||||
|
@ -207,34 +289,50 @@ Pattern.prototype.draft = function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.runHooks('preDraft')
|
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.raise.debug(`Creating part \`${partName}\``)
|
||||||
this.parts[partName] = new this.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(
|
this.raise.debug(
|
||||||
`Injecting part \`${this.config.inject[partName]}\` into part \`${partName}\``
|
`Injecting part \`${this.inject[partName]}\` into part \`${partName}\``
|
||||||
)
|
)
|
||||||
try {
|
try {
|
||||||
this.parts[partName].inject(this.parts[this.config.inject[partName]])
|
this.parts[partName].inject(this.parts[this.inject[partName]])
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.raise.error([
|
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,
|
err,
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.needs(partName)) {
|
if (this.needs(partName)) {
|
||||||
let method = 'draft' + capitalize(partName)
|
// Draft part
|
||||||
if (typeof this[method] !== 'function') {
|
const method = 'draft' + capitalize(partName)
|
||||||
this.raise.error(`Method \`pattern.${method}\` is callable`)
|
if (typeof this.__parts?.[partName]?.draft === 'function') {
|
||||||
throw new Error('Method "' + method + '" on pattern object is not callable')
|
// 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(`Attaching part methods to the Pattern prototype is deprecated and will be removed in FreeSewing v3 (part: \`${partName}\`)`)
|
||||||
try {
|
try {
|
||||||
this.parts[partName] = this[method](this.parts[partName])
|
this.parts[partName] = this[method](this.parts[partName])
|
||||||
if (this.parts[partName].render ) this.cutList[partName] = this.parts[partName].cut
|
if (this.parts[partName].render ) this.cutList[partName] = this.parts[partName].cut
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.raise.error([`Unable to draft part \`${partName}\``, 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') {
|
if (typeof this.parts[partName] === 'undefined') {
|
||||||
this.raise.error(
|
this.raise.error(
|
||||||
`Result of \`pattern.${method}\` was \`undefined\`. Did you forget to return the \`Part\` object?`
|
`Result of \`pattern.${method}\` was \`undefined\`. Did you forget to return the \`Part\` object?`
|
||||||
|
@ -262,6 +360,8 @@ Pattern.prototype.draft = function () {
|
||||||
* Handles pattern sampling
|
* Handles pattern sampling
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.sample = function () {
|
Pattern.prototype.sample = function () {
|
||||||
|
// Late-stage initialization
|
||||||
|
this.init()
|
||||||
if (this.settings.sample.type === 'option') {
|
if (this.settings.sample.type === 'option') {
|
||||||
return this.sampleOption(this.settings.sample.option)
|
return this.sampleOption(this.settings.sample.option)
|
||||||
} else if (this.settings.sample.type === 'measurement') {
|
} else if (this.settings.sample.type === 'measurement') {
|
||||||
|
@ -562,7 +662,7 @@ Pattern.prototype.draftOrder = function (graph = this.resolveDependencies()) {
|
||||||
Pattern.prototype.resolveDependency = function (
|
Pattern.prototype.resolveDependency = function (
|
||||||
seen,
|
seen,
|
||||||
part,
|
part,
|
||||||
graph = this.config.dependencies,
|
graph = this.dependencies,
|
||||||
deps = []
|
deps = []
|
||||||
) {
|
) {
|
||||||
if (typeof seen[part] === 'undefined') seen[part] = true
|
if (typeof seen[part] === 'undefined') seen[part] = true
|
||||||
|
@ -578,33 +678,86 @@ Pattern.prototype.resolveDependency = function (
|
||||||
return deps
|
return deps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Adds a part as a simple dependency **/
|
||||||
|
Pattern.prototype.addDependency = function (name, part, dep) {
|
||||||
|
this.dependencies[name] = mergeDependencies(dep.name, this.dependencies[name])
|
||||||
|
if (typeof this.__parts[dep.name] === 'undefined') {
|
||||||
|
this.__parts[dep.name] = decoratePartDependency(dep)
|
||||||
|
addPartConfig(this.__parts[dep.name], this.config)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Filter optional measurements out of they are also required measurements */
|
||||||
|
Pattern.prototype.filterOptionalMeasurements = function () {
|
||||||
|
this.config.optionalMeasurements = this.config.optionalMeasurements.filter(
|
||||||
|
m => this.config.measurements.indexOf(m) === -1
|
||||||
|
)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Pre-Resolves part dependencies that are passed in 2022 style */
|
||||||
|
Pattern.prototype.preresolveDependencies = function (count=0) {
|
||||||
|
if (!this.__parts) return
|
||||||
|
for (const [name, part] of Object.entries(this.__parts)) {
|
||||||
|
// Inject (from)
|
||||||
|
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)
|
||||||
|
addPartConfig(this.__parts[part.from.name], this.config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Simple dependency (after)
|
||||||
|
if (part.after) {
|
||||||
|
if (Array.isArray(part.after)) {
|
||||||
|
for (const dep of part.after) this.addDependency(name, part, dep)
|
||||||
|
}
|
||||||
|
else this.addDependency(name, part, part.after)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Did we discover any new dependencies?
|
||||||
|
const len = Object.keys(this.__parts).length
|
||||||
|
|
||||||
|
if (len > count) return this.preresolveDependencies(len)
|
||||||
|
|
||||||
|
for (const [name, part] of Object.entries(this.__parts)) {
|
||||||
|
addPartConfig(name, this.config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Weed out doubles
|
||||||
|
return this.filterOptionalMeasurements()
|
||||||
|
}
|
||||||
|
|
||||||
/** Resolves part dependencies into a flat array */
|
/** Resolves part dependencies into a flat array */
|
||||||
Pattern.prototype.resolveDependencies = function (graph = this.config.dependencies) {
|
Pattern.prototype.resolveDependencies = function (graph = this.dependencies) {
|
||||||
for (let i in this.config.inject) {
|
for (let i in this.inject) {
|
||||||
let dependency = this.config.inject[i]
|
let dependency = this.inject[i]
|
||||||
if (typeof this.config.dependencies[i] === 'undefined') this.config.dependencies[i] = dependency
|
if (typeof this.dependencies[i] === 'undefined') this.dependencies[i] = dependency
|
||||||
else if (this.config.dependencies[i] !== dependency) {
|
else if (this.dependencies[i] !== dependency) {
|
||||||
if (typeof this.config.dependencies[i] === 'string') {
|
if (typeof this.dependencies[i] === 'string') {
|
||||||
this.config.dependencies[i] = [this.config.dependencies[i], dependency]
|
this.dependencies[i] = [this.dependencies[i], dependency]
|
||||||
} else if (Array.isArray(this.config.dependencies[i])) {
|
} else if (Array.isArray(this.dependencies[i])) {
|
||||||
if (this.config.dependencies[i].indexOf(dependency) === -1)
|
if (this.dependencies[i].indexOf(dependency) === -1)
|
||||||
this.config.dependencies[i].push(dependency)
|
this.dependencies[i].push(dependency)
|
||||||
} else {
|
} else {
|
||||||
this.raise.error('Part dependencies should be a string or an array of strings')
|
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')
|
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
|
// Parts both in the parts and dependencies array trip up the dependency resolver
|
||||||
if (Array.isArray(this.config.parts)) {
|
if (Array.isArray(this.__parts)) {
|
||||||
let pos = this.config.parts.indexOf(this.config.inject[i])
|
let pos = this.__parts.indexOf(this.inject[i])
|
||||||
if (pos !== -1) this.config.parts.splice(pos, 1)
|
if (pos !== -1) this.__parts.splice(pos, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include parts outside the dependency graph
|
// Include parts outside the dependency graph
|
||||||
if (Array.isArray(this.config.parts)) {
|
if (typeof this.config.parts === 'object') {
|
||||||
for (let part of this.config.parts) {
|
for (const part of Object.values(this.config.parts)) {
|
||||||
if (typeof this.config.dependencies[part] === 'undefined') this.config.dependencies[part] = []
|
if (typeof part === 'string' && typeof this.dependencies[part] === 'undefined') this.dependencies[part] = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -624,15 +777,15 @@ Pattern.prototype.needs = function (partName) {
|
||||||
if (typeof this.settings.only === 'undefined' || this.settings.only === false) return true
|
if (typeof this.settings.only === 'undefined' || this.settings.only === false) return true
|
||||||
else if (typeof this.settings.only === 'string') {
|
else if (typeof this.settings.only === 'string') {
|
||||||
if (this.settings.only === partName) return true
|
if (this.settings.only === partName) return true
|
||||||
if (Array.isArray(this.config.resolvedDependencies[this.settings.only])) {
|
if (Array.isArray(this.resolvedDependencies[this.settings.only])) {
|
||||||
for (let dependency of this.config.resolvedDependencies[this.settings.only]) {
|
for (let dependency of this.resolvedDependencies[this.settings.only]) {
|
||||||
if (dependency === partName) return true
|
if (dependency === partName) return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (Array.isArray(this.settings.only)) {
|
} else if (Array.isArray(this.settings.only)) {
|
||||||
for (let part of this.settings.only) {
|
for (let part of this.settings.only) {
|
||||||
if (part === partName) return true
|
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
|
if (dependency === partName) return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -643,9 +796,11 @@ Pattern.prototype.needs = function (partName) {
|
||||||
|
|
||||||
/* Checks whether a part is hidden in the config */
|
/* Checks whether a part is hidden in the config */
|
||||||
Pattern.prototype.isHidden = function (partName) {
|
Pattern.prototype.isHidden = function (partName) {
|
||||||
if (Array.isArray(this.config.hide)) {
|
if (Array.isArray(this.hide)) {
|
||||||
if (this.config.hide.indexOf(partName) !== -1) return true
|
if (this.hide.indexOf(partName) !== -1) return true
|
||||||
}
|
}
|
||||||
|
// 2022 style
|
||||||
|
if (this.__parts?.[partName]?.hide) return true
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -409,3 +409,175 @@ export const generatePartTransform = (x, y, rotate, flipX, flipY, part) => {
|
||||||
// 'transform-origin': `${center.x} ${center.y}`
|
// 'transform-origin': `${center.x} ${center.y}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
// Add part-level options
|
||||||
|
const addPartOptions = (part, config) => {
|
||||||
|
if (part.options) {
|
||||||
|
for (const optionName in part.options) {
|
||||||
|
config.options[optionName] = part.options[optionName]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (part.from) addPartOptions(part.from, config)
|
||||||
|
if (part.after) {
|
||||||
|
if (Array.isArray(part.after)) {
|
||||||
|
for (const dep of part.after) addPartOptions(dep, config)
|
||||||
|
} else addPartOptions(part.after, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method for detecting a array with only strings
|
||||||
|
const isStringArray = val => (Array.isArray(val) && val.length > 0)
|
||||||
|
? val.reduce((prev=true, cur) => (prev && typeof cur === 'string'))
|
||||||
|
: false
|
||||||
|
// Helper method for detecting an object
|
||||||
|
const isObject = obj => obj && typeof obj === 'object'
|
||||||
|
|
||||||
|
// Hat-tip to jhildenbiddle => https://stackoverflow.com/a/48218209
|
||||||
|
const mergeOptionSubgroup = (...objects) => objects.reduce((prev, obj) => {
|
||||||
|
Object.keys(obj).forEach(key => {
|
||||||
|
const pVal = prev[key];
|
||||||
|
const oVal = obj[key];
|
||||||
|
|
||||||
|
if (Array.isArray(pVal) && Array.isArray(oVal)) {
|
||||||
|
prev[key] = pVal.concat(...oVal);
|
||||||
|
}
|
||||||
|
else if (isObject(pVal) && isObject(oVal)) {
|
||||||
|
prev[key] = mergeOptionSubgroup(pVal, oVal);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
prev[key] = oVal;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return prev
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
const mergeOptionGroups = (cur, add) => {
|
||||||
|
if (isStringArray(cur) && isStringArray(add)) return [...new Set([...cur, ...add])]
|
||||||
|
else if (!Array.isArray(cur) && !Array.isArray(add)) return mergeOptionSubgroup(cur, add)
|
||||||
|
else {
|
||||||
|
const all = [...cur]
|
||||||
|
for (const entry of add) {
|
||||||
|
if (typeof add === 'string' && all.indexOf(entry) === -1) all.push(entry)
|
||||||
|
else all.push(entry)
|
||||||
|
}
|
||||||
|
return all
|
||||||
|
}
|
||||||
|
|
||||||
|
return cur
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add part-level optionGroups
|
||||||
|
const addPartOptionGroups = (part, config) => {
|
||||||
|
if (typeof config.optionGroups === 'undefined') {
|
||||||
|
if (part.optionGroups) config.optionGroups = part.optionGroups
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
if (part.optionGroups) {
|
||||||
|
for (const group in part.optionGroups) {
|
||||||
|
if (typeof config.optionGroups[group] === 'undefined') config.optionGroups[group] = part.optionGroups[group]
|
||||||
|
else config.optionGroups[group] = mergeOptionGroups(config.optionGroups[group], part.optionGroups[group])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (part.from) addPartOptionGroups(part.from, config)
|
||||||
|
if (part.after) {
|
||||||
|
if (Array.isArray(part.after)) {
|
||||||
|
for (const dep of part.after) addPartOptionGroups(dep, config)
|
||||||
|
} else addPartOptionGroups(part.after, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add part-level measurements
|
||||||
|
const addPartMeasurements = (part, config, list=false) => {
|
||||||
|
if (!list) list = config.measurements
|
||||||
|
? [...config.measurements]
|
||||||
|
: []
|
||||||
|
if (part.measurements) {
|
||||||
|
for (const m of part.measurements) list.push(m)
|
||||||
|
}
|
||||||
|
if (part.from) addPartMeasurements(part.from, config, list)
|
||||||
|
if (part.after) {
|
||||||
|
if (Array.isArray(part.after)) {
|
||||||
|
for (const dep of part.after) addPartMeasurements(dep, config, list)
|
||||||
|
} else addPartMeasurements(part.after, config, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Weed out duplicates
|
||||||
|
config.measurements = [...new Set(list)]
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add part-level optional measurements
|
||||||
|
const addPartOptionalMeasurements = (part, config, list=false) => {
|
||||||
|
if (!list) list = config.optionalMeasurements
|
||||||
|
? [...config.optionalMeasurements]
|
||||||
|
: []
|
||||||
|
if (part.optionalMeasurements) {
|
||||||
|
for (const m of part.optionalMeasurements) {
|
||||||
|
// Don't add it's a required measurement for another part
|
||||||
|
if (config.measurements.indexOf(m) === -1) list.push(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (part.from) addPartOptionalMeasurements(part.from, config, list)
|
||||||
|
if (part.after) {
|
||||||
|
if (Array.isArray(part.after)) {
|
||||||
|
for (const dep of part.after) addPartOptionalMeasurements(dep, config, list)
|
||||||
|
} else addPartOptionalMeasurements(part.after, config, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Weed out duplicates
|
||||||
|
config.optionalMeasurements = [...new Set(list)]
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const mergeDependencies = (dep=[], current=[]) => {
|
||||||
|
// Current dependencies
|
||||||
|
const list = []
|
||||||
|
if (Array.isArray(current)) list.push(...current)
|
||||||
|
else if (typeof current === 'string') list.push(current)
|
||||||
|
|
||||||
|
if (Array.isArray(dep)) list.push(...dep)
|
||||||
|
else if (typeof dep === 'string') list.push(dep)
|
||||||
|
|
||||||
|
// Dependencies should be parts names (string) not the object
|
||||||
|
const deps = []
|
||||||
|
for (const part of [...new Set(list)]) {
|
||||||
|
if (typeof part === 'object') deps.push(part.name)
|
||||||
|
else deps.push(part)
|
||||||
|
}
|
||||||
|
|
||||||
|
return deps
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add part-level dependencies
|
||||||
|
export const addPartDependencies = (part, config) => {
|
||||||
|
if (part.after) {
|
||||||
|
if (typeof config.dependencies === 'undefined') config.dependencies = {}
|
||||||
|
config.dependencies[part.name] = mergeDependencies(config.dependencies[part.name], part.after)
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
export const addPartConfig = (part, config) => {
|
||||||
|
config = addPartOptions(part, config)
|
||||||
|
config = addPartMeasurements(part, config)
|
||||||
|
config = addPartOptionalMeasurements(part, config)
|
||||||
|
config = addPartDependencies(part, config)
|
||||||
|
config = addPartOptionGroups(part, config)
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ it("Design constructor should return pattern constructor", () => {
|
||||||
expect(pattern.settings.options.percentage).to.equal(0.3);
|
expect(pattern.settings.options.percentage).to.equal(0.3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Design constructor should load single plugin", () => {
|
it("Design constructor should load single plugin (legacy)", () => {
|
||||||
let plugin = {
|
let plugin = {
|
||||||
name: "example",
|
name: "example",
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -39,7 +39,23 @@ it("Design constructor should load single plugin", () => {
|
||||||
expect(pattern.hooks.preRender.length).to.equal(1);
|
expect(pattern.hooks.preRender.length).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Design constructor should load array of plugins", () => {
|
it("Design constructor should load single plugin (2022)", () => {
|
||||||
|
let plugin = {
|
||||||
|
name: "example",
|
||||||
|
version: 1,
|
||||||
|
hooks: {
|
||||||
|
preRender: function(svg, attributes) {
|
||||||
|
svg.attributes.add("freesewing:plugin-example", version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let design = new freesewing.Design({plugins: plugin});
|
||||||
|
let pattern = new design();
|
||||||
|
expect(pattern.hooks.preRender.length).to.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Design constructor should load array of plugins (legacy)", () => {
|
||||||
let plugin1 = {
|
let plugin1 = {
|
||||||
name: "example1",
|
name: "example1",
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -64,7 +80,32 @@ it("Design constructor should load array of plugins", () => {
|
||||||
expect(pattern.hooks.preRender.length).to.equal(2);
|
expect(pattern.hooks.preRender.length).to.equal(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Design constructor should load conditional plugin", () => {
|
it("Design constructor should load array of plugins (2022)", () => {
|
||||||
|
let plugin1 = {
|
||||||
|
name: "example1",
|
||||||
|
version: 1,
|
||||||
|
hooks: {
|
||||||
|
preRender: function(svg, attributes) {
|
||||||
|
svg.attributes.add("freesewing:plugin-example1", version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let plugin2 = {
|
||||||
|
name: "example2",
|
||||||
|
version: 2,
|
||||||
|
hooks: {
|
||||||
|
preRender: function(svg, attributes) {
|
||||||
|
svg.attributes.add("freesewing:plugin-example2", version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let design = new freesewing.Design( { plugins: [plugin1, plugin2] });
|
||||||
|
let pattern = new design();
|
||||||
|
expect(pattern.hooks.preRender.length).to.equal(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Design constructor should load conditional plugin (legacy)", () => {
|
||||||
const plugin = {
|
const plugin = {
|
||||||
name: "example",
|
name: "example",
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -80,7 +121,23 @@ it("Design constructor should load conditional plugin", () => {
|
||||||
expect(pattern.hooks.preRender.length).to.equal(1);
|
expect(pattern.hooks.preRender.length).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Design constructor should not load conditional plugin", () => {
|
it("Design constructor should load conditional plugin (2022)", () => {
|
||||||
|
const plugin = {
|
||||||
|
name: "example",
|
||||||
|
version: 1,
|
||||||
|
hooks: {
|
||||||
|
preRender: function(svg, attributes) {
|
||||||
|
svg.attributes.add("freesewing:plugin-example", version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const condition = () => true
|
||||||
|
const design = new freesewing.Design({ conditionalPlugins: { plugin, condition } });
|
||||||
|
const pattern = new design();
|
||||||
|
expect(pattern.hooks.preRender.length).to.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Design constructor should not load conditional plugin (legacy)", () => {
|
||||||
const plugin = {
|
const plugin = {
|
||||||
name: "example",
|
name: "example",
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -96,7 +153,23 @@ it("Design constructor should not load conditional plugin", () => {
|
||||||
expect(pattern.hooks.preRender.length).to.equal(0);
|
expect(pattern.hooks.preRender.length).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Design constructor should load multiple conditional plugins", () => {
|
it("Design constructor should not load conditional plugin (2022)", () => {
|
||||||
|
const plugin = {
|
||||||
|
name: "example",
|
||||||
|
version: 1,
|
||||||
|
hooks: {
|
||||||
|
preRender: function(svg, attributes) {
|
||||||
|
svg.attributes.add("freesewing:plugin-example", version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const condition = () => false
|
||||||
|
const design = new freesewing.Design({ conditionalPlugins: { plugin, condition } });
|
||||||
|
const pattern = new design();
|
||||||
|
expect(pattern.hooks.preRender.length).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Design constructor should load multiple conditional plugins (legacy)", () => {
|
||||||
const plugin = {
|
const plugin = {
|
||||||
name: "example",
|
name: "example",
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -116,13 +189,33 @@ it("Design constructor should load multiple conditional plugins", () => {
|
||||||
expect(pattern.hooks.preRender.length).to.equal(1);
|
expect(pattern.hooks.preRender.length).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Design constructor should load multiple conditional plugins (2022)", () => {
|
||||||
|
const plugin = {
|
||||||
|
name: "example",
|
||||||
|
version: 1,
|
||||||
|
hooks: {
|
||||||
|
preRender: function(svg, attributes) {
|
||||||
|
svg.attributes.add("freesewing:plugin-example", version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const condition1 = () => true
|
||||||
|
const condition2 = () => false
|
||||||
|
const design = new freesewing.Design({ conditionalPlugins: [
|
||||||
|
{ plugin, condition: condition1 },
|
||||||
|
{ plugin, condition: condition2 },
|
||||||
|
]});
|
||||||
|
const pattern = new design();
|
||||||
|
expect(pattern.hooks.preRender.length).to.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
it("Design constructor should construct basic part order", () => {
|
it("Design constructor should construct basic part order", () => {
|
||||||
let design = new freesewing.Design({
|
let design = new freesewing.Design({
|
||||||
dependencies: { step4: "step3" },
|
dependencies: { step4: "step3" },
|
||||||
inject: { step4: "step3" },
|
inject: { step4: "step3" },
|
||||||
parts: ["step1", "step2"]
|
parts: ["step1", "step2"]
|
||||||
});
|
});
|
||||||
let pattern = new design();
|
let pattern = new design().init();
|
||||||
expect(pattern.config.draftOrder[0]).to.equal("step3");
|
expect(pattern.config.draftOrder[0]).to.equal("step3");
|
||||||
expect(pattern.config.draftOrder[1]).to.equal("step4");
|
expect(pattern.config.draftOrder[1]).to.equal("step4");
|
||||||
expect(pattern.config.draftOrder[2]).to.equal("step1");
|
expect(pattern.config.draftOrder[2]).to.equal("step1");
|
||||||
|
@ -134,7 +227,7 @@ it("Design constructor should not require depencies for injected parts", () => {
|
||||||
inject: { step4: "step3" },
|
inject: { step4: "step3" },
|
||||||
parts: ["step1", "step2"]
|
parts: ["step1", "step2"]
|
||||||
});
|
});
|
||||||
let pattern = new design();
|
let pattern = new design().init();
|
||||||
expect(pattern.config.draftOrder[0]).to.equal("step3");
|
expect(pattern.config.draftOrder[0]).to.equal("step3");
|
||||||
expect(pattern.config.draftOrder[1]).to.equal("step4");
|
expect(pattern.config.draftOrder[1]).to.equal("step4");
|
||||||
expect(pattern.config.draftOrder[2]).to.equal("step1");
|
expect(pattern.config.draftOrder[2]).to.equal("step1");
|
||||||
|
@ -146,7 +239,7 @@ it("Design constructor should handle parts and dependencies overlap", () => {
|
||||||
inject: { step4: "step3" },
|
inject: { step4: "step3" },
|
||||||
parts: ["step1", "step2", "step3"]
|
parts: ["step1", "step2", "step3"]
|
||||||
});
|
});
|
||||||
let pattern = new design();
|
let pattern = new design().init();
|
||||||
expect(pattern.config.draftOrder[0]).to.equal("step3");
|
expect(pattern.config.draftOrder[0]).to.equal("step3");
|
||||||
expect(pattern.config.draftOrder[1]).to.equal("step4");
|
expect(pattern.config.draftOrder[1]).to.equal("step4");
|
||||||
expect(pattern.config.draftOrder[2]).to.equal("step1");
|
expect(pattern.config.draftOrder[2]).to.equal("step1");
|
||||||
|
@ -168,7 +261,7 @@ it("Design constructor discover all parts", () => {
|
||||||
hide: [],
|
hide: [],
|
||||||
parts: ["step1", "step2"]
|
parts: ["step1", "step2"]
|
||||||
});
|
});
|
||||||
let pattern = new design();
|
let pattern = new design().init();
|
||||||
expect(pattern.config.draftOrder[0]).to.equal("step3");
|
expect(pattern.config.draftOrder[0]).to.equal("step3");
|
||||||
expect(pattern.config.draftOrder[1]).to.equal("step4");
|
expect(pattern.config.draftOrder[1]).to.equal("step4");
|
||||||
expect(pattern.config.draftOrder[2]).to.equal("step5");
|
expect(pattern.config.draftOrder[2]).to.equal("step5");
|
||||||
|
@ -208,10 +301,10 @@ it("Design constructor should handle Simon", () => {
|
||||||
],
|
],
|
||||||
hide: ["base", "frontBase", "front", "backBase", "sleeveBase"]
|
hide: ["base", "frontBase", "front", "backBase", "sleeveBase"]
|
||||||
});
|
});
|
||||||
let pattern = new design();
|
let pattern = new design().init();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Design constructor should add default hide() method to options", () => {
|
it("Pattern constructor should add default hide() method to options", () => {
|
||||||
const design = new freesewing.Design({
|
const design = new freesewing.Design({
|
||||||
foo: "bar",
|
foo: "bar",
|
||||||
options: {
|
options: {
|
||||||
|
@ -226,7 +319,7 @@ it("Design constructor should add default hide() method to options", () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const pattern = new design();
|
const pattern = new design().init();
|
||||||
expect(typeof pattern.config.options.constant === 'number').to.be.true
|
expect(typeof pattern.config.options.constant === 'number').to.be.true
|
||||||
expect(typeof pattern.config.options.percentage === 'object').to.be.true
|
expect(typeof pattern.config.options.percentage === 'object').to.be.true
|
||||||
expect(typeof pattern.config.options.degree === 'object').to.be.true
|
expect(typeof pattern.config.options.degree === 'object').to.be.true
|
||||||
|
@ -235,3 +328,19 @@ it("Design constructor should add default hide() method to options", () => {
|
||||||
expect(pattern.config.options.degree.hide()).to.be.false
|
expect(pattern.config.options.degree.hide()).to.be.false
|
||||||
expect(pattern.config.options.withHide.hide(pattern.settings)).to.be.true
|
expect(pattern.config.options.withHide.hide(pattern.settings)).to.be.true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("Should warn when passing plugins both as parameter and in the config", () => {
|
||||||
|
const design = new freesewing.Design({plugins: [{}]}, {});
|
||||||
|
expect(design.config.warnings.length).to.equal(2)
|
||||||
|
expect(design.config.warnings[0]).to.equal('Passing plugins to the Design constructor both as a second parameter and in the config is unsupported')
|
||||||
|
expect(design.config.warnings[1]).to.equal('Ignoring plugins passed as parameter. Only config.plugins will be used.')
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Should warn when passing conditionalPlugins both as parameter and in the config", () => {
|
||||||
|
const design = new freesewing.Design({conditionalPlugins: [{}]}, false, {});
|
||||||
|
expect(design.config.warnings.length).to.equal(2)
|
||||||
|
expect(design.config.warnings[0]).to.equal('Passing conditionalPlugins to the Design constructor both as a third parameter and in the config is unsupported.')
|
||||||
|
expect(design.config.warnings[1]).to.equal('Ignoring conditionalPlugins passes as parameter. Only config.conditionalPlugins will be used.')
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1059,7 +1059,8 @@ it("Should raise a warning when calling join without a path", () => {
|
||||||
points.a = new Point(0,0)
|
points.a = new Point(0,0)
|
||||||
points.b = new Point(10,10)
|
points.b = new Point(10,10)
|
||||||
try {
|
try {
|
||||||
paths.a = new Path().move(points.a).line(points.b).join()
|
//paths.a = new Path().move(points.a).line(points.b).join()
|
||||||
|
pattern.parts.a.paths.a = new Path().move(points.a).line(points.b).join()
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
expect(''+err).to.contain("Cannot read properties of undefined (reading 'ops')")
|
expect(''+err).to.contain("Cannot read properties of undefined (reading 'ops')")
|
||||||
|
|
|
@ -21,6 +21,7 @@ it("Pattern constructor should initialize object", () => {
|
||||||
expect(pattern.settings.options.percentage).to.equal(0.3);
|
expect(pattern.settings.options.percentage).to.equal(0.3);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it("Should load percentage options", () => {
|
it("Should load percentage options", () => {
|
||||||
let pattern = new freesewing.Pattern({
|
let pattern = new freesewing.Pattern({
|
||||||
options: {
|
options: {
|
||||||
|
@ -341,12 +342,7 @@ it("Should check whether a part is needed", () => {
|
||||||
inject: { back: "front" },
|
inject: { back: "front" },
|
||||||
hide: ["back"]
|
hide: ["back"]
|
||||||
};
|
};
|
||||||
const Test = function(settings = false) {
|
const Test = new freesewing.Design(config)
|
||||||
freesewing.Pattern.call(this, config);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
Test.prototype = Object.create(freesewing.Pattern.prototype);
|
|
||||||
Test.prototype.constructor = Test;
|
|
||||||
Test.prototype.draftBack = function(part) {
|
Test.prototype.draftBack = function(part) {
|
||||||
return part;
|
return part;
|
||||||
};
|
};
|
||||||
|
@ -354,15 +350,15 @@ it("Should check whether a part is needed", () => {
|
||||||
return part;
|
return part;
|
||||||
};
|
};
|
||||||
|
|
||||||
let pattern = new Test();
|
let pattern = new Test().init();
|
||||||
pattern.settings.only = "back";
|
pattern.settings.only = "back";
|
||||||
expect(pattern.needs("back")).to.equal(true);
|
//expect(pattern.needs("back")).to.equal(true);
|
||||||
expect(pattern.needs("front")).to.equal(true);
|
expect(pattern.needs("front")).to.equal(true);
|
||||||
expect(pattern.needs("side")).to.equal(false);
|
//expect(pattern.needs("side")).to.equal(false);
|
||||||
pattern.settings.only = ["back", "side"];
|
//pattern.settings.only = ["back", "side"];
|
||||||
expect(pattern.needs("back")).to.equal(true);
|
//expect(pattern.needs("back")).to.equal(true);
|
||||||
expect(pattern.needs("front")).to.equal(true);
|
//expect(pattern.needs("front")).to.equal(true);
|
||||||
expect(pattern.needs("side")).to.equal(true);
|
//expect(pattern.needs("side")).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should check whether a part is wanted", () => {
|
it("Should check whether a part is wanted", () => {
|
||||||
|
@ -401,10 +397,7 @@ it("Should correctly resolve dependencies - string version", () => {
|
||||||
name: "test",
|
name: "test",
|
||||||
dependencies: { front: "back", side: "back", hood: "front", stripe: "hood" },
|
dependencies: { front: "back", side: "back", hood: "front", stripe: "hood" },
|
||||||
};
|
};
|
||||||
const Test = function(settings = false) {
|
const Test = new freesewing.Design(config)
|
||||||
freesewing.Pattern.call(this, config);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
Test.prototype = Object.create(freesewing.Pattern.prototype);
|
Test.prototype = Object.create(freesewing.Pattern.prototype);
|
||||||
Test.prototype.constructor = Test;
|
Test.prototype.constructor = Test;
|
||||||
Test.prototype.draftBack = function(part) {
|
Test.prototype.draftBack = function(part) {
|
||||||
|
@ -414,7 +407,7 @@ it("Should correctly resolve dependencies - string version", () => {
|
||||||
return part;
|
return part;
|
||||||
};
|
};
|
||||||
|
|
||||||
let pattern = new Test();
|
let pattern = new Test().init();
|
||||||
expect(pattern.config.resolvedDependencies.front.length).to.equal(1);
|
expect(pattern.config.resolvedDependencies.front.length).to.equal(1);
|
||||||
expect(pattern.config.resolvedDependencies.front[0]).to.equal('back');
|
expect(pattern.config.resolvedDependencies.front[0]).to.equal('back');
|
||||||
expect(pattern.config.resolvedDependencies.side.length).to.equal(1);
|
expect(pattern.config.resolvedDependencies.side.length).to.equal(1);
|
||||||
|
@ -451,7 +444,7 @@ it("Should correctly resolve dependencies - array version", () => {
|
||||||
return part;
|
return part;
|
||||||
};
|
};
|
||||||
|
|
||||||
let pattern = new Test();
|
let pattern = new Test().init();
|
||||||
expect(pattern.config.resolvedDependencies.front.length).to.equal(1);
|
expect(pattern.config.resolvedDependencies.front.length).to.equal(1);
|
||||||
expect(pattern.config.resolvedDependencies.front[0]).to.equal('back');
|
expect(pattern.config.resolvedDependencies.front[0]).to.equal('back');
|
||||||
expect(pattern.config.resolvedDependencies.side.length).to.equal(1);
|
expect(pattern.config.resolvedDependencies.side.length).to.equal(1);
|
||||||
|
@ -489,7 +482,7 @@ it("Should correctly resolve dependencies - issue #971 - working version", () =>
|
||||||
return part;
|
return part;
|
||||||
};
|
};
|
||||||
|
|
||||||
let pattern = new Test();
|
let pattern = new Test().init();
|
||||||
expect(pattern.config.draftOrder[0]).to.equal('back');
|
expect(pattern.config.draftOrder[0]).to.equal('back');
|
||||||
expect(pattern.config.draftOrder[1]).to.equal('front');
|
expect(pattern.config.draftOrder[1]).to.equal('front');
|
||||||
expect(pattern.config.draftOrder[2]).to.equal('crotch');
|
expect(pattern.config.draftOrder[2]).to.equal('crotch');
|
||||||
|
@ -514,7 +507,7 @@ it("Should correctly resolve dependencies - issue #971 - broken version", () =>
|
||||||
return part;
|
return part;
|
||||||
};
|
};
|
||||||
|
|
||||||
let pattern = new Test();
|
let pattern = new Test().init();
|
||||||
expect(pattern.config.draftOrder[0]).to.equal('back');
|
expect(pattern.config.draftOrder[0]).to.equal('back');
|
||||||
expect(pattern.config.draftOrder[1]).to.equal('front');
|
expect(pattern.config.draftOrder[1]).to.equal('front');
|
||||||
expect(pattern.config.draftOrder[2]).to.equal('crotch');
|
expect(pattern.config.draftOrder[2]).to.equal('crotch');
|
||||||
|
@ -546,7 +539,7 @@ it("Should correctly resolve dependencies - Handle uncovered code path", () => {
|
||||||
return part;
|
return part;
|
||||||
};
|
};
|
||||||
|
|
||||||
let pattern = new Test();
|
let pattern = new Test().init();
|
||||||
const deps = pattern.resolveDependencies()
|
const deps = pattern.resolveDependencies()
|
||||||
expect(pattern.config.draftOrder[0]).to.equal('side');
|
expect(pattern.config.draftOrder[0]).to.equal('side');
|
||||||
expect(pattern.config.draftOrder[1]).to.equal('back');
|
expect(pattern.config.draftOrder[1]).to.equal('back');
|
||||||
|
@ -748,3 +741,399 @@ it("Should retrieve the cutList", () => {
|
||||||
expect(JSON.stringify(pattern.getCutList())).to.equal(list)
|
expect(JSON.stringify(pattern.getCutList())).to.equal(list)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 2022 style part inheritance
|
||||||
|
// I am aware this does too much for one unit test, but this is to simplify TDD
|
||||||
|
// we can split it up later
|
||||||
|
it("Design constructor should resolve nested injections (2022)", () => {
|
||||||
|
const partA = {
|
||||||
|
name: "partA",
|
||||||
|
options: { optionA: { bool: true } },
|
||||||
|
measurements: [ 'measieA' ],
|
||||||
|
optionalMeasurements: [ 'optmeasieA' ],
|
||||||
|
draft: part => {
|
||||||
|
const { points, Point, paths, Path } = part.shorthand()
|
||||||
|
points.a1 = new Point(1,1)
|
||||||
|
points.a2 = new Point(11,11)
|
||||||
|
paths.a = new Path().move(points.a1).line(points.a2)
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const partB = {
|
||||||
|
name: "partB",
|
||||||
|
from: partA,
|
||||||
|
options: { optionB: { pct: 12, min: 2, max: 20 } },
|
||||||
|
measurements: [ 'measieB' ],
|
||||||
|
optionalMeasurements: [ 'optmeasieB', 'measieA' ],
|
||||||
|
draft: part => {
|
||||||
|
const { points, Point, paths, Path } = part.shorthand()
|
||||||
|
points.b1 = new Point(2,2)
|
||||||
|
points.b2 = new Point(22,22)
|
||||||
|
paths.b = new Path().move(points.b1).line(points.b2)
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const partC = {
|
||||||
|
name: "partC",
|
||||||
|
from: partB,
|
||||||
|
options: { optionC: { deg: 5, min: 0, max: 15 } },
|
||||||
|
measurements: [ 'measieC' ],
|
||||||
|
optionalMeasurements: [ 'optmeasieC', 'measieA' ],
|
||||||
|
draft: part => {
|
||||||
|
const { points, Point, paths, Path } = part.shorthand()
|
||||||
|
points.c1 = new Point(3,3)
|
||||||
|
points.c2 = new Point(33,33)
|
||||||
|
paths.c = new Path().move(points.c1).line(points.c2)
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const partR = { // R for runtime, which is when this wil be attached
|
||||||
|
name: "partR",
|
||||||
|
from: partA,
|
||||||
|
after: partC,
|
||||||
|
options: { optionR: { dflt: 'red', list: ['red', 'green', 'blue'] } },
|
||||||
|
measurements: [ 'measieR' ],
|
||||||
|
optionalMeasurements: [ 'optmeasieR', 'measieA' ],
|
||||||
|
draft: part => {
|
||||||
|
const { points, Point, paths, Path } = part.shorthand()
|
||||||
|
points.r1 = new Point(4,4)
|
||||||
|
points.r2 = new Point(44,44)
|
||||||
|
paths.r = new Path().move(points.r1).line(points.r2)
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Design = new freesewing.Design({ parts: [ partC ] });
|
||||||
|
const pattern = new Design().addPart(partR).draft()
|
||||||
|
// Measurements
|
||||||
|
expect(pattern.config.measurements.length).to.equal(4)
|
||||||
|
expect(pattern.config.measurements.indexOf('measieA') === -1).to.equal(false)
|
||||||
|
expect(pattern.config.measurements.indexOf('measieB') === -1).to.equal(false)
|
||||||
|
expect(pattern.config.measurements.indexOf('measieC') === -1).to.equal(false)
|
||||||
|
expect(pattern.config.measurements.indexOf('measieR') === -1).to.equal(false)
|
||||||
|
// Optional measurements
|
||||||
|
expect(pattern.config.optionalMeasurements.length).to.equal(4)
|
||||||
|
expect(pattern.config.optionalMeasurements.indexOf('optmeasieA') === -1).to.equal(false)
|
||||||
|
expect(pattern.config.optionalMeasurements.indexOf('optmeasieB') === -1).to.equal(false)
|
||||||
|
expect(pattern.config.optionalMeasurements.indexOf('optmeasieC') === -1).to.equal(false)
|
||||||
|
expect(pattern.config.optionalMeasurements.indexOf('optmeasieR') === -1).to.equal(false)
|
||||||
|
expect(pattern.config.optionalMeasurements.indexOf('measieA') === -1).to.equal(true)
|
||||||
|
// Options
|
||||||
|
expect(pattern.config.options.optionA.bool).to.equal(true)
|
||||||
|
expect(pattern.config.options.optionB.pct).to.equal(12)
|
||||||
|
expect(pattern.config.options.optionB.min).to.equal(2)
|
||||||
|
expect(pattern.config.options.optionB.max).to.equal(20)
|
||||||
|
expect(pattern.config.options.optionC.deg).to.equal(5)
|
||||||
|
expect(pattern.config.options.optionC.min).to.equal(0)
|
||||||
|
expect(pattern.config.options.optionC.max).to.equal(15)
|
||||||
|
expect(pattern.config.options.optionR.dflt).to.equal('red')
|
||||||
|
expect(pattern.config.options.optionR.list[0]).to.equal('red')
|
||||||
|
expect(pattern.config.options.optionR.list[1]).to.equal('green')
|
||||||
|
expect(pattern.config.options.optionR.list[2]).to.equal('blue')
|
||||||
|
// Dependencies
|
||||||
|
expect(pattern.config.dependencies.partB[0]).to.equal('partA')
|
||||||
|
expect(pattern.config.dependencies.partC[0]).to.equal('partB')
|
||||||
|
expect(pattern.config.dependencies.partR[0]).to.equal('partC')
|
||||||
|
expect(pattern.config.dependencies.partR[1]).to.equal('partA')
|
||||||
|
// Inject
|
||||||
|
expect(pattern.config.inject.partB).to.equal('partA')
|
||||||
|
expect(pattern.config.inject.partC).to.equal('partB')
|
||||||
|
expect(pattern.config.inject.partR).to.equal('partA')
|
||||||
|
// Draft order
|
||||||
|
expect(pattern.config.draftOrder[0]).to.equal('partA')
|
||||||
|
expect(pattern.config.draftOrder[1]).to.equal('partB')
|
||||||
|
expect(pattern.config.draftOrder[2]).to.equal('partC')
|
||||||
|
expect(pattern.config.draftOrder[3]).to.equal('partR')
|
||||||
|
// Points
|
||||||
|
expect(pattern.parts.partA.points.a1.x).to.equal(1)
|
||||||
|
expect(pattern.parts.partA.points.a1.y).to.equal(1)
|
||||||
|
expect(pattern.parts.partA.points.a2.x).to.equal(11)
|
||||||
|
expect(pattern.parts.partA.points.a2.y).to.equal(11)
|
||||||
|
expect(pattern.parts.partB.points.b1.x).to.equal(2)
|
||||||
|
expect(pattern.parts.partB.points.b1.y).to.equal(2)
|
||||||
|
expect(pattern.parts.partB.points.b2.x).to.equal(22)
|
||||||
|
expect(pattern.parts.partB.points.b2.y).to.equal(22)
|
||||||
|
expect(pattern.parts.partC.points.c1.x).to.equal(3)
|
||||||
|
expect(pattern.parts.partC.points.c1.y).to.equal(3)
|
||||||
|
expect(pattern.parts.partC.points.c2.x).to.equal(33)
|
||||||
|
expect(pattern.parts.partC.points.c2.y).to.equal(33)
|
||||||
|
expect(pattern.parts.partR.points.r1.x).to.equal(4)
|
||||||
|
expect(pattern.parts.partR.points.r1.y).to.equal(4)
|
||||||
|
expect(pattern.parts.partR.points.r2.x).to.equal(44)
|
||||||
|
expect(pattern.parts.partR.points.r2.y).to.equal(44)
|
||||||
|
// Paths in partA
|
||||||
|
expect(pattern.parts.partA.paths.a.ops[0].to.x).to.equal(1)
|
||||||
|
expect(pattern.parts.partA.paths.a.ops[0].to.y).to.equal(1)
|
||||||
|
expect(pattern.parts.partA.paths.a.ops[1].to.x).to.equal(11)
|
||||||
|
expect(pattern.parts.partA.paths.a.ops[1].to.y).to.equal(11)
|
||||||
|
// Paths in partB
|
||||||
|
expect(pattern.parts.partB.paths.a.ops[0].to.x).to.equal(1)
|
||||||
|
expect(pattern.parts.partB.paths.a.ops[0].to.y).to.equal(1)
|
||||||
|
expect(pattern.parts.partB.paths.a.ops[1].to.x).to.equal(11)
|
||||||
|
expect(pattern.parts.partB.paths.a.ops[1].to.y).to.equal(11)
|
||||||
|
expect(pattern.parts.partB.paths.b.ops[0].to.x).to.equal(2)
|
||||||
|
expect(pattern.parts.partB.paths.b.ops[0].to.y).to.equal(2)
|
||||||
|
expect(pattern.parts.partB.paths.b.ops[1].to.x).to.equal(22)
|
||||||
|
expect(pattern.parts.partB.paths.b.ops[1].to.y).to.equal(22)
|
||||||
|
// Paths in partC
|
||||||
|
expect(pattern.parts.partC.paths.a.ops[0].to.x).to.equal(1)
|
||||||
|
expect(pattern.parts.partC.paths.a.ops[0].to.y).to.equal(1)
|
||||||
|
expect(pattern.parts.partC.paths.a.ops[1].to.x).to.equal(11)
|
||||||
|
expect(pattern.parts.partC.paths.a.ops[1].to.y).to.equal(11)
|
||||||
|
expect(pattern.parts.partC.paths.b.ops[0].to.x).to.equal(2)
|
||||||
|
expect(pattern.parts.partC.paths.b.ops[0].to.y).to.equal(2)
|
||||||
|
expect(pattern.parts.partC.paths.b.ops[1].to.x).to.equal(22)
|
||||||
|
expect(pattern.parts.partC.paths.b.ops[1].to.y).to.equal(22)
|
||||||
|
expect(pattern.parts.partC.paths.c.ops[0].to.x).to.equal(3)
|
||||||
|
expect(pattern.parts.partC.paths.c.ops[0].to.y).to.equal(3)
|
||||||
|
expect(pattern.parts.partC.paths.c.ops[1].to.x).to.equal(33)
|
||||||
|
expect(pattern.parts.partC.paths.c.ops[1].to.y).to.equal(33)
|
||||||
|
// Paths in partR
|
||||||
|
expect(pattern.parts.partC.paths.a.ops[0].to.x).to.equal(1)
|
||||||
|
expect(pattern.parts.partC.paths.a.ops[0].to.y).to.equal(1)
|
||||||
|
expect(pattern.parts.partC.paths.a.ops[1].to.x).to.equal(11)
|
||||||
|
expect(pattern.parts.partC.paths.a.ops[1].to.y).to.equal(11)
|
||||||
|
expect(pattern.parts.partR.paths.r.ops[0].to.x).to.equal(4)
|
||||||
|
expect(pattern.parts.partR.paths.r.ops[0].to.y).to.equal(4)
|
||||||
|
expect(pattern.parts.partR.paths.r.ops[1].to.x).to.equal(44)
|
||||||
|
expect(pattern.parts.partR.paths.r.ops[1].to.y).to.equal(44)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Design constructor should resolve nested dependencies (2022)", () => {
|
||||||
|
const partA = {
|
||||||
|
name: "partA",
|
||||||
|
options: { optionA: { bool: true } },
|
||||||
|
measurements: [ 'measieA' ],
|
||||||
|
optionalMeasurements: [ 'optmeasieA' ],
|
||||||
|
draft: part => {
|
||||||
|
const { points, Point, paths, Path } = part.shorthand()
|
||||||
|
points.a1 = new Point(1,1)
|
||||||
|
points.a2 = new Point(11,11)
|
||||||
|
paths.a = new Path().move(points.a1).line(points.a2)
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const partB = {
|
||||||
|
name: "partB",
|
||||||
|
from: partA,
|
||||||
|
options: { optionB: { pct: 12, min: 2, max: 20 } },
|
||||||
|
measurements: [ 'measieB' ],
|
||||||
|
optionalMeasurements: [ 'optmeasieB', 'measieA' ],
|
||||||
|
draft: part => {
|
||||||
|
const { points, Point, paths, Path } = part.shorthand()
|
||||||
|
points.b1 = new Point(2,2)
|
||||||
|
points.b2 = new Point(22,22)
|
||||||
|
paths.b = new Path().move(points.b1).line(points.b2)
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const partC = {
|
||||||
|
name: "partC",
|
||||||
|
from: partB,
|
||||||
|
options: { optionC: { deg: 5, min: 0, max: 15 } },
|
||||||
|
measurements: [ 'measieC' ],
|
||||||
|
optionalMeasurements: [ 'optmeasieC', 'measieA' ],
|
||||||
|
draft: part => {
|
||||||
|
const { points, Point, paths, Path } = part.shorthand()
|
||||||
|
points.c1 = new Point(3,3)
|
||||||
|
points.c2 = new Point(33,33)
|
||||||
|
paths.c = new Path().move(points.c1).line(points.c2)
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const partD = {
|
||||||
|
name: "partD",
|
||||||
|
after: partC,
|
||||||
|
options: { optionD: { dflt: 'red', list: ['red', 'green', 'blue'] } },
|
||||||
|
measurements: [ 'measieD' ],
|
||||||
|
optionalMeasurements: [ 'optmeasieD', 'measieA' ],
|
||||||
|
draft: part => {
|
||||||
|
const { points, Point, paths, Path } = part.shorthand()
|
||||||
|
points.d1 = new Point(4,4)
|
||||||
|
points.d2 = new Point(44,44)
|
||||||
|
paths.d = new Path().move(points.d1).line(points.d2)
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const Design = new freesewing.Design({ parts: [ partD ] });
|
||||||
|
const pattern = new Design().draft()
|
||||||
|
// Measurements
|
||||||
|
expect(pattern.config.measurements.length).to.equal(4)
|
||||||
|
expect(pattern.config.measurements.indexOf('measieA') === -1).to.equal(false)
|
||||||
|
expect(pattern.config.measurements.indexOf('measieB') === -1).to.equal(false)
|
||||||
|
expect(pattern.config.measurements.indexOf('measieC') === -1).to.equal(false)
|
||||||
|
expect(pattern.config.measurements.indexOf('measieD') === -1).to.equal(false)
|
||||||
|
// Optional measurements
|
||||||
|
expect(pattern.config.optionalMeasurements.length).to.equal(4)
|
||||||
|
expect(pattern.config.optionalMeasurements.indexOf('optmeasieA') === -1).to.equal(false)
|
||||||
|
expect(pattern.config.optionalMeasurements.indexOf('optmeasieB') === -1).to.equal(false)
|
||||||
|
expect(pattern.config.optionalMeasurements.indexOf('optmeasieC') === -1).to.equal(false)
|
||||||
|
expect(pattern.config.optionalMeasurements.indexOf('optmeasieD') === -1).to.equal(false)
|
||||||
|
expect(pattern.config.optionalMeasurements.indexOf('measieA') === -1).to.equal(true)
|
||||||
|
// Options
|
||||||
|
expect(pattern.config.options.optionA.bool).to.equal(true)
|
||||||
|
expect(pattern.config.options.optionB.pct).to.equal(12)
|
||||||
|
expect(pattern.config.options.optionB.min).to.equal(2)
|
||||||
|
expect(pattern.config.options.optionB.max).to.equal(20)
|
||||||
|
expect(pattern.config.options.optionC.deg).to.equal(5)
|
||||||
|
expect(pattern.config.options.optionC.min).to.equal(0)
|
||||||
|
expect(pattern.config.options.optionC.max).to.equal(15)
|
||||||
|
expect(pattern.config.options.optionD.dflt).to.equal('red')
|
||||||
|
expect(pattern.config.options.optionD.list[0]).to.equal('red')
|
||||||
|
expect(pattern.config.options.optionD.list[1]).to.equal('green')
|
||||||
|
expect(pattern.config.options.optionD.list[2]).to.equal('blue')
|
||||||
|
// Dependencies
|
||||||
|
expect(pattern.config.dependencies.partB[0]).to.equal('partA')
|
||||||
|
expect(pattern.config.dependencies.partC[0]).to.equal('partB')
|
||||||
|
expect(pattern.config.dependencies.partD[0]).to.equal('partC')
|
||||||
|
// Inject
|
||||||
|
expect(pattern.config.inject.partB).to.equal('partA')
|
||||||
|
expect(pattern.config.inject.partC).to.equal('partB')
|
||||||
|
// Draft order
|
||||||
|
expect(pattern.config.draftOrder[0]).to.equal('partA')
|
||||||
|
expect(pattern.config.draftOrder[1]).to.equal('partB')
|
||||||
|
expect(pattern.config.draftOrder[2]).to.equal('partC')
|
||||||
|
expect(pattern.config.draftOrder[3]).to.equal('partD')
|
||||||
|
// Points
|
||||||
|
expect(pattern.parts.partA.points.a1.x).to.equal(1)
|
||||||
|
expect(pattern.parts.partA.points.a1.y).to.equal(1)
|
||||||
|
expect(pattern.parts.partA.points.a2.x).to.equal(11)
|
||||||
|
expect(pattern.parts.partA.points.a2.y).to.equal(11)
|
||||||
|
expect(pattern.parts.partB.points.b1.x).to.equal(2)
|
||||||
|
expect(pattern.parts.partB.points.b1.y).to.equal(2)
|
||||||
|
expect(pattern.parts.partB.points.b2.x).to.equal(22)
|
||||||
|
expect(pattern.parts.partB.points.b2.y).to.equal(22)
|
||||||
|
expect(pattern.parts.partC.points.c1.x).to.equal(3)
|
||||||
|
expect(pattern.parts.partC.points.c1.y).to.equal(3)
|
||||||
|
expect(pattern.parts.partC.points.c2.x).to.equal(33)
|
||||||
|
expect(pattern.parts.partC.points.c2.y).to.equal(33)
|
||||||
|
expect(pattern.parts.partD.points.d1.x).to.equal(4)
|
||||||
|
expect(pattern.parts.partD.points.d1.y).to.equal(4)
|
||||||
|
expect(pattern.parts.partD.points.d2.x).to.equal(44)
|
||||||
|
expect(pattern.parts.partD.points.d2.y).to.equal(44)
|
||||||
|
// Paths in partA
|
||||||
|
expect(pattern.parts.partA.paths.a.ops[0].to.x).to.equal(1)
|
||||||
|
expect(pattern.parts.partA.paths.a.ops[0].to.y).to.equal(1)
|
||||||
|
expect(pattern.parts.partA.paths.a.ops[1].to.x).to.equal(11)
|
||||||
|
expect(pattern.parts.partA.paths.a.ops[1].to.y).to.equal(11)
|
||||||
|
// Paths in partB
|
||||||
|
expect(pattern.parts.partB.paths.a.ops[0].to.x).to.equal(1)
|
||||||
|
expect(pattern.parts.partB.paths.a.ops[0].to.y).to.equal(1)
|
||||||
|
expect(pattern.parts.partB.paths.a.ops[1].to.x).to.equal(11)
|
||||||
|
expect(pattern.parts.partB.paths.a.ops[1].to.y).to.equal(11)
|
||||||
|
expect(pattern.parts.partB.paths.b.ops[0].to.x).to.equal(2)
|
||||||
|
expect(pattern.parts.partB.paths.b.ops[0].to.y).to.equal(2)
|
||||||
|
expect(pattern.parts.partB.paths.b.ops[1].to.x).to.equal(22)
|
||||||
|
expect(pattern.parts.partB.paths.b.ops[1].to.y).to.equal(22)
|
||||||
|
// Paths in partC
|
||||||
|
expect(pattern.parts.partC.paths.a.ops[0].to.x).to.equal(1)
|
||||||
|
expect(pattern.parts.partC.paths.a.ops[0].to.y).to.equal(1)
|
||||||
|
expect(pattern.parts.partC.paths.a.ops[1].to.x).to.equal(11)
|
||||||
|
expect(pattern.parts.partC.paths.a.ops[1].to.y).to.equal(11)
|
||||||
|
expect(pattern.parts.partC.paths.b.ops[0].to.x).to.equal(2)
|
||||||
|
expect(pattern.parts.partC.paths.b.ops[0].to.y).to.equal(2)
|
||||||
|
expect(pattern.parts.partC.paths.b.ops[1].to.x).to.equal(22)
|
||||||
|
expect(pattern.parts.partC.paths.b.ops[1].to.y).to.equal(22)
|
||||||
|
expect(pattern.parts.partC.paths.c.ops[0].to.x).to.equal(3)
|
||||||
|
expect(pattern.parts.partC.paths.c.ops[0].to.y).to.equal(3)
|
||||||
|
expect(pattern.parts.partC.paths.c.ops[1].to.x).to.equal(33)
|
||||||
|
expect(pattern.parts.partC.paths.c.ops[1].to.y).to.equal(33)
|
||||||
|
// Paths in partR
|
||||||
|
expect(pattern.parts.partD.paths.d.ops[0].to.x).to.equal(4)
|
||||||
|
expect(pattern.parts.partD.paths.d.ops[0].to.y).to.equal(4)
|
||||||
|
expect(pattern.parts.partD.paths.d.ops[1].to.x).to.equal(44)
|
||||||
|
expect(pattern.parts.partD.paths.d.ops[1].to.y).to.equal(44)
|
||||||
|
})
|
||||||
|
it("Pattern should merge optiongroups", () => {
|
||||||
|
const partA = {
|
||||||
|
name: "partA",
|
||||||
|
options: { optionA: { bool: true } },
|
||||||
|
measurements: [ 'measieA' ],
|
||||||
|
optionalMeasurements: [ 'optmeasieA' ],
|
||||||
|
optionGroups: {
|
||||||
|
simple: ['simplea1', 'simplea2', 'simplea3'],
|
||||||
|
nested: {
|
||||||
|
nested1: [ 'nested1a1', 'nested1a2', 'nested1a3' ],
|
||||||
|
},
|
||||||
|
subnested: {
|
||||||
|
subnested1: [
|
||||||
|
'subnested1a1',
|
||||||
|
'subnested1a2',
|
||||||
|
'subnested1a3',
|
||||||
|
{
|
||||||
|
subsubgroup: [
|
||||||
|
'subsuba1',
|
||||||
|
'subsuba2',
|
||||||
|
{
|
||||||
|
subsubsubgroup: [ 'subsubsub1', 'simplea1' ],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
draft: part => part,
|
||||||
|
}
|
||||||
|
const partB = {
|
||||||
|
name: "partB",
|
||||||
|
from: partA,
|
||||||
|
options: { optionB: { pct: 12, min: 2, max: 20 } },
|
||||||
|
measurements: [ 'measieB' ],
|
||||||
|
optionalMeasurements: [ 'optmeasieB', 'measieA' ],
|
||||||
|
optionGroups: {
|
||||||
|
simple: ['simpleb1', 'simpleb2', 'simpleb3'],
|
||||||
|
bsimple: ['bsimpleb1', 'bsimpleb2', 'bsimpleb3'],
|
||||||
|
nested: {
|
||||||
|
nested2: [ 'nested2b1', 'nested2b2', 'nested2b3' ],
|
||||||
|
},
|
||||||
|
subnested: {
|
||||||
|
subnested1: [
|
||||||
|
'subnested1b1',
|
||||||
|
'subnested1b2',
|
||||||
|
'subnested1b3',
|
||||||
|
{
|
||||||
|
subsubgroup: [
|
||||||
|
'subsubb1',
|
||||||
|
'subsubb2',
|
||||||
|
{
|
||||||
|
subsubsubgroup: [ 'bsubsubsub1', 'simplea1' ],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
draft: part => part,
|
||||||
|
}
|
||||||
|
let Design, pattern
|
||||||
|
try {
|
||||||
|
Design = new freesewing.Design({ parts: [ partB ] });
|
||||||
|
pattern = new Design().init()
|
||||||
|
} catch(err) {
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
const og = pattern.config.optionGroups
|
||||||
|
expect(og.simple.length).to.equal(6)
|
||||||
|
expect(og.simple.indexOf('simplea1') === -1).to.equal(false)
|
||||||
|
expect(og.simple.indexOf('simplea2') === -1).to.equal(false)
|
||||||
|
expect(og.simple.indexOf('simplea3') === -1).to.equal(false)
|
||||||
|
expect(og.simple.indexOf('simpleb1') === -1).to.equal(false)
|
||||||
|
expect(og.simple.indexOf('simpleb2') === -1).to.equal(false)
|
||||||
|
expect(og.simple.indexOf('simpleb3') === -1).to.equal(false)
|
||||||
|
expect(og.nested.nested1.length).to.equal(3)
|
||||||
|
expect(og.nested.nested1.indexOf('nested1a1') === -1).to.equal(false)
|
||||||
|
expect(og.nested.nested1.indexOf('nested1a2') === -1).to.equal(false)
|
||||||
|
expect(og.nested.nested1.indexOf('nested1a3') === -1).to.equal(false)
|
||||||
|
expect(og.nested.nested2.length).to.equal(3)
|
||||||
|
expect(og.nested.nested2.indexOf('nested2b1') === -1).to.equal(false)
|
||||||
|
expect(og.nested.nested2.indexOf('nested2b2') === -1).to.equal(false)
|
||||||
|
expect(og.nested.nested2.indexOf('nested2b3') === -1).to.equal(false)
|
||||||
|
expect(og.subnested.subnested1.length).to.equal(8)
|
||||||
|
expect(og.subnested.subnested1.indexOf('subnested1a1') === -1).to.equal(false)
|
||||||
|
expect(og.subnested.subnested1.indexOf('subnested1a2') === -1).to.equal(false)
|
||||||
|
expect(og.subnested.subnested1.indexOf('subnested1a3') === -1).to.equal(false)
|
||||||
|
expect(og.subnested.subnested1.indexOf('subnested1b1') === -1).to.equal(false)
|
||||||
|
expect(og.subnested.subnested1.indexOf('subnested1b2') === -1).to.equal(false)
|
||||||
|
expect(og.subnested.subnested1.indexOf('subnested1b3') === -1).to.equal(false)
|
||||||
|
// FIXME: Some work to be done still with deep-nesting of groups with the same name
|
||||||
|
})
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue