1
0
Fork 0

wip: Recursive resolving of (non-injected) dependencies

We started out with following dependencies that are injected
(from) and now added dependencies that are merely required to
be drafted first (after).

This also adds further support for part-level configuration.
This commit is contained in:
joostdecock 2022-08-14 16:59:51 +02:00
parent 4cf9c3bd47
commit 2b254c721d
3 changed files with 203 additions and 13 deletions

View file

@ -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)
}
const newlen = Object.keys(this.__parts).length
else this.addDependency(name, part, part.after)
}
}
// 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 */

View file

@ -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
}

View file

@ -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)
})