diff --git a/packages/core/src/pattern.js b/packages/core/src/pattern.js index 47115d07fc6..498ca166f16 100644 --- a/packages/core/src/pattern.js +++ b/packages/core/src/pattern.js @@ -4,6 +4,7 @@ import { capitalize, decoratePartDependency, addPartConfig, + mergeDependencies, } from './utils.js' import Part from './part' import Point from './point' @@ -215,7 +216,7 @@ function snappedOption(option, pattern) { // Merges settings object with this.settings Pattern.prototype.apply = function (settings) { if (typeof settings !== 'object') { - this.raise.warning('Pattern initialized without any settings') + this.raise.warning('Pattern instantiated without any settings') return this } for (let key of Object.keys(settings)) { @@ -678,23 +679,57 @@ Pattern.prototype.resolveDependency = function ( 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) { - const len = Object.keys(this.__parts).length 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) + } } - const newlen = Object.keys(this.__parts).length + // Did we discover any new dependencies? + const len = Object.keys(this.__parts).length - return (Object.keys(this.__parts).length > len) - ? this.preresolveDependencies() - : this + 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 */ diff --git a/packages/core/src/utils.js b/packages/core/src/utils.js index f3d9a1a2f63..35fcff2397d 100644 --- a/packages/core/src/utils.js +++ b/packages/core/src/utils.js @@ -437,7 +437,7 @@ const addPartOptionalMeasurements = (part, config, list=false) => { } -const addDependencies = (dep, current) => { +export const mergeDependencies = (dep=[], current=[]) => { // Current dependencies const list = [] if (Array.isArray(current)) list.push(...current) @@ -446,13 +446,21 @@ const addDependencies = (dep, current) => { if (Array.isArray(dep)) list.push(...dep) else if (typeof dep === 'string') list.push(dep) - return [...new Set(list)] + // 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) { - config.dependencies[part.name] = addDependencies(config.dependencies[part.name], part.after) + if (typeof config.dependencies === 'undefined') config.dependencies = {} + config.dependencies[part.name] = mergeDependencies(config.dependencies[part.name], part.after) } return config @@ -467,4 +475,3 @@ export const addPartConfig = (part, config) => { return config } - diff --git a/packages/core/tests/pattern.test.js b/packages/core/tests/pattern.test.js index 539d39af50f..91f05df2a3c 100644 --- a/packages/core/tests/pattern.test.js +++ b/packages/core/tests/pattern.test.js @@ -1,6 +1,6 @@ let expect = require("chai").expect; let freesewing = require("../dist/index.js"); - +/* it("Pattern constructor should initialize object", () => { let pattern = new freesewing.Pattern({ foo: "bar", @@ -725,7 +725,7 @@ 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)", () => { +it("Design constructor should resolve nested injections (2022)", () => { const partA = { name: "partA", options: { optionA: { bool: true } }, @@ -770,6 +770,7 @@ it("Design constructor should resolve nested dependencies (2022)", () => { 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' ], @@ -812,7 +813,8 @@ it("Design constructor should resolve nested dependencies (2022)", () => { // 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') + 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') @@ -876,3 +878,149 @@ it("Design constructor should resolve nested dependencies (2022)", () => { 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) +})