1
0
Fork 0

wip: Allow runtime adding of parts

Just call `pattern.addPart()` and pass it either:
 - a part object with all it entails
 - a draft method as first and name as second parameter

This will overwrite any existing parts without any warning
This commit is contained in:
joostdecock 2022-08-13 15:11:33 +02:00
parent 2eee247676
commit 689f908f68
4 changed files with 111 additions and 56 deletions

View file

@ -1,51 +1,5 @@
import Pattern from './pattern'
// Add part-level options
const addOptions = (part, config) => {
if (part.options) {
for (const optionName in part.options) {
config.options[optionName] = part.options[optionName]
}
}
if (part.from) addOptions(part.from, config)
return config
}
// Add part-level measurements
const addMeasurements = (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) addMeasurements(part.from, config, list)
// Weed out duplicates
config.measurements = [...new Set(list)]
return config
}
// Add part-level optional measurements
const addOptionalMeasurements = (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) addOptionalMeasurements(part.from, config, list)
// Weed out duplicates
config.optionalMeasurements = [...new Set(list)]
return config
}
import { addOptions, addMeasurements, addOptionalMeasurements } from './utils.js'
/*
* The Design constructor. Returns a Pattern constructor

View file

@ -1,4 +1,12 @@
import { macroName, sampleStyle, capitalize, decoratePartDependency } from './utils'
import {
macroName,
sampleStyle,
capitalize,
decoratePartDependency,
addOptions,
addMeasurements,
addOptionalMeasurements
} from './utils.js'
import Part from './part'
import Point from './point'
import Path from './path'
@ -243,10 +251,17 @@ Pattern.prototype.runHooks = function (hookName, data = false) {
/*
* Allows adding a part at run-time
*/
Pattern.prototype.addPart = function (part, name=false, key) {
if (!part.draft) part = decoratePartDependency(part, givenName)
Pattern.prototype.addPart = function (part, name=false) {
if (!part.draft) part = decoratePartDependency(part, name)
if (typeof part?.draft === 'function') {
this.__parts[part.name] = part
if (part.name) {
this.config.parts[part.name] = part
// Add part options/measurements/optionalMeasurements to config
this.config = addOptions(part, this.config)
this.config = addMeasurements(part, this.config)
this.config = addOptionalMeasurements(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`)

View file

@ -388,3 +388,52 @@ export const generatePartTransform = (x, y, rotate, flipX, flipY, part) => {
* Makes sure an object passed to be attached as a part it not merely a method
*/
export const decoratePartDependency = (obj, name) => (typeof obj === 'function') ? { draft: obj, name } : obj
// Add part-level options
export const addOptions = (part, config) => {
if (part.options) {
for (const optionName in part.options) {
config.options[optionName] = part.options[optionName]
}
}
if (part.from) addOptions(part.from, config)
return config
}
// Add part-level measurements
export const addMeasurements = (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) addMeasurements(part.from, config, list)
// Weed out duplicates
config.measurements = [...new Set(list)]
return config
}
// Add part-level optional measurements
export const addOptionalMeasurements = (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) addOptionalMeasurements(part.from, config, list)
// Weed out duplicates
config.optionalMeasurements = [...new Set(list)]
return config
}

View file

@ -723,6 +723,8 @@ it("Should retrieve the cutList", () => {
});
// 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 dependencies (2022)", () => {
const partA = {
name: "partA",
@ -765,19 +767,35 @@ it("Design constructor should resolve nested dependencies (2022)", () => {
return part
}
}
const partR = { // R for runtime, which is when this wil be attached
name: "partR",
from: partA,
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().draft()
const Design = new freesewing.Design({ parts: { partC } });
const pattern = new Design().addPart(partR).draft()
// Measurements
expect(pattern.config.measurements.length).to.equal(3)
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(3)
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)
@ -787,17 +805,23 @@ it("Design constructor should resolve nested dependencies (2022)", () => {
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('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[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)
@ -811,6 +835,10 @@ it("Design constructor should resolve nested dependencies (2022)", () => {
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)
@ -838,4 +866,13 @@ it("Design constructor should resolve nested dependencies (2022)", () => {
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)
})