feat(core): Added support for multiple sets of settings
This changes they was settings (what the user provides) are handled. Before this, settings were passed as an object and that was it. Now, settings are treated as an array of settings objects and this adds full support for managing multiple sets of settings in a single pattern instance. This is the mechanism that's used for FreeSewing's sampling which used to be a rather hackish implementation, but now merely sets up the relevant list of settings, and then calls `pattern.draft()` as usual. Things to be mindful of is that parts, the store and settings themselves are tied to each set of settings. So where they used to be an object, they are now an array of object.
This commit is contained in:
parent
11a2a1dd1c
commit
0b18d81e14
7 changed files with 397 additions and 252 deletions
|
@ -2,6 +2,7 @@ export const loadDesignDefaults = () => ({
|
||||||
measurements: [],
|
measurements: [],
|
||||||
optionalMeasurements: [],
|
optionalMeasurements: [],
|
||||||
options: {},
|
options: {},
|
||||||
|
optionDistance: {},
|
||||||
parts: [],
|
parts: [],
|
||||||
data: {},
|
data: {},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
|
@ -10,6 +11,7 @@ export const loadDesignDefaults = () => ({
|
||||||
export const loadPatternDefaults = () => ({
|
export const loadPatternDefaults = () => ({
|
||||||
complete: true,
|
complete: true,
|
||||||
idPrefix: 'fs-',
|
idPrefix: 'fs-',
|
||||||
|
stackPrefix: '',
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
units: 'metric',
|
units: 'metric',
|
||||||
margin: 2,
|
margin: 2,
|
||||||
|
|
|
@ -10,12 +10,12 @@ export function Design(config) {
|
||||||
config = { ...loadDesignDefaults(), ...config }
|
config = { ...loadDesignDefaults(), ...config }
|
||||||
|
|
||||||
// Create the pattern constructor
|
// Create the pattern constructor
|
||||||
const pattern = function (settings) {
|
const pattern = function (...sets) {
|
||||||
// Pass the design config
|
// Pass the design config
|
||||||
Pattern.call(this, config)
|
Pattern.call(this, config)
|
||||||
|
|
||||||
// Pass the pattern settings
|
// Pass the pattern settings
|
||||||
return this.__applySettings(settings)
|
return this.__applySettings(sets)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up inheritance
|
// Set up inheritance
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Attributes } from './attributes.mjs'
|
import { Attributes } from './attributes.mjs'
|
||||||
import { Design } from './design.mjs'
|
import { Design } from './design.mjs'
|
||||||
import { Pattern } from './pattern.mjs'
|
import { Pattern } from './pattern.mjs'
|
||||||
|
import { Part } from './part.mjs'
|
||||||
import { Point } from './point.mjs'
|
import { Point } from './point.mjs'
|
||||||
import { Path } from './path.mjs'
|
import { Path } from './path.mjs'
|
||||||
import { Snippet } from './snippet.mjs'
|
import { Snippet } from './snippet.mjs'
|
||||||
|
@ -44,6 +45,7 @@ export {
|
||||||
Pattern,
|
Pattern,
|
||||||
Point,
|
Point,
|
||||||
Path,
|
Path,
|
||||||
|
Part,
|
||||||
Snippet,
|
Snippet,
|
||||||
Store,
|
Store,
|
||||||
Bezier,
|
Bezier,
|
||||||
|
|
|
@ -336,21 +336,21 @@ Part.prototype.shorthand = function () {
|
||||||
return shorthand
|
return shorthand
|
||||||
}
|
}
|
||||||
|
|
||||||
Part.prototype.isEmpty = function () {
|
//Part.prototype.isEmpty = function () {
|
||||||
if (Object.keys(this.snippets).length > 0) return false
|
// if (Object.keys(this.snippets).length > 0) return false
|
||||||
|
//
|
||||||
if (Object.keys(this.paths).length > 0) {
|
// if (Object.keys(this.paths).length > 0) {
|
||||||
for (const p in this.paths) {
|
// for (const p in this.paths) {
|
||||||
if (this.paths[p].render && this.paths[p].length()) return false
|
// if (this.paths[p].render && this.paths[p].length()) return false
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
for (const p in this.points) {
|
// for (const p in this.points) {
|
||||||
if (this.points[p].attributes.get('data-text')) return false
|
// if (this.points[p].attributes.get('data-text')) return false
|
||||||
if (this.points[p].attributes.get('data-circle')) return false
|
// if (this.points[p].attributes.get('data-circle')) return false
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
return true
|
// return true
|
||||||
}
|
//}
|
||||||
|
|
||||||
export default Part
|
export default Part
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { loadPatternDefaults } from './config.mjs'
|
||||||
export function Pattern(config) {
|
export function Pattern(config) {
|
||||||
// Non-enumerable properties
|
// Non-enumerable properties
|
||||||
addNonEnumProp(this, 'plugins', {})
|
addNonEnumProp(this, 'plugins', {})
|
||||||
|
addNonEnumProp(this, 'parts', [{}])
|
||||||
addNonEnumProp(this, 'width', 0)
|
addNonEnumProp(this, 'width', 0)
|
||||||
addNonEnumProp(this, 'height', 0)
|
addNonEnumProp(this, 'height', 0)
|
||||||
addNonEnumProp(this, 'autoLayout', { stacks: {} })
|
addNonEnumProp(this, 'autoLayout', { stacks: {} })
|
||||||
|
@ -40,9 +41,8 @@ export function Pattern(config) {
|
||||||
|
|
||||||
// Enumerable properties
|
// Enumerable properties
|
||||||
this.config = config // Design config
|
this.config = config // Design config
|
||||||
this.parts = {} // Drafted parts container
|
|
||||||
this.stacks = {} // Drafted stacks container
|
this.stacks = {} // Drafted stacks container
|
||||||
this.store = new Store() // Store for sharing data across parts
|
this.stores = [new Store()]
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -65,36 +65,42 @@ Pattern.prototype.init = function () {
|
||||||
.__loadOptionDefaults() // Merges default options with user provided ones
|
.__loadOptionDefaults() // Merges default options with user provided ones
|
||||||
|
|
||||||
// Say hello
|
// Say hello
|
||||||
this.store.log.info(
|
this.stores[0].log.info(
|
||||||
`New \`${this.store.get('data.name', 'No Name')}:` +
|
`New \`${this.stores[0].get('data.name', 'No Name')}:` +
|
||||||
`${this.store.get(
|
`${this.stores[0].get(
|
||||||
'data.version',
|
'data.version',
|
||||||
'No version'
|
'No version'
|
||||||
)}\` pattern using \`@freesewing/core:${version}\``
|
)}\` pattern using \`@freesewing/core:${version}\``
|
||||||
)
|
)
|
||||||
this.store.log.info(`Pattern initialized. Draft order is: ${this.__draftOrder.join(', ')}`)
|
this.stores[0].log.info(`Pattern initialized. Draft order is: ${this.__draftOrder.join(', ')}`)
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern.prototype.__loadConfigData = function () {
|
Pattern.prototype.__loadConfigData = function () {
|
||||||
if (this.config.data) this.store.set('data', this.config.data)
|
if (this.config.data) {
|
||||||
|
for (const i in this.settings) this.stores[i].set('data', this.config.data)
|
||||||
|
}
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern.prototype.__createPartWithContext = function (name) {
|
Pattern.prototype.__createPartWithContext = function (name, set) {
|
||||||
// Context object to add to Part closure
|
// Context object to add to Part closure
|
||||||
const part = new Part()
|
const part = new Part()
|
||||||
part.name = name
|
part.name = name
|
||||||
|
part.set = set
|
||||||
part.stack = this.__parts[name]?.stack || name
|
part.stack = this.__parts[name]?.stack || name
|
||||||
part.context = {
|
part.context = {
|
||||||
parts: this.parts,
|
parts: this.parts[set],
|
||||||
config: this.config,
|
config: this.config,
|
||||||
settings: this.settings,
|
settings: this.settings[set],
|
||||||
store: this.store,
|
store: this.stores[set],
|
||||||
macros: this.macros,
|
macros: this.macros,
|
||||||
}
|
}
|
||||||
|
if (this.settings[set]?.partClasses) {
|
||||||
|
part.attr('class', this.settings[set].partClasses)
|
||||||
|
}
|
||||||
|
|
||||||
for (const macro in this.macros) {
|
for (const macro in this.macros) {
|
||||||
part[macroName(macro)] = this.macros[macro]
|
part[macroName(macro)] = this.macros[macro]
|
||||||
|
@ -110,7 +116,7 @@ Pattern.prototype.__createStackWithContext = function (name) {
|
||||||
stack.context = {
|
stack.context = {
|
||||||
config: this.config,
|
config: this.config,
|
||||||
settings: this.settings,
|
settings: this.settings,
|
||||||
store: this.store,
|
stores: this.stores,
|
||||||
}
|
}
|
||||||
|
|
||||||
return stack
|
return stack
|
||||||
|
@ -119,22 +125,25 @@ Pattern.prototype.__createStackWithContext = function (name) {
|
||||||
// Merges default for options with user-provided options
|
// Merges default for options with user-provided options
|
||||||
Pattern.prototype.__loadOptionDefaults = function () {
|
Pattern.prototype.__loadOptionDefaults = function () {
|
||||||
if (Object.keys(this.config.options).length < 1) return this
|
if (Object.keys(this.config.options).length < 1) return this
|
||||||
for (const [name, option] of Object.entries(this.config.options)) {
|
for (const i in this.settings) {
|
||||||
// Don't overwrite user-provided settings.options
|
for (const [name, option] of Object.entries(this.config.options)) {
|
||||||
if (typeof this.settings.options[name] === 'undefined') {
|
// Don't overwrite user-provided settings.options
|
||||||
if (typeof option === 'object') {
|
if (typeof this.settings[i].options[name] === 'undefined') {
|
||||||
if (typeof option.pct !== 'undefined') this.settings.options[name] = option.pct / 100
|
if (typeof option === 'object') {
|
||||||
else if (typeof option.mm !== 'undefined') this.settings.options[name] = option.mm
|
if (typeof option.pct !== 'undefined') this.settings[i].options[name] = option.pct / 100
|
||||||
else if (typeof option.deg !== 'undefined') this.settings.options[name] = option.deg
|
else if (typeof option.mm !== 'undefined') this.settings[i].options[name] = option.mm
|
||||||
else if (typeof option.count !== 'undefined') this.settings.options[name] = option.count
|
else if (typeof option.deg !== 'undefined') this.settings[i].options[name] = option.deg
|
||||||
else if (typeof option.bool !== 'undefined') this.settings.options[name] = option.bool
|
else if (typeof option.count !== 'undefined')
|
||||||
else if (typeof option.dflt !== 'undefined') this.settings.options[name] = option.dflt
|
this.settings[i].options[name] = option.count
|
||||||
else {
|
else if (typeof option.bool !== 'undefined') this.settings[i].options[name] = option.bool
|
||||||
let err = 'Unknown option type: ' + JSON.stringify(option)
|
else if (typeof option.dflt !== 'undefined') this.settings[i].options[name] = option.dflt
|
||||||
this.store.log.error(err)
|
else {
|
||||||
throw new Error(err)
|
let err = 'Unknown option type: ' + JSON.stringify(option)
|
||||||
}
|
this.stores[i].log.error(err)
|
||||||
} else this.settings.options[name] = option
|
throw new Error(err)
|
||||||
|
}
|
||||||
|
} else this.settings[i].options[name] = option
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,12 +162,12 @@ Pattern.prototype.getPartList = function () {
|
||||||
return Object.keys(this.config.parts)
|
return Object.keys(this.config.parts)
|
||||||
}
|
}
|
||||||
|
|
||||||
function snappedOption(option, pattern) {
|
Pattern.prototype.__snappedPercentageOption = function (optionName, set) {
|
||||||
const conf = pattern.config.options[option]
|
const conf = this.config.options[optionName]
|
||||||
const abs = conf.toAbs(pattern.settings.options[option], pattern.settings)
|
const abs = conf.toAbs(this.settings[set].options[optionName], this.settings[set])
|
||||||
// Handle units-specific config
|
// Handle units-specific config
|
||||||
if (!Array.isArray(conf.snap) && conf.snap.metric && conf.snap.imperial)
|
if (!Array.isArray(conf.snap) && conf.snap.metric && conf.snap.imperial)
|
||||||
conf.snap = conf.snap[pattern.settings.units]
|
conf.snap = conf.snap[this.settings[set].units]
|
||||||
// Simple steps
|
// Simple steps
|
||||||
if (typeof conf.snap === 'number') return Math.ceil(abs / conf.snap) * conf.snap
|
if (typeof conf.snap === 'number') return Math.ceil(abs / conf.snap) * conf.snap
|
||||||
// List of snaps
|
// List of snaps
|
||||||
|
@ -187,7 +196,7 @@ Pattern.prototype.runHooks = function (hookName, data = false) {
|
||||||
if (data === false) data = this
|
if (data === false) data = this
|
||||||
let hooks = this.hooks[hookName]
|
let hooks = this.hooks[hookName]
|
||||||
if (hooks.length > 0) {
|
if (hooks.length > 0) {
|
||||||
this.store.log.debug(`Running \`${hookName}\` hooks`)
|
this.stores[0].log.debug(`Running \`${hookName}\` hooks`)
|
||||||
for (let hook of hooks) {
|
for (let hook of hooks) {
|
||||||
hook.method(data, hook.data)
|
hook.method(data, hook.data)
|
||||||
}
|
}
|
||||||
|
@ -202,9 +211,30 @@ Pattern.prototype.addPart = function (part) {
|
||||||
if (part.name) {
|
if (part.name) {
|
||||||
this.config.parts[part.name] = part
|
this.config.parts[part.name] = part
|
||||||
// Add part-level config to config
|
// Add part-level config to config
|
||||||
this.config = addPartConfig(part, this.config, this.store)
|
this.config = addPartConfig(part, this.config, this.stores[0])
|
||||||
} else this.store.log.error(`Part must have a name`)
|
} else this.stores[0].log.error(`Part must have a name`)
|
||||||
} else this.store.log.error(`Part must have a draft() method`)
|
} else this.stores[0].log.error(`Part must have a draft() method`)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
Pattern.prototype.__loadAbsoluteOptionsSet = function (set) {
|
||||||
|
for (const optionName in this.settings[set].options) {
|
||||||
|
const option = this.config.options[optionName]
|
||||||
|
if (
|
||||||
|
typeof option !== 'undefined' &&
|
||||||
|
typeof option.snap !== 'undefined' &&
|
||||||
|
option.toAbs instanceof Function
|
||||||
|
) {
|
||||||
|
this.settings[set].absoluteOptions[optionName] = this.__snappedPercentageOption(
|
||||||
|
optionName,
|
||||||
|
set
|
||||||
|
)
|
||||||
|
this.stores[set].log.debug(
|
||||||
|
`🧲 Snapped __${optionName}__ to \`${this.settings[set].absoluteOptions[optionName]}\` for set __${set}__`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -216,66 +246,68 @@ Pattern.prototype.draft = function () {
|
||||||
// Late-stage initialization
|
// Late-stage initialization
|
||||||
this.init()
|
this.init()
|
||||||
|
|
||||||
if (this.is !== 'sample') {
|
// Iterate over the provided sets of settings (typically just one)
|
||||||
this.is = 'draft'
|
for (const set in this.settings) {
|
||||||
this.store.log.debug(`Drafting pattern`)
|
// Set store
|
||||||
}
|
this.stores[set].log.debug(`📐 Drafting pattern (set ${set})`)
|
||||||
// Handle snap for pct options
|
|
||||||
for (const i in this.settings.options) {
|
|
||||||
if (
|
|
||||||
typeof this.config.options[i] !== 'undefined' &&
|
|
||||||
typeof this.config.options[i].snap !== 'undefined' &&
|
|
||||||
this.config.options[i].toAbs instanceof Function
|
|
||||||
) {
|
|
||||||
this.settings.absoluteOptions[i] = snappedOption(i, this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.runHooks('preDraft')
|
// Create parts container
|
||||||
for (const partName of this.config.draftOrder) {
|
this.parts[set] = {}
|
||||||
// Create parts
|
|
||||||
this.store.log.debug(`Creating part \`${partName}\``)
|
// Handle snap for pct options
|
||||||
this.parts[partName] = this.__createPartWithContext(partName)
|
this.__loadAbsoluteOptionsSet(set)
|
||||||
// Handle inject/inheritance
|
|
||||||
if (typeof this.__inject[partName] === 'string') {
|
this.runHooks('preDraft')
|
||||||
this.store.log.debug(`Creating part \`${partName}\` from part \`${this.__inject[partName]}\``)
|
for (const partName of this.config.draftOrder) {
|
||||||
try {
|
// Create parts
|
||||||
this.parts[partName].inject(this.parts[this.__inject[partName]])
|
this.stores[set].log.debug(`📦 Creating part \`${partName}\` (set ${set})`)
|
||||||
} catch (err) {
|
this.parts[set][partName] = this.__createPartWithContext(partName, set)
|
||||||
this.store.log.error([
|
// Handle inject/inheritance
|
||||||
`Could not inject part \`${this.inject[partName]}\` into part \`${partName}\``,
|
if (typeof this.__inject[partName] === 'string') {
|
||||||
err,
|
this.stores[set].log.debug(
|
||||||
])
|
`Creating part \`${partName}\` from part \`${this.__inject[partName]}\``
|
||||||
}
|
)
|
||||||
}
|
|
||||||
if (this.needs(partName)) {
|
|
||||||
// Draft part
|
|
||||||
if (typeof this.__parts?.[partName]?.draft === 'function') {
|
|
||||||
try {
|
try {
|
||||||
const result = this.__parts[partName].draft(this.parts[partName].shorthand())
|
this.parts[set][partName].inject(this.parts[set][this.__inject[partName]])
|
||||||
if (typeof result === 'undefined') {
|
|
||||||
this.store.log.error(
|
|
||||||
`Result of drafting part ${partName} was undefined. Did you forget to return the part?`
|
|
||||||
)
|
|
||||||
} else this.parts[partName] = result
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.store.log.error([`Unable to draft part \`${partName}\``, err])
|
this.stores[set].log.error([
|
||||||
|
`Could not inject part \`${this.__inject[partName]}\` into part \`${partName}\``,
|
||||||
|
err,
|
||||||
|
])
|
||||||
}
|
}
|
||||||
} else this.store.log.error(`Unable to draft pattern. Part.draft() is not callable`)
|
|
||||||
try {
|
|
||||||
this.parts[partName].render =
|
|
||||||
this.parts[partName].render === false ? false : this.wants(partName)
|
|
||||||
} catch (err) {
|
|
||||||
this.store.log.error([`Unable to set \`render\` property on part \`${partName}\``, err])
|
|
||||||
}
|
}
|
||||||
} else {
|
if (this.needs(partName, set)) {
|
||||||
this.store.log.debug(
|
// Draft part
|
||||||
`Part \`${partName}\` is not needed. Skipping draft and setting render to \`false\``
|
if (typeof this.__parts?.[partName]?.draft === 'function') {
|
||||||
)
|
try {
|
||||||
this.parts[partName].render = false
|
const result = this.__parts[partName].draft(this.parts[set][partName].shorthand())
|
||||||
|
if (typeof result === 'undefined') {
|
||||||
|
this.stores[set].log.error(
|
||||||
|
`Result of drafting part ${partName} was undefined. Did you forget to return the part?`
|
||||||
|
)
|
||||||
|
} else this.parts[set][partName] = result
|
||||||
|
} catch (err) {
|
||||||
|
this.stores[set].log.error([`Unable to draft part \`${partName}\` (set ${set})`, err])
|
||||||
|
}
|
||||||
|
} else this.stores[set].log.error(`Unable to draft pattern. Part.draft() is not callable`)
|
||||||
|
try {
|
||||||
|
this.parts[set][partName].render =
|
||||||
|
this.parts[set][partName].render === false ? false : this.wants(partName, set)
|
||||||
|
} catch (err) {
|
||||||
|
this.stores[set].log.error([
|
||||||
|
`Unable to set \`render\` property on part \`${partName}\``,
|
||||||
|
err,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.stores[set].log.debug(
|
||||||
|
`Part \`${partName}\` is not needed. Skipping draft and setting render to \`false\``
|
||||||
|
)
|
||||||
|
this.parts[set][partName].render = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
this.runHooks('postDraft')
|
||||||
}
|
}
|
||||||
this.runHooks('postDraft')
|
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -284,28 +316,26 @@ Pattern.prototype.draft = function () {
|
||||||
* Handles pattern sampling
|
* Handles pattern sampling
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.sample = function () {
|
Pattern.prototype.sample = function () {
|
||||||
// Late-stage initialization
|
if (this.settings[0].sample.type === 'option') {
|
||||||
this.init()
|
return this.sampleOption(this.settings[0].sample.option)
|
||||||
if (this.settings.sample.type === 'option') {
|
} else if (this.settings[0].sample.type === 'measurement') {
|
||||||
return this.sampleOption(this.settings.sample.option)
|
return this.sampleMeasurement(this.settings[0].sample.measurement)
|
||||||
} else if (this.settings.sample.type === 'measurement') {
|
|
||||||
return this.sampleMeasurement(this.settings.sample.measurement)
|
|
||||||
} else if (this.settings.sample.type === 'models') {
|
} else if (this.settings.sample.type === 'models') {
|
||||||
return this.sampleModels(this.settings.sample.models, this.settings.sample.focus || false)
|
return this.sampleModels(this.settings[0].sample.models, this.settings[0].sample.focus || false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern.prototype.sampleParts = function () {
|
//Pattern.prototype.sampleParts = function () {
|
||||||
let parts = {}
|
// let parts = {}
|
||||||
this.settings.complete = false
|
// this.settings.complete = false
|
||||||
this.settings.paperless = false
|
// this.settings.paperless = false
|
||||||
this.draft()
|
// this.draft()
|
||||||
for (let i in this.parts) {
|
// for (let i in this.parts) {
|
||||||
parts[i] = new this.Part()
|
// parts[i] = new this.Part()
|
||||||
parts[i].render = this.parts[i].render
|
// parts[i].render = this.parts[i].render
|
||||||
}
|
// }
|
||||||
return parts
|
// return parts
|
||||||
}
|
//}
|
||||||
|
|
||||||
Pattern.prototype.sampleRun = function (parts, anchors, run, runs, extraClass = false) {
|
Pattern.prototype.sampleRun = function (parts, anchors, run, runs, extraClass = false) {
|
||||||
this.draft()
|
this.draft()
|
||||||
|
@ -345,109 +375,175 @@ Pattern.prototype.sampleRun = function (parts, anchors, run, runs, extraClass =
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
Pattern.prototype.__setBase = function () {
|
||||||
* Handles option sampling
|
return {
|
||||||
*/
|
...this.settings[0],
|
||||||
Pattern.prototype.sampleOption = function (optionName) {
|
measurements: { ...(this.settings[0].measurements || {}) },
|
||||||
this.is = 'sample'
|
options: { ...(this.settings[0].options || {}) },
|
||||||
this.store.log.debug(`Sampling option \`${optionName}\``)
|
|
||||||
this.runHooks('preSample')
|
|
||||||
let step, val
|
|
||||||
let factor = 1
|
|
||||||
let anchors = {}
|
|
||||||
let parts = this.sampleParts()
|
|
||||||
let option = this.config.options[optionName]
|
|
||||||
if (typeof option.list === 'object') {
|
|
||||||
return this.sampleListOption(optionName)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Pattern.prototype.__listOptionSets = function (optionName) {
|
||||||
|
let option = this.config.options[optionName]
|
||||||
|
const base = this.__setBase()
|
||||||
|
const sets = []
|
||||||
|
let run = 1
|
||||||
|
for (const choice of option.list) {
|
||||||
|
const settings = {
|
||||||
|
...base,
|
||||||
|
options: {
|
||||||
|
...base.options,
|
||||||
|
},
|
||||||
|
idPrefix: `sample-${run}`,
|
||||||
|
partClasses: `sample-${run}`,
|
||||||
|
}
|
||||||
|
settings.options[optionName] = choice
|
||||||
|
sets.push(settings)
|
||||||
|
run++
|
||||||
|
}
|
||||||
|
|
||||||
|
return sets
|
||||||
|
}
|
||||||
|
|
||||||
|
Pattern.prototype.__optionSets = function (optionName) {
|
||||||
|
let option = this.config.options[optionName]
|
||||||
|
if (typeof option.list === 'object') return this.__listOptionSets(optionName)
|
||||||
|
const sets = []
|
||||||
|
let factor = 1
|
||||||
|
let step, val
|
||||||
if (typeof option.min === 'undefined' || typeof option.max === 'undefined') {
|
if (typeof option.min === 'undefined' || typeof option.max === 'undefined') {
|
||||||
let min = option * 0.9
|
const min = option * 0.9
|
||||||
let max = option * 1.1
|
const max = option * 1.1
|
||||||
option = { min, max }
|
option = { min, max }
|
||||||
}
|
}
|
||||||
if (typeof option.pct !== 'undefined') factor = 100
|
if (typeof option.pct !== 'undefined') factor = 100
|
||||||
val = option.min / factor
|
val = option.min / factor
|
||||||
step = (option.max / factor - val) / 9
|
step = (option.max / factor - val) / 9
|
||||||
|
const base = this.__setBase()
|
||||||
for (let run = 1; run < 11; run++) {
|
for (let run = 1; run < 11; run++) {
|
||||||
this.settings.options[optionName] = val
|
const settings = {
|
||||||
this.sampleRun(parts, anchors, run, 10)
|
...base,
|
||||||
|
options: {
|
||||||
|
...base.options,
|
||||||
|
},
|
||||||
|
idPrefix: `sample-${run}`,
|
||||||
|
partClasses: `sample-${run}`,
|
||||||
|
}
|
||||||
|
settings.options[optionName] = val
|
||||||
|
sets.push(settings)
|
||||||
val += step
|
val += step
|
||||||
}
|
}
|
||||||
this.parts = parts
|
|
||||||
this.runHooks('postSample')
|
|
||||||
|
|
||||||
return this
|
return sets
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern.prototype.sampleListOption = function (optionName) {
|
/**
|
||||||
let parts = this.sampleParts()
|
* Handles option sampling
|
||||||
let option = this.config.options[optionName]
|
*/
|
||||||
let anchors = {}
|
Pattern.prototype.sampleOption = function (optionName) {
|
||||||
let run = 1
|
this.stores[0].log.debug(`Sampling option \`${optionName}\``)
|
||||||
let runs = option.list.length
|
this.runHooks('preSample')
|
||||||
for (let val of option.list) {
|
this.__applySettings(this.__optionSets(optionName))
|
||||||
this.settings.options[optionName] = val
|
this.init()
|
||||||
this.sampleRun(parts, anchors, run, runs)
|
this.runHooks('postSample')
|
||||||
run++
|
|
||||||
}
|
|
||||||
this.parts = parts
|
|
||||||
|
|
||||||
return this
|
return this.draft()
|
||||||
|
}
|
||||||
|
|
||||||
|
//Pattern.prototype.sampleListOption = function (optionName) {
|
||||||
|
// let parts = this.sampleParts()
|
||||||
|
// let option = this.config.options[optionName]
|
||||||
|
// let anchors = {}
|
||||||
|
// let run = 1
|
||||||
|
// let runs = option.list.length
|
||||||
|
// for (let val of option.list) {
|
||||||
|
// this.settings.options[optionName] = val
|
||||||
|
// this.sampleRun(parts, anchors, run, runs)
|
||||||
|
// run++
|
||||||
|
// }
|
||||||
|
// this.parts = parts
|
||||||
|
//
|
||||||
|
// return this
|
||||||
|
//}
|
||||||
|
|
||||||
|
Pattern.prototype.__measurementSets = function (measurementName) {
|
||||||
|
let val = this.settings[0].measurements[measurementName]
|
||||||
|
if (val === undefined)
|
||||||
|
this.stores.log.error(
|
||||||
|
`Cannot sample measurement \`${measurementName}\` because it's \`undefined\``
|
||||||
|
)
|
||||||
|
let step = val / 50
|
||||||
|
val = val * 0.9
|
||||||
|
const sets = []
|
||||||
|
const base = this.__setBase()
|
||||||
|
for (let run = 1; run < 11; run++) {
|
||||||
|
const settings = {
|
||||||
|
...base,
|
||||||
|
measurements: {
|
||||||
|
...base.measurements,
|
||||||
|
},
|
||||||
|
idPrefix: `sample-${run}`,
|
||||||
|
partClasses: `sample-${run}`,
|
||||||
|
}
|
||||||
|
settings.measurements[measurementName] = val
|
||||||
|
sets.push(settings)
|
||||||
|
val += step
|
||||||
|
}
|
||||||
|
|
||||||
|
return sets
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles measurement sampling
|
* Handles measurement sampling
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.sampleMeasurement = function (measurementName) {
|
Pattern.prototype.sampleMeasurement = function (measurementName) {
|
||||||
this.is = 'sample'
|
|
||||||
this.store.log.debug(`Sampling measurement \`${measurementName}\``)
|
this.store.log.debug(`Sampling measurement \`${measurementName}\``)
|
||||||
this.runHooks('preSample')
|
this.runHooks('preSample')
|
||||||
let anchors = {}
|
this.__applySettings(this.__measurementSets(measurementName))
|
||||||
let parts = this.sampleParts()
|
this.init()
|
||||||
let val = this.settings.measurements[measurementName]
|
|
||||||
if (val === undefined)
|
|
||||||
this.store.log.error(
|
|
||||||
`Cannot sample measurement \`${measurementName}\` because it's \`undefined\``
|
|
||||||
)
|
|
||||||
let step = val / 50
|
|
||||||
val = val * 0.9
|
|
||||||
for (let run = 1; run < 11; run++) {
|
|
||||||
this.settings.measurements[measurementName] = val
|
|
||||||
this.sampleRun(parts, anchors, run, 10)
|
|
||||||
val += step
|
|
||||||
}
|
|
||||||
this.parts = parts
|
|
||||||
this.runHooks('postSample')
|
this.runHooks('postSample')
|
||||||
|
|
||||||
return this
|
return this.draft()
|
||||||
|
}
|
||||||
|
|
||||||
|
Pattern.prototype.__modelSets = function (models, focus = false) {
|
||||||
|
const sets = []
|
||||||
|
const base = this.__setBase()
|
||||||
|
let run = 1
|
||||||
|
// If there's a focus, do it first so it's at the bottom of the SVG
|
||||||
|
if (focus) {
|
||||||
|
sets.push({
|
||||||
|
...base,
|
||||||
|
measurements: models[focus],
|
||||||
|
idPrefix: `sample-${run}`,
|
||||||
|
partClasses: `sample-${run} sample-focus`,
|
||||||
|
})
|
||||||
|
run++
|
||||||
|
delete models[focus]
|
||||||
|
}
|
||||||
|
for (const measurements of Object.values(models)) {
|
||||||
|
sets.push({
|
||||||
|
...base,
|
||||||
|
measurements,
|
||||||
|
idPrefix: `sample-${run}`,
|
||||||
|
partClasses: `sample-${run}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return sets
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles models sampling
|
* Handles models sampling
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.sampleModels = function (models, focus = false) {
|
Pattern.prototype.sampleModels = function (models, focus = false) {
|
||||||
this.is = 'sample'
|
this.store.log.debug(`Sampling models \`${Object.keys(models).join(', ')}\``)
|
||||||
this.store.log.debug(`Sampling models`)
|
|
||||||
this.runHooks('preSample')
|
this.runHooks('preSample')
|
||||||
let anchors = {}
|
this.__applySettings(this.__modelSets(models, focus))
|
||||||
let parts = this.sampleParts()
|
this.init()
|
||||||
// If there's a focus, do it first so it's at the bottom of the SVG
|
|
||||||
if (focus) {
|
|
||||||
this.settings.measurements = models[focus]
|
|
||||||
this.sampleRun(parts, anchors, -1, -1, 'sample-focus')
|
|
||||||
delete models[focus]
|
|
||||||
}
|
|
||||||
let run = -1
|
|
||||||
let runs = Object.keys(models).length
|
|
||||||
for (let l in models) {
|
|
||||||
run++
|
|
||||||
this.settings.measurements = models[l]
|
|
||||||
this.sampleRun(parts, anchors, run, runs)
|
|
||||||
}
|
|
||||||
this.parts = parts
|
|
||||||
this.runHooks('postSample')
|
this.runHooks('postSample')
|
||||||
|
|
||||||
return this
|
return this.draft()
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern.prototype.render = function () {
|
Pattern.prototype.render = function () {
|
||||||
|
@ -478,7 +574,7 @@ Pattern.prototype.__loadPlugin = function (plugin, data) {
|
||||||
if (plugin.hooks) this.__loadPluginHooks(plugin, data)
|
if (plugin.hooks) this.__loadPluginHooks(plugin, data)
|
||||||
if (plugin.macros) this.__loadPluginMacros(plugin)
|
if (plugin.macros) this.__loadPluginMacros(plugin)
|
||||||
if (plugin.store) this.__loadPluginStoreMethods(plugin)
|
if (plugin.store) this.__loadPluginStoreMethods(plugin)
|
||||||
this.store.log.info(`Loaded plugin \`${plugin.name}:${plugin.version}\``)
|
this.stores[0].log.info(`Loaded plugin \`${plugin.name}:${plugin.version}\``)
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -486,7 +582,7 @@ Pattern.prototype.__loadPlugin = function (plugin, data) {
|
||||||
Pattern.prototype.use = function (plugin, data) {
|
Pattern.prototype.use = function (plugin, data) {
|
||||||
if (this.plugins?.[plugin.name]?.condition && !plugin.condition) {
|
if (this.plugins?.[plugin.name]?.condition && !plugin.condition) {
|
||||||
// Plugin was first loaded conditionally, and is now loaded explicitly
|
// Plugin was first loaded conditionally, and is now loaded explicitly
|
||||||
this.store.log.info(
|
this.stores[0].log.info(
|
||||||
`Plugin \`${plugin.plugin.name} was loaded conditionally earlier, but is now loaded explicitly.`
|
`Plugin \`${plugin.plugin.name} was loaded conditionally earlier, but is now loaded explicitly.`
|
||||||
)
|
)
|
||||||
return this.__loadPlugin(plugin, data)
|
return this.__loadPlugin(plugin, data)
|
||||||
|
@ -497,7 +593,7 @@ Pattern.prototype.use = function (plugin, data) {
|
||||||
? this.__useIf(plugin, data) // Conditional plugin
|
? this.__useIf(plugin, data) // Conditional plugin
|
||||||
: this.__loadPlugin(plugin, data) // Regular plugin
|
: this.__loadPlugin(plugin, data) // Regular plugin
|
||||||
|
|
||||||
this.store.log.info(
|
this.stores[0].log.info(
|
||||||
`Plugin \`${
|
`Plugin \`${
|
||||||
plugin.plugin ? plugin.plugin.name : plugin.name
|
plugin.plugin ? plugin.plugin.name : plugin.name
|
||||||
}\` was requested, but it's already loaded. Skipping.`
|
}\` was requested, but it's already loaded. Skipping.`
|
||||||
|
@ -506,14 +602,18 @@ Pattern.prototype.use = function (plugin, data) {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern.prototype.__useIf = function (plugin, settings) {
|
Pattern.prototype.__useIf = function (plugin) {
|
||||||
if (plugin.condition(settings)) {
|
let load = 0
|
||||||
this.store.log.info(
|
for (const set of this.settings) {
|
||||||
|
if (plugin.condition(set)) load++
|
||||||
|
}
|
||||||
|
if (load > 0) {
|
||||||
|
this.stores[0].log.info(
|
||||||
`Condition met: Loaded plugin \`${plugin.plugin.name}:${plugin.plugin.version}\``
|
`Condition met: Loaded plugin \`${plugin.plugin.name}:${plugin.plugin.version}\``
|
||||||
)
|
)
|
||||||
this.__loadPlugin(plugin.plugin, plugin.data)
|
this.__loadPlugin(plugin.plugin, plugin.data)
|
||||||
} else {
|
} else {
|
||||||
this.store.log.info(
|
this.stores[0].log.info(
|
||||||
`Condition not met: Skipped loading plugin \`${plugin.plugin.name}:${plugin.plugin.version}\``
|
`Condition not met: Skipped loading plugin \`${plugin.plugin.name}:${plugin.plugin.version}\``
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -542,8 +642,9 @@ Pattern.prototype.__loadPluginMacros = function (plugin) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern.prototype.__loadPluginStoreMethods = function (plugin) {
|
Pattern.prototype.__loadPluginStoreMethods = function (plugin) {
|
||||||
if (Array.isArray(plugin.store)) this.store = this.store.extend(...plugin.store)
|
if (Array.isArray(plugin.store)) {
|
||||||
else this.store.log.warning(`Plugin store methods should be an Array`)
|
for (const store of this.stores) store.extend(...plugin.store)
|
||||||
|
} else this.stores[0].log.warning(`Plugin store methods should be an Array`)
|
||||||
}
|
}
|
||||||
|
|
||||||
Pattern.prototype.macro = function (key, method) {
|
Pattern.prototype.macro = function (key, method) {
|
||||||
|
@ -552,18 +653,23 @@ Pattern.prototype.macro = function (key, method) {
|
||||||
|
|
||||||
/** Packs stacks in a 2D space and sets pattern size */
|
/** Packs stacks in a 2D space and sets pattern size */
|
||||||
Pattern.prototype.pack = function () {
|
Pattern.prototype.pack = function () {
|
||||||
if (this.store.logs.error.length > 0) {
|
for (const set in this.settings) {
|
||||||
this.store.log.warning(`One or more errors occured. Not packing pattern parts`)
|
if (this.stores[set].logs.error.length > 0) {
|
||||||
return this
|
this.stores[set].log.warning(`One or more errors occured. Not packing pattern parts`)
|
||||||
|
return this
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// First, create all stacks
|
// First, create all stacks
|
||||||
this.stacks = {}
|
this.stacks = {}
|
||||||
for (const [name, part] of Object.entries(this.parts)) {
|
for (const set in this.settings) {
|
||||||
const stackName =
|
for (const [name, part] of Object.entries(this.parts[set])) {
|
||||||
typeof part.stack === 'function' ? part.stack(this.settings, name) : part.stack
|
const stackName =
|
||||||
if (typeof this.stacks[stackName] === 'undefined')
|
this.settings[set].stackPrefix +
|
||||||
this.stacks[stackName] = this.__createStackWithContext(stackName)
|
(typeof part.stack === 'function' ? part.stack(this.settings, name) : part.stack)
|
||||||
this.stacks[stackName].addPart(part)
|
if (typeof this.stacks[stackName] === 'undefined')
|
||||||
|
this.stacks[stackName] = this.__createStackWithContext(stackName, set)
|
||||||
|
this.stacks[stackName].addPart(part)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let bins = []
|
let bins = []
|
||||||
|
@ -572,7 +678,7 @@ Pattern.prototype.pack = function () {
|
||||||
stack.attributes.remove('transform')
|
stack.attributes.remove('transform')
|
||||||
if (!this.isStackHidden(key)) {
|
if (!this.isStackHidden(key)) {
|
||||||
stack.home()
|
stack.home()
|
||||||
if (this.settings.layout === true)
|
if (this.settings[0].layout === true)
|
||||||
bins.push({ id: key, width: stack.width, height: stack.height })
|
bins.push({ id: key, width: stack.width, height: stack.height })
|
||||||
else {
|
else {
|
||||||
if (this.width < stack.width) this.width = stack.width
|
if (this.width < stack.width) this.width = stack.width
|
||||||
|
@ -580,7 +686,7 @@ Pattern.prototype.pack = function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.settings.layout === true) {
|
if (this.settings[0].layout === true) {
|
||||||
let size = pack(bins, { inPlace: true })
|
let size = pack(bins, { inPlace: true })
|
||||||
for (let bin of bins) {
|
for (let bin of bins) {
|
||||||
this.autoLayout.stacks[bin.id] = { move: {} }
|
this.autoLayout.stacks[bin.id] = { move: {} }
|
||||||
|
@ -595,10 +701,10 @@ Pattern.prototype.pack = function () {
|
||||||
}
|
}
|
||||||
this.width = size.width
|
this.width = size.width
|
||||||
this.height = size.height
|
this.height = size.height
|
||||||
} else if (typeof this.settings.layout === 'object') {
|
} else if (typeof this.settings[0].layout === 'object') {
|
||||||
this.width = this.settings.layout.width
|
this.width = this.settings[0].layout.width
|
||||||
this.height = this.settings.layout.height
|
this.height = this.settings[0].layout.height
|
||||||
for (let stackId of Object.keys(this.settings.layout.stacks)) {
|
for (let stackId of Object.keys(this.settings[0].layout.stacks)) {
|
||||||
// Some parts are added by late-stage plugins
|
// Some parts are added by late-stage plugins
|
||||||
if (this.stacks[stackId]) {
|
if (this.stacks[stackId]) {
|
||||||
let transforms = this.settings.layout.stacks[stackId]
|
let transforms = this.settings.layout.stacks[stackId]
|
||||||
|
@ -657,7 +763,7 @@ Pattern.prototype.resolveDependency = function (seen, part, graph = this.depende
|
||||||
Pattern.prototype.__addDependency = function (name, part, dep) {
|
Pattern.prototype.__addDependency = function (name, part, dep) {
|
||||||
this.__dependencies[name] = mergeDependencies(dep.name, this.__dependencies[name])
|
this.__dependencies[name] = mergeDependencies(dep.name, this.__dependencies[name])
|
||||||
if (typeof this.__parts[dep.name] === 'undefined') {
|
if (typeof this.__parts[dep.name] === 'undefined') {
|
||||||
this.config = addPartConfig(this.__parts[dep.name], this.config, this.store)
|
this.config = addPartConfig(this.__parts[dep.name], this.config, this.stores[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
return this
|
return this
|
||||||
|
@ -673,12 +779,17 @@ Pattern.prototype.__filterOptionalMeasurements = function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Pre-Resolves part dependencies that are passed in 2022 style */
|
/** Pre-Resolves part dependencies that are passed in 2022 style */
|
||||||
Pattern.prototype.__resolveParts = function (count = 0) {
|
Pattern.prototype.__resolveParts = function (count = 0, distance = 0) {
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
for (const part of this.config.parts) {
|
for (const part of this.config.parts) {
|
||||||
|
part.distance = distance
|
||||||
this.__parts[part.name] = part
|
this.__parts[part.name] = part
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
distance++
|
||||||
|
for (const part of this.config.parts) {
|
||||||
|
if (typeof part.distance === 'undefined') part.distance = distance
|
||||||
|
}
|
||||||
for (const [name, part] of Object.entries(this.__parts)) {
|
for (const [name, part] of Object.entries(this.__parts)) {
|
||||||
// Hide when hideAll is set
|
// Hide when hideAll is set
|
||||||
if (part.hideAll) part.hide = true
|
if (part.hideAll) part.hide = true
|
||||||
|
@ -687,6 +798,7 @@ Pattern.prototype.__resolveParts = function (count = 0) {
|
||||||
if (part.hideDependencies || part.hideAll) {
|
if (part.hideDependencies || part.hideAll) {
|
||||||
part.from.hide = true
|
part.from.hide = true
|
||||||
part.from.hideAll = true
|
part.from.hideAll = true
|
||||||
|
part.from.distance = distance
|
||||||
}
|
}
|
||||||
this.__parts[part.from.name] = part.from
|
this.__parts[part.from.name] = part.from
|
||||||
this.__inject[name] = part.from.name
|
this.__inject[name] = part.from.name
|
||||||
|
@ -695,11 +807,13 @@ Pattern.prototype.__resolveParts = function (count = 0) {
|
||||||
if (part.after) {
|
if (part.after) {
|
||||||
if (Array.isArray(part.after)) {
|
if (Array.isArray(part.after)) {
|
||||||
for (const dep of part.after) {
|
for (const dep of part.after) {
|
||||||
|
dep.distance = distance
|
||||||
this.__parts[dep.name] = dep
|
this.__parts[dep.name] = dep
|
||||||
this.__addDependency(name, part, dep)
|
this.__addDependency(name, part, dep)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (part.hideDependencies) part.after.hide = true
|
if (part.hideDependencies) part.after.hide = true
|
||||||
|
part.after.distance = distance
|
||||||
this.__parts[part.after.name] = part.after
|
this.__parts[part.after.name] = part.after
|
||||||
this.__addDependency(name, part, part.after)
|
this.__addDependency(name, part, part.after)
|
||||||
}
|
}
|
||||||
|
@ -708,10 +822,10 @@ Pattern.prototype.__resolveParts = function (count = 0) {
|
||||||
// Did we discover any new dependencies?
|
// Did we discover any new dependencies?
|
||||||
const len = Object.keys(this.__parts).length
|
const len = Object.keys(this.__parts).length
|
||||||
// If so, resolve recursively
|
// If so, resolve recursively
|
||||||
if (len > count) return this.__resolveParts(len)
|
if (len > count) return this.__resolveParts(len, distance)
|
||||||
|
|
||||||
for (const part of Object.values(this.__parts)) {
|
for (const part of Object.values(this.__parts)) {
|
||||||
this.config = addPartConfig(part, this.config, this.store)
|
this.config = addPartConfig(part, this.config, this.stores[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
return this
|
return this
|
||||||
|
@ -730,7 +844,7 @@ Pattern.prototype.__resolveDependencies = function (graph = false) {
|
||||||
if (this.__dependencies[i].indexOf(dependency) === -1)
|
if (this.__dependencies[i].indexOf(dependency) === -1)
|
||||||
this.__dependencies[i].push(dependency)
|
this.__dependencies[i].push(dependency)
|
||||||
} else {
|
} else {
|
||||||
this.store.log.error('Part dependencies should be a string or an array of strings')
|
this.stores[0].log.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')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -751,17 +865,20 @@ Pattern.prototype.__resolveDependencies = function (graph = false) {
|
||||||
* This depends on the 'only' setting and the
|
* This depends on the 'only' setting and the
|
||||||
* configured dependencies.
|
* configured dependencies.
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.needs = function (partName) {
|
Pattern.prototype.needs = function (partName, set = 0) {
|
||||||
// If only is unset, all parts are needed
|
// If only is unset, all parts are needed
|
||||||
if (
|
if (
|
||||||
typeof this.settings.only === 'undefined' ||
|
typeof this.settings[set].only === 'undefined' ||
|
||||||
this.settings.only === false ||
|
this.settings[set].only === false ||
|
||||||
(Array.isArray(this.settings.only) && this.settings.only.length === 0)
|
(Array.isArray(this.settings[set].only) && this.settings[set].only.length === 0)
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
// Make only to always be an array
|
// Make only to always be an array
|
||||||
const only = typeof this.settings.only === 'string' ? [this.settings.only] : this.settings.only
|
const only =
|
||||||
|
typeof this.settings[set].only === 'string'
|
||||||
|
? [this.settings[set].only]
|
||||||
|
: this.settings[set].only
|
||||||
|
|
||||||
// Walk the only parts, checking each one for a match in its dependencies
|
// Walk the only parts, checking each one for a match in its dependencies
|
||||||
for (const part of only) {
|
for (const part of only) {
|
||||||
|
@ -779,12 +896,12 @@ Pattern.prototype.needs = function (partName) {
|
||||||
/** Determines whether a part is wanted by the user
|
/** Determines whether a part is wanted by the user
|
||||||
* This depends on the 'only' setting
|
* This depends on the 'only' setting
|
||||||
*/
|
*/
|
||||||
Pattern.prototype.wants = function (partName) {
|
Pattern.prototype.wants = function (partName, set = 0) {
|
||||||
// Hidden parts are not wanted
|
// Hidden parts are not wanted
|
||||||
if (this.isHidden(partName)) return false
|
if (this.isHidden(partName)) return false
|
||||||
else if (typeof this.settings.only === 'string') return this.settings.only === partName
|
else if (typeof this.settings[set].only === 'string') return this.settings[set].only === partName
|
||||||
else if (Array.isArray(this.settings.only)) {
|
else if (Array.isArray(this.settings[set].only)) {
|
||||||
for (const part of this.settings.only) {
|
for (const part of this.settings[set].only) {
|
||||||
if (part === partName) return true
|
if (part === partName) return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -838,12 +955,12 @@ Pattern.prototype.getRenderProps = function () {
|
||||||
props.height = this.height
|
props.height = this.height
|
||||||
props.autoLayout = this.autoLayout
|
props.autoLayout = this.autoLayout
|
||||||
props.settings = this.settings
|
props.settings = this.settings
|
||||||
props.logs = {
|
props.logs = this.stores.map((store) => ({
|
||||||
debug: this.store.logs.debug,
|
debug: store.logs.debug,
|
||||||
info: this.store.logs.info,
|
info: store.logs.info,
|
||||||
error: this.store.logs.error,
|
error: store.logs.error,
|
||||||
warning: this.store.logs.warning,
|
warning: store.logs.warning,
|
||||||
}
|
}))
|
||||||
props.parts = {}
|
props.parts = {}
|
||||||
for (let p in this.parts) {
|
for (let p in this.parts) {
|
||||||
if (this.parts[p].render) {
|
if (this.parts[p].render) {
|
||||||
|
@ -856,6 +973,7 @@ Pattern.prototype.getRenderProps = function () {
|
||||||
width: this.parts[p].width,
|
width: this.parts[p].width,
|
||||||
bottomRight: this.parts[p].bottomRight,
|
bottomRight: this.parts[p].bottomRight,
|
||||||
topLeft: this.parts[p].topLeft,
|
topLeft: this.parts[p].topLeft,
|
||||||
|
store: this.stores[this.parts[p].set],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -870,8 +988,14 @@ Pattern.prototype.getRenderProps = function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merges settings object with default settings
|
// Merges settings object with default settings
|
||||||
Pattern.prototype.__applySettings = function (settings) {
|
Pattern.prototype.__applySettings = function (sets) {
|
||||||
this.settings = { ...loadPatternDefaults(), ...settings }
|
if (!Array.isArray(sets)) throw 'Sets should be an array of settings objects'
|
||||||
|
if (sets.length === 0) sets.push({}) // Required to load default settings
|
||||||
|
this.settings = []
|
||||||
|
for (const set in sets) {
|
||||||
|
this.settings.push({ ...loadPatternDefaults(), ...sets[set] })
|
||||||
|
if (set > 0) this.stores.push(new Store())
|
||||||
|
}
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,8 +76,11 @@ Stack.prototype.home = function () {
|
||||||
if (this.bottomRight.y === -Infinity) this.bottomRight.y = 0
|
if (this.bottomRight.y === -Infinity) this.bottomRight.y = 0
|
||||||
|
|
||||||
// Add margin
|
// Add margin
|
||||||
let margin = this.context.settings.margin
|
let margin = 0
|
||||||
if (this.context.settings.paperless && margin < 10) margin = 10
|
for (const set in this.context.settings) {
|
||||||
|
if (this.context.settings[set].margin > margin) margin = this.context.settings[set].margin
|
||||||
|
if (this.context.settings[set].paperless && margin < 10) margin = 10
|
||||||
|
}
|
||||||
this.topLeft.x -= margin
|
this.topLeft.x -= margin
|
||||||
this.topLeft.y -= margin
|
this.topLeft.y -= margin
|
||||||
this.bottomRight.x += margin
|
this.bottomRight.x += margin
|
||||||
|
@ -90,7 +93,9 @@ Stack.prototype.home = function () {
|
||||||
this.height = this.bottomRight.y - this.topLeft.y
|
this.height = this.bottomRight.y - this.topLeft.y
|
||||||
|
|
||||||
// Add transform
|
// Add transform
|
||||||
this.anchor = this.getAnchor()
|
//this.anchor = this.getAnchor()
|
||||||
|
// FIXME: Can we be certain this is always (0,0) /
|
||||||
|
this.anchor = new Point(0, 0)
|
||||||
|
|
||||||
if (this.topLeft.x === this.anchor.x && this.topLeft.y === this.anchor.y) return this
|
if (this.topLeft.x === this.anchor.x && this.topLeft.y === this.anchor.y) return this
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -15,7 +15,7 @@ export function isCoord(value) {
|
||||||
|
|
||||||
/** Returns internal hook name for a macro */
|
/** Returns internal hook name for a macro */
|
||||||
export function macroName(name) {
|
export function macroName(name) {
|
||||||
return `_macro_${name}`
|
return `__macro_${name}`
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Find intersection of two (endless) lines */
|
/** Find intersection of two (endless) lines */
|
||||||
|
@ -441,8 +441,14 @@ export function addNonEnumProp(obj, name, value) {
|
||||||
const addPartOptions = (part, config, store) => {
|
const addPartOptions = (part, config, store) => {
|
||||||
if (part.options) {
|
if (part.options) {
|
||||||
for (const optionName in part.options) {
|
for (const optionName in part.options) {
|
||||||
store.log.debug(`Config resolver: Option __${optionName}__ in ${part.name}`)
|
if (!config.optionDistance[optionName]) {
|
||||||
config.options[optionName] = part.options[optionName]
|
config.optionDistance[optionName] = part.distance
|
||||||
|
config.options[optionName] = part.options[optionName]
|
||||||
|
store.log.debug(`🔵 __${optionName}__ option loaded from \`${part.name}\``)
|
||||||
|
} else if (config.optionDistance[optionName] > part.distance) {
|
||||||
|
config.options[optionName] = part.options[optionName]
|
||||||
|
store.log.debug(`🟣 __${optionName}__ option overwritten by \`${part.name}\``)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (part.from) addPartOptions(part.from, config, store)
|
if (part.from) addPartOptions(part.from, config, store)
|
||||||
|
@ -460,8 +466,10 @@ const addPartMeasurements = (part, config, store, list = false) => {
|
||||||
if (!list) list = config.measurements ? [...config.measurements] : []
|
if (!list) list = config.measurements ? [...config.measurements] : []
|
||||||
if (part.measurements) {
|
if (part.measurements) {
|
||||||
for (const m of part.measurements) {
|
for (const m of part.measurements) {
|
||||||
list.push(m)
|
if (list.indexOf(m) === -1) {
|
||||||
store.log.debug(`Config resolver: Measurement __${m}__ is required in ${part.name}`)
|
list.push(m)
|
||||||
|
store.log.debug(`🟠 __${m}__ measurement is required in \`${part.name}\``)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (part.from) addPartMeasurements(part.from, config, store, list)
|
if (part.from) addPartMeasurements(part.from, config, store, list)
|
||||||
|
@ -484,8 +492,10 @@ const addPartOptionalMeasurements = (part, config, store, list = false) => {
|
||||||
for (const m of part.optionalMeasurements) {
|
for (const m of part.optionalMeasurements) {
|
||||||
// Don't add it's a required measurement for another part
|
// Don't add it's a required measurement for another part
|
||||||
if (config.measurements.indexOf(m) === -1) {
|
if (config.measurements.indexOf(m) === -1) {
|
||||||
store.log.debug(`Config resolver: Measurement __${m}__ is optional in ${part.name}`)
|
if (list.indexOf(m) === -1) {
|
||||||
list.push(m)
|
list.push(m)
|
||||||
|
store.log.debug(`🟡 __${m}__ measurement is optional in \`${part.name}\``)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -514,7 +524,7 @@ export const addPartPlugins = (part, config, store) => {
|
||||||
const pluginObj = { ...plugin[0], data: plugin[1] }
|
const pluginObj = { ...plugin[0], data: plugin[1] }
|
||||||
plugin = pluginObj
|
plugin = pluginObj
|
||||||
}
|
}
|
||||||
store.log.debug(`Config resolver: Plugin __${plugin.name}__ in ${part.name}`)
|
store.log.debug(`🔌 __${plugin.name}__ plugin in \`${part.name}\``)
|
||||||
// Do not overwrite an existing plugin with a conditional plugin unless it is also conditional
|
// Do not overwrite an existing plugin with a conditional plugin unless it is also conditional
|
||||||
if (plugin.plugin && plugin.condition) {
|
if (plugin.plugin && plugin.condition) {
|
||||||
if (plugins[plugin.plugin.name]?.condition) {
|
if (plugins[plugin.plugin.name]?.condition) {
|
||||||
|
@ -538,7 +548,9 @@ export const addPartPlugins = (part, config, store) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const addPartConfig = (part, config, store) => {
|
export const addPartConfig = (part, config, store) => {
|
||||||
|
if (part.resolved) return config
|
||||||
// Add parts, using set to keep them unique in the array
|
// Add parts, using set to keep them unique in the array
|
||||||
|
part.resolved = true
|
||||||
config.parts = [...new Set(config.parts).add(part)]
|
config.parts = [...new Set(config.parts).add(part)]
|
||||||
config = addPartOptions(part, config, store)
|
config = addPartOptions(part, config, store)
|
||||||
config = addPartMeasurements(part, config, store)
|
config = addPartMeasurements(part, config, store)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue