Merge branch 'v3-stacks' into develop
This commit is contained in:
commit
8cc4c588ff
13 changed files with 442 additions and 137 deletions
|
@ -32,7 +32,7 @@ import {
|
||||||
rad2deg,
|
rad2deg,
|
||||||
pctBasedOn,
|
pctBasedOn,
|
||||||
Bezier,
|
Bezier,
|
||||||
generatePartTransform,
|
generateStackTransform,
|
||||||
macroName,
|
macroName,
|
||||||
} from './utils.mjs'
|
} from './utils.mjs'
|
||||||
import { version } from '../data.mjs'
|
import { version } from '../data.mjs'
|
||||||
|
@ -71,7 +71,7 @@ export {
|
||||||
deg2rad,
|
deg2rad,
|
||||||
rad2deg,
|
rad2deg,
|
||||||
pctBasedOn,
|
pctBasedOn,
|
||||||
generatePartTransform,
|
generateStackTransform,
|
||||||
macroName,
|
macroName,
|
||||||
isCoord,
|
isCoord,
|
||||||
version,
|
version,
|
||||||
|
|
|
@ -26,6 +26,7 @@ export function Part() {
|
||||||
this.points = {}
|
this.points = {}
|
||||||
this.paths = {}
|
this.paths = {}
|
||||||
this.snippets = {}
|
this.snippets = {}
|
||||||
|
this.name = null
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -110,31 +111,15 @@ Part.prototype.boundary = function () {
|
||||||
if (topLeft.y === Infinity) topLeft.y = 0
|
if (topLeft.y === Infinity) topLeft.y = 0
|
||||||
if (bottomRight.x === -Infinity) bottomRight.x = 0
|
if (bottomRight.x === -Infinity) bottomRight.x = 0
|
||||||
if (bottomRight.y === -Infinity) bottomRight.y = 0
|
if (bottomRight.y === -Infinity) bottomRight.y = 0
|
||||||
// Add margin
|
|
||||||
let margin = this.context.settings.margin
|
this.topLeft = topLeft
|
||||||
if (this.context.settings.paperless && margin < 10) margin = 10
|
this.bottomRight = bottomRight
|
||||||
this.topLeft = new Point(topLeft.x - margin, topLeft.y - margin)
|
|
||||||
this.bottomRight = new Point(bottomRight.x + margin, bottomRight.y + margin)
|
|
||||||
this.width = this.bottomRight.x - this.topLeft.x
|
this.width = this.bottomRight.x - this.topLeft.x
|
||||||
this.height = this.bottomRight.y - this.topLeft.y
|
this.height = this.bottomRight.y - this.topLeft.y
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stacks part so that its top left corner is in (0,0) */
|
|
||||||
Part.prototype.stack = function () {
|
|
||||||
if (this.topLeft !== false) return this
|
|
||||||
else this.boundary()
|
|
||||||
if (this.topLeft.x == 0 && this.topLeft.y == 0) return this
|
|
||||||
else {
|
|
||||||
this.attr('transform', `translate(${this.topLeft.x * -1}, ${this.topLeft.y * -1})`)
|
|
||||||
this.layout.move.x = this.topLeft.x * -1
|
|
||||||
this.layout.move.y = this.topLeft.y * -1
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Adds an attribute. This is here to make this call chainable in assignment */
|
/** Adds an attribute. This is here to make this call chainable in assignment */
|
||||||
Part.prototype.attr = function (name, value, overwrite = false) {
|
Part.prototype.attr = function (name, value, overwrite = false) {
|
||||||
if (overwrite) this.attributes.set(name, value)
|
if (overwrite) this.attributes.set(name, value)
|
||||||
|
@ -351,15 +336,6 @@ Part.prototype.shorthand = function () {
|
||||||
return shorthand
|
return shorthand
|
||||||
}
|
}
|
||||||
|
|
||||||
Part.prototype.generateTransform = function (transforms) {
|
|
||||||
const { move, rotate, flipX, flipY } = transforms
|
|
||||||
const generated = utils.generatePartTransform(move.x, move.y, rotate, flipX, flipY, this)
|
|
||||||
|
|
||||||
for (var t in generated) {
|
|
||||||
this.attr(t, generated[t], true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
mergeDependencies,
|
mergeDependencies,
|
||||||
} from './utils.mjs'
|
} from './utils.mjs'
|
||||||
import { Part } from './part.mjs'
|
import { Part } from './part.mjs'
|
||||||
|
import { Stack } from './stack.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'
|
||||||
|
@ -23,7 +24,7 @@ export function Pattern(config) {
|
||||||
addNonEnumProp(this, 'plugins', {})
|
addNonEnumProp(this, 'plugins', {})
|
||||||
addNonEnumProp(this, 'width', 0)
|
addNonEnumProp(this, 'width', 0)
|
||||||
addNonEnumProp(this, 'height', 0)
|
addNonEnumProp(this, 'height', 0)
|
||||||
addNonEnumProp(this, 'autoLayout', { parts: {} })
|
addNonEnumProp(this, 'autoLayout', { stacks: {} })
|
||||||
addNonEnumProp(this, 'is', '')
|
addNonEnumProp(this, 'is', '')
|
||||||
addNonEnumProp(this, 'hooks', new Hooks())
|
addNonEnumProp(this, 'hooks', new Hooks())
|
||||||
addNonEnumProp(this, 'Point', Point)
|
addNonEnumProp(this, 'Point', Point)
|
||||||
|
@ -41,6 +42,7 @@ export function Pattern(config) {
|
||||||
// Enumerable properties
|
// Enumerable properties
|
||||||
this.config = config // Design config
|
this.config = config // Design config
|
||||||
this.parts = {} // Drafted parts container
|
this.parts = {} // Drafted parts container
|
||||||
|
this.stacks = {} // Drafted stacks container
|
||||||
this.store = new Store() // Store for sharing data across parts
|
this.store = new Store() // Store for sharing data across parts
|
||||||
|
|
||||||
return this
|
return this
|
||||||
|
@ -86,6 +88,7 @@ Pattern.prototype.__createPartWithContext = function (name) {
|
||||||
// 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.stack = this.__parts[name]?.stack || name
|
||||||
part.context = {
|
part.context = {
|
||||||
parts: this.parts,
|
parts: this.parts,
|
||||||
config: this.config,
|
config: this.config,
|
||||||
|
@ -101,6 +104,19 @@ Pattern.prototype.__createPartWithContext = function (name) {
|
||||||
return part
|
return part
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Pattern.prototype.__createStackWithContext = function (name) {
|
||||||
|
// Context object to add to Stack closure
|
||||||
|
const stack = new Stack()
|
||||||
|
stack.name = name
|
||||||
|
stack.context = {
|
||||||
|
config: this.config,
|
||||||
|
settings: this.settings,
|
||||||
|
store: this.store,
|
||||||
|
}
|
||||||
|
|
||||||
|
return stack
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -536,40 +552,48 @@ Pattern.prototype.macro = function (key, method) {
|
||||||
this.macros[key] = method
|
this.macros[key] = method
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Packs parts 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) {
|
if (this.store.logs.error.length > 0) {
|
||||||
this.store.log.warning(`One or more errors occured. Not packing pattern parts`)
|
this.store.log.warning(`One or more errors occured. Not packing pattern parts`)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
// First, create all stacks
|
||||||
|
this.stacks = {}
|
||||||
|
for (const [name, part] of Object.entries(this.parts)) {
|
||||||
|
const stackName = (typeof part.stack === 'function')
|
||||||
|
? part.stack(this.settings, name)
|
||||||
|
: part.stack
|
||||||
|
if (typeof this.stacks[stackName] === 'undefined')
|
||||||
|
this.stacks[stackName] = this.__createStackWithContext(stackName)
|
||||||
|
this.stacks[stackName].addPart(part)
|
||||||
|
}
|
||||||
|
|
||||||
let bins = []
|
let bins = []
|
||||||
for (let key in this.parts) {
|
for (const [key, stack] of Object.entries(this.stacks)) {
|
||||||
let part = this.parts[key]
|
// Avoid multiple render calls to cause addition of transforms
|
||||||
// Avoid multiple render calls to cause stacking of transforms
|
stack.attributes.remove('transform')
|
||||||
part.attributes.remove('transform')
|
if (!this.isStackHidden(key)) {
|
||||||
if (part.render) {
|
stack.home()
|
||||||
part.stack()
|
if (this.settings.layout === true)
|
||||||
let width = part.bottomRight.x - part.topLeft.x
|
bins.push({ id: key, width: stack.width, height: stack.height })
|
||||||
let height = part.bottomRight.y - part.topLeft.y
|
|
||||||
if (this.settings.layout === true) bins.push({ id: key, width, height })
|
|
||||||
else {
|
else {
|
||||||
if (this.width < width) this.width = width
|
if (this.width < stack.width) this.width = stack.width
|
||||||
if (this.height < height) this.height = height
|
if (this.height < stack.height) this.height = stack.height
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.settings.layout === true) {
|
if (this.settings.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.parts[bin.id] = { move: {} }
|
this.autoLayout.stacks[bin.id] = { move: {} }
|
||||||
let part = this.parts[bin.id]
|
let stack = this.stacks[bin.id]
|
||||||
if (bin.x !== 0 || bin.y !== 0) {
|
if (bin.x !== 0 || bin.y !== 0) {
|
||||||
part.attr('transform', `translate(${bin.x}, ${bin.y})`)
|
stack.attr('transform', `translate(${bin.x}, ${bin.y})`)
|
||||||
}
|
}
|
||||||
|
this.autoLayout.stacks[bin.id].move = {
|
||||||
this.autoLayout.parts[bin.id].move = {
|
x: bin.x + stack.layout.move.x,
|
||||||
x: bin.x + part.layout.move.x,
|
y: bin.y + stack.layout.move.y,
|
||||||
y: bin.y + part.layout.move.y,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.width = size.width
|
this.width = size.width
|
||||||
|
@ -577,11 +601,11 @@ Pattern.prototype.pack = function () {
|
||||||
} else if (typeof this.settings.layout === 'object') {
|
} else if (typeof this.settings.layout === 'object') {
|
||||||
this.width = this.settings.layout.width
|
this.width = this.settings.layout.width
|
||||||
this.height = this.settings.layout.height
|
this.height = this.settings.layout.height
|
||||||
for (let partId of Object.keys(this.settings.layout.parts)) {
|
for (let stackId of Object.keys(this.settings.layout.stacks)) {
|
||||||
// Some parts are added by late-stage plugins
|
// Some parts are added by late-stage plugins
|
||||||
if (this.parts[partId]) {
|
if (this.stacks[stackId]) {
|
||||||
let transforms = this.settings.layout.parts[partId]
|
let transforms = this.settings.layout.stacks[stackId]
|
||||||
this.parts[partId].generateTransform(transforms)
|
this.stacks[stackId].generateTransform(transforms)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -783,6 +807,23 @@ Pattern.prototype.isHidden = function (partName) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Checks whether (all parts in) a stack is hidden in the config */
|
||||||
|
Pattern.prototype.isStackHidden = function (stackName) {
|
||||||
|
if (!this.stacks[stackName]) return true
|
||||||
|
const parts = this.stacks[stackName].getPartNames()
|
||||||
|
if (Array.isArray(this.settings.only)) {
|
||||||
|
for (const partName of parts) {
|
||||||
|
if (this.settings.only.includes(partName)) return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const partName of parts) {
|
||||||
|
if (this.__parts?.[partName]?.hide) return true
|
||||||
|
if (this.__parts?.[partName]?.hideAll) return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns props required to render this pattern through
|
/** Returns props required to render this pattern through
|
||||||
* an external renderer (eg. a React component)
|
* an external renderer (eg. a React component)
|
||||||
*/
|
*/
|
||||||
|
@ -821,6 +862,12 @@ Pattern.prototype.getRenderProps = function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
props.stacks = {}
|
||||||
|
for (let s in this.stacks) {
|
||||||
|
if (!this.isStackHidden(s)) {
|
||||||
|
props.stacks[s] = this.stacks[s]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return props
|
return props
|
||||||
}
|
}
|
||||||
|
|
140
packages/core/src/stack.mjs
Normal file
140
packages/core/src/stack.mjs
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
import { Attributes } from './attributes.mjs'
|
||||||
|
import { Point } from './point.mjs'
|
||||||
|
import * as utils from './utils.mjs'
|
||||||
|
|
||||||
|
export function Stack(name = null) {
|
||||||
|
// Non-enumerable properties
|
||||||
|
utils.addNonEnumProp(this, 'freeId', 0)
|
||||||
|
utils.addNonEnumProp(this, 'layout', { move: { x: 0, y: 0 } })
|
||||||
|
|
||||||
|
// Enumerable properties
|
||||||
|
this.attributes = new Attributes()
|
||||||
|
this.parts = new Set()
|
||||||
|
this.name = name
|
||||||
|
this.topLeft = false
|
||||||
|
this.bottomRight = false
|
||||||
|
this.width = false
|
||||||
|
this.height = false
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adds a part to the stack */
|
||||||
|
Stack.prototype.addPart = function (part) {
|
||||||
|
if (part) this.parts.add(part)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a list of parts in this stack */
|
||||||
|
Stack.prototype.getPartList = function (part) {
|
||||||
|
return [...this.parts]
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a list of names of parts in this stack */
|
||||||
|
Stack.prototype.getPartNames = function (part) {
|
||||||
|
return [...this.parts].map((p) => p.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Homes the stack so that its top left corner is in (0,0) */
|
||||||
|
//Stack.prototype.home = function () {
|
||||||
|
// const parts = this.getPartList()
|
||||||
|
// if (parts.length < 1) return this
|
||||||
|
// for (const part of this.getPartList()) {
|
||||||
|
// part.home()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (parts.length === 1) {
|
||||||
|
// this.topLeft = part.topLeft
|
||||||
|
// this.bottomRigth = part.bottomRight
|
||||||
|
// this.width = part.width
|
||||||
|
// this.height = part.height
|
||||||
|
//
|
||||||
|
// return this
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return this.boundary()
|
||||||
|
//}
|
||||||
|
|
||||||
|
/** Calculates the stack's bounding box and sets it */
|
||||||
|
Stack.prototype.home = function () {
|
||||||
|
if (this.topLeft) return this // Cached
|
||||||
|
this.topLeft = new Point(Infinity, Infinity)
|
||||||
|
this.bottomRight = new Point(-Infinity, -Infinity)
|
||||||
|
for (const part of this.getPartList()) {
|
||||||
|
part.boundary()
|
||||||
|
if (part.topLeft.x < this.topLeft.x) this.topLeft.x = part.topLeft.x
|
||||||
|
if (part.topLeft.y < this.topLeft.y) this.topLeft.y = part.topLeft.y
|
||||||
|
if (part.bottomRight.x > this.bottomRight.x) this.bottomRight.x = part.bottomRight.x
|
||||||
|
if (part.bottomRight.y > this.bottomRight.y) this.bottomRight.y = part.bottomRight.y
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix infinity if it's not overwritten
|
||||||
|
if (this.topLeft.x === Infinity) this.topLeft.x = 0
|
||||||
|
if (this.topLeft.y === Infinity) this.topLeft.y = 0
|
||||||
|
if (this.bottomRight.x === -Infinity) this.bottomRight.x = 0
|
||||||
|
if (this.bottomRight.y === -Infinity) this.bottomRight.y = 0
|
||||||
|
|
||||||
|
// Add margin
|
||||||
|
let margin = this.context.settings.margin
|
||||||
|
if (this.context.settings.paperless && margin < 10) margin = 10
|
||||||
|
this.topLeft.x -= margin
|
||||||
|
this.topLeft.y -= margin
|
||||||
|
this.bottomRight.x += margin
|
||||||
|
this.bottomRight.y += margin
|
||||||
|
|
||||||
|
// Set dimensions
|
||||||
|
this.width = this.bottomRight.x - this.topLeft.x
|
||||||
|
this.height = this.bottomRight.y - this.topLeft.y
|
||||||
|
this.width = this.bottomRight.x - this.topLeft.x
|
||||||
|
this.height = this.bottomRight.y - this.topLeft.y
|
||||||
|
|
||||||
|
// Add transform
|
||||||
|
this.anchor = this.getAnchor()
|
||||||
|
|
||||||
|
if (this.topLeft.x === this.anchor.x && this.topLeft.y === this.anchor.y) return this
|
||||||
|
else {
|
||||||
|
this.attr('transform', `translate(${this.anchor.x - this.topLeft.x}, ${this.anchor.y - this.topLeft.y})`)
|
||||||
|
this.layout.move.x = this.anchor.x - this.topLeft.x
|
||||||
|
this.layout.move.y = this.anchor.y - this.topLeft.y
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Finds the anchor to align parts in this stack */
|
||||||
|
Stack.prototype.getAnchor = function() {
|
||||||
|
let anchorPoint = true
|
||||||
|
let gridAnchorPoint = true
|
||||||
|
const parts = this.getPartList()
|
||||||
|
for (const part of parts) {
|
||||||
|
if (typeof part.points.anchor === 'undefined') anchorPoint = false
|
||||||
|
if (typeof part.points.gridAnchor === 'undefined') gridAnchorPoint = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anchorPoint) return parts[0].points.anchor
|
||||||
|
if (gridAnchorPoint) return parts[0].points.gridAnchor
|
||||||
|
|
||||||
|
return new Point(0,0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Adds an attribute. This is here to make this call chainable in assignment */
|
||||||
|
Stack.prototype.attr = function (name, value, overwrite = false) {
|
||||||
|
if (overwrite) this.attributes.set(name, value)
|
||||||
|
else this.attributes.add(name, value)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generates the transform for a stack */
|
||||||
|
Stack.prototype.generateTransform = function (transforms) {
|
||||||
|
const { move, rotate, flipX, flipY } = transforms
|
||||||
|
const generated = utils.generateStackTransform(move.x, move.y, rotate, flipX, flipY, this)
|
||||||
|
|
||||||
|
for (var t in generated) {
|
||||||
|
this.attr(t, generated[t], true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default Stack
|
|
@ -5,12 +5,6 @@ import get from 'lodash.get'
|
||||||
const avoid = ['set', 'setIfUnset', 'push', 'unset', 'get', 'extend']
|
const avoid = ['set', 'setIfUnset', 'push', 'unset', 'get', 'extend']
|
||||||
|
|
||||||
export function Store(methods = []) {
|
export function Store(methods = []) {
|
||||||
for (const method of methods) {
|
|
||||||
if (avoid.indexOf(method[0]) !== -1) {
|
|
||||||
console.log(`WARNING: You can't squat ${method[0]}in the store`)
|
|
||||||
} else set(this, ...method)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Default logging methods
|
* Default logging methods
|
||||||
* You can override these with a plugin
|
* You can override these with a plugin
|
||||||
|
@ -37,6 +31,12 @@ export function Store(methods = []) {
|
||||||
}
|
}
|
||||||
this.logs = logs
|
this.logs = logs
|
||||||
|
|
||||||
|
for (const method of methods) {
|
||||||
|
if (avoid.indexOf(method[0]) !== -1) {
|
||||||
|
this.logs.warning(`You cannot squat ${method[0]} in the store`)
|
||||||
|
} else set(this, ...method)
|
||||||
|
}
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -361,8 +361,8 @@ export function pctBasedOn(measurement) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Generates the transform attributes needed for a given part */
|
/** Generates the transform attributes needed for a given stack */
|
||||||
export const generatePartTransform = (x, y, rotate, flipX, flipY, part) => {
|
export const generateStackTransform = (x, y, rotate, flipX, flipY, part) => {
|
||||||
const transforms = []
|
const transforms = []
|
||||||
let xTotal = x || 0
|
let xTotal = x || 0
|
||||||
let yTotal = y || 0
|
let yTotal = y || 0
|
||||||
|
|
|
@ -124,7 +124,7 @@ describe('Part', () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should calculate the part boundary with default margin', () => {
|
it('Should calculate the part boundary', () => {
|
||||||
const design = new Design()
|
const design = new Design()
|
||||||
const pattern = new design()
|
const pattern = new design()
|
||||||
const part = pattern.__createPartWithContext()
|
const part = pattern.__createPartWithContext()
|
||||||
|
@ -134,52 +134,15 @@ describe('Part', () => {
|
||||||
part.points.to = new short.Point(19, 76)
|
part.points.to = new short.Point(19, 76)
|
||||||
part.paths.test = new short.Path().move(part.points.from).line(part.points.to)
|
part.paths.test = new short.Path().move(part.points.from).line(part.points.to)
|
||||||
let boundary = part.boundary()
|
let boundary = part.boundary()
|
||||||
expect(boundary.topLeft.x).to.equal(17)
|
expect(boundary.topLeft.x).to.equal(19)
|
||||||
expect(boundary.topLeft.y).to.equal(74)
|
expect(boundary.topLeft.y).to.equal(76)
|
||||||
expect(boundary.bottomRight.x).to.equal(125)
|
expect(boundary.bottomRight.x).to.equal(123)
|
||||||
expect(boundary.bottomRight.y).to.equal(458)
|
expect(boundary.bottomRight.y).to.equal(456)
|
||||||
boundary = part.boundary()
|
boundary = part.boundary()
|
||||||
expect(boundary.width).to.equal(108)
|
expect(boundary.width).to.equal(104)
|
||||||
expect(boundary.height).to.equal(384)
|
expect(boundary.height).to.equal(380)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should calculate the part boundary with custom margin', () => {
|
|
||||||
const design = new Design()
|
|
||||||
const pattern = new design({ margin: 5 })
|
|
||||||
const part = pattern.__createPartWithContext()
|
|
||||||
pattern.init()
|
|
||||||
const short = part.shorthand()
|
|
||||||
part.points.from = new short.Point(123, 456)
|
|
||||||
part.points.to = new short.Point(19, 76)
|
|
||||||
part.paths.test = new short.Path().move(part.points.from).line(part.points.to)
|
|
||||||
let boundary = part.boundary()
|
|
||||||
expect(boundary.topLeft.x).to.equal(14)
|
|
||||||
expect(boundary.topLeft.y).to.equal(71)
|
|
||||||
expect(boundary.bottomRight.x).to.equal(128)
|
|
||||||
expect(boundary.bottomRight.y).to.equal(461)
|
|
||||||
boundary = part.boundary()
|
|
||||||
expect(boundary.width).to.equal(114)
|
|
||||||
expect(boundary.height).to.equal(390)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('Should calculate the part boundary for paperless', () => {
|
|
||||||
const design = new Design()
|
|
||||||
const pattern = new design({ paperless: true })
|
|
||||||
const part = pattern.__createPartWithContext()
|
|
||||||
pattern.init()
|
|
||||||
const short = part.shorthand()
|
|
||||||
part.points.from = new short.Point(123, 456)
|
|
||||||
part.points.to = new short.Point(19, 76)
|
|
||||||
part.paths.test = new short.Path().move(part.points.from).line(part.points.to)
|
|
||||||
let boundary = part.boundary()
|
|
||||||
expect(boundary.topLeft.x).to.equal(9)
|
|
||||||
expect(boundary.topLeft.y).to.equal(66)
|
|
||||||
expect(boundary.bottomRight.x).to.equal(133)
|
|
||||||
expect(boundary.bottomRight.y).to.equal(466)
|
|
||||||
boundary = part.boundary()
|
|
||||||
expect(boundary.width).to.equal(124)
|
|
||||||
expect(boundary.height).to.equal(400)
|
|
||||||
})
|
|
||||||
/*
|
/*
|
||||||
it('Should stack a part', () => {
|
it('Should stack a part', () => {
|
||||||
const part = {
|
const part = {
|
||||||
|
@ -195,7 +158,7 @@ describe('Part', () => {
|
||||||
const design = new Design({ parts: [ part ]})
|
const design = new Design({ parts: [ part ]})
|
||||||
const pattern = new design({ paperless: true })
|
const pattern = new design({ paperless: true })
|
||||||
pattern.draft()
|
pattern.draft()
|
||||||
pattern.parts.test.stack()
|
pattern.parts.test.home()
|
||||||
console.log(pattern.parts.test.attributes)
|
console.log(pattern.parts.test.attributes)
|
||||||
expect(part.attributes.get('transform')).to.equal('translate(-17, -74)')
|
expect(part.attributes.get('transform')).to.equal('translate(-17, -74)')
|
||||||
})
|
})
|
||||||
|
@ -208,9 +171,9 @@ describe('Part', () => {
|
||||||
part.points.from = new short.Point(2, 2)
|
part.points.from = new short.Point(2, 2)
|
||||||
part.points.to = new short.Point(19, 76)
|
part.points.to = new short.Point(19, 76)
|
||||||
part.paths.test = new short.Path().move(part.points.from).line(part.points.to)
|
part.paths.test = new short.Path().move(part.points.from).line(part.points.to)
|
||||||
part.stack()
|
part.home()
|
||||||
expect(part.attributes.get('transform')).to.equal(false)
|
expect(part.attributes.get('transform')).to.equal(false)
|
||||||
part.stack()
|
part.home()
|
||||||
expect(part.attributes.get('transform')).to.equal(false)
|
expect(part.attributes.get('transform')).to.equal(false)
|
||||||
})
|
})
|
||||||
*/
|
*/
|
||||||
|
@ -243,25 +206,6 @@ describe('Part', () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should generate the part transforms', () => {
|
|
||||||
const design = new Design()
|
|
||||||
const pattern = new design({ margin: 5 })
|
|
||||||
const part = pattern.__createPartWithContext()
|
|
||||||
pattern.init()
|
|
||||||
let short = part.shorthand()
|
|
||||||
part.points.from = new short.Point(2, 2)
|
|
||||||
part.points.to = new short.Point(19, 76)
|
|
||||||
part.paths.test = new short.Path().move(part.points.from).line(part.points.to)
|
|
||||||
part.stack()
|
|
||||||
part.generateTransform({
|
|
||||||
move: {
|
|
||||||
x: 10,
|
|
||||||
y: 20,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
expect(part.attributes.list.transform.length).to.equal(1)
|
|
||||||
expect(part.attributes.list.transform[0]).to.equal('translate(10 20)')
|
|
||||||
})
|
|
||||||
describe('isEmpty', () => {
|
describe('isEmpty', () => {
|
||||||
it('Should return true if the part has no paths or snippets', () => {
|
it('Should return true if the part has no paths or snippets', () => {
|
||||||
const design = new Design()
|
const design = new Design()
|
||||||
|
|
|
@ -18,7 +18,7 @@ describe('Pattern', () => {
|
||||||
expect(typeof pattern.config).to.equal('object')
|
expect(typeof pattern.config).to.equal('object')
|
||||||
expect(typeof pattern.parts).to.equal('object')
|
expect(typeof pattern.parts).to.equal('object')
|
||||||
expect(typeof pattern.store).to.equal('object')
|
expect(typeof pattern.store).to.equal('object')
|
||||||
expect(Object.keys(pattern).length).to.equal(4)
|
expect(Object.keys(pattern).length).to.equal(5)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Pattern constructor should add non-enumerable properties', () => {
|
it('Pattern constructor should add non-enumerable properties', () => {
|
||||||
|
|
178
packages/core/tests/stacks.test.mjs
Normal file
178
packages/core/tests/stacks.test.mjs
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
import chai from 'chai'
|
||||||
|
import { round, Design, Point, pctBasedOn } from '../src/index.mjs'
|
||||||
|
|
||||||
|
const expect = chai.expect
|
||||||
|
|
||||||
|
describe('Stacks', () => {
|
||||||
|
describe('Pattern.init()', () => {
|
||||||
|
const partA = {
|
||||||
|
name: 'test.partA',
|
||||||
|
measurements: ['head'],
|
||||||
|
options: {
|
||||||
|
size: { pct: 40, min: 20, max: 80 },
|
||||||
|
},
|
||||||
|
draft: ({ points, Point, paths, Path, part, store, measurements, options }) => {
|
||||||
|
store.set('size', measurements.head * options.size)
|
||||||
|
points.from = new Point(0, 0)
|
||||||
|
points.to = new Point(0, store.get('size'))
|
||||||
|
paths.line = new Path().move(points.from).line(points.to)
|
||||||
|
return part
|
||||||
|
},
|
||||||
|
stack: 'box',
|
||||||
|
}
|
||||||
|
const partB = {
|
||||||
|
name: 'test.partB',
|
||||||
|
measurements: ['head'],
|
||||||
|
after: partA,
|
||||||
|
draft: ({ points, Point, paths, Path, part, store }) => {
|
||||||
|
points.from = new Point(0, store.get('size'))
|
||||||
|
points.to = new Point(store.get('size'), store.get('size'))
|
||||||
|
paths.line = new Path().move(points.from).line(points.to)
|
||||||
|
return part
|
||||||
|
},
|
||||||
|
stack: 'box',
|
||||||
|
}
|
||||||
|
const partC = {
|
||||||
|
name: 'test.partC',
|
||||||
|
after: partB,
|
||||||
|
draft: ({ points, Point, paths, Path, part, store }) => {
|
||||||
|
points.from = new Point(store.get('size'), store.get('size'))
|
||||||
|
points.to = new Point(store.get('size'), 0)
|
||||||
|
paths.line = new Path().move(points.from).line(points.to)
|
||||||
|
return part
|
||||||
|
},
|
||||||
|
stack: 'box',
|
||||||
|
}
|
||||||
|
const partD = {
|
||||||
|
name: 'test.partD',
|
||||||
|
after: partC,
|
||||||
|
draft: ({ points, Point, paths, Path, part, store }) => {
|
||||||
|
points.from = new Point(store.get('size'), 0)
|
||||||
|
points.to = new Point(0, 0)
|
||||||
|
paths.line = new Path().move(points.from).line(points.to)
|
||||||
|
return part
|
||||||
|
},
|
||||||
|
// stack: 'box',
|
||||||
|
}
|
||||||
|
|
||||||
|
const Pattern = new Design({
|
||||||
|
data: {
|
||||||
|
name: 'test',
|
||||||
|
version: '1.2.3',
|
||||||
|
},
|
||||||
|
parts: [partD],
|
||||||
|
})
|
||||||
|
const pattern = new Pattern({
|
||||||
|
measurements: {
|
||||||
|
head: 400,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
pattern.draft()
|
||||||
|
//console.log(pattern.parts)
|
||||||
|
//pattern.render()
|
||||||
|
|
||||||
|
it('Pattern.init() should resolve dependencies', () => {
|
||||||
|
expect(typeof pattern.config.resolvedDependencies).to.equal('object')
|
||||||
|
expect(Array.isArray(pattern.config.resolvedDependencies['test.partA'])).to.equal(true)
|
||||||
|
expect(pattern.config.resolvedDependencies['test.partA'].length).to.equal(0)
|
||||||
|
expect(Array.isArray(pattern.config.resolvedDependencies['test.partB'])).to.equal(true)
|
||||||
|
expect(pattern.config.resolvedDependencies['test.partB'].length).to.equal(1)
|
||||||
|
expect(pattern.config.resolvedDependencies['test.partB'][0]).to.equal('test.partA')
|
||||||
|
expect(Array.isArray(pattern.config.resolvedDependencies['test.partC'])).to.equal(true)
|
||||||
|
expect(pattern.config.resolvedDependencies['test.partC'].length).to.equal(2)
|
||||||
|
expect(
|
||||||
|
pattern.config.resolvedDependencies['test.partC'].indexOf('test.partA') !== -1
|
||||||
|
).to.equal(true)
|
||||||
|
expect(
|
||||||
|
pattern.config.resolvedDependencies['test.partC'].indexOf('test.partB') !== -1
|
||||||
|
).to.equal(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
it('Should calculate the part boundary', () => {
|
||||||
|
const part = {
|
||||||
|
name: 'test',
|
||||||
|
draft: ({points, Point, paths, Path, part }) => {
|
||||||
|
points.from = new Point(123, 456)
|
||||||
|
points.to = new Point(19, 76)
|
||||||
|
paths.test = new Path().move(points.from).line(points.to)
|
||||||
|
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const design = new Design({ parts: [ part ]})
|
||||||
|
const pattern = new design()
|
||||||
|
pattern.draft().render()
|
||||||
|
expect(pattern.stacks.test.topLeft.x).to.equal(17)
|
||||||
|
expect(pattern.stacks.test.topLeft.y).to.equal(74)
|
||||||
|
expect(pattern.stacks.test.bottomRight.x).to.equal(125)
|
||||||
|
expect(pattern.stacks.test.bottomRight.y).to.equal(458)
|
||||||
|
expect(pattern.stacks.test.width).to.equal(108)
|
||||||
|
expect(pattern.stacks.test.height).to.equal(384)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should calculate the part boundary with custom margin', () => {
|
||||||
|
const part = {
|
||||||
|
name: 'test',
|
||||||
|
draft: ({points, Point, paths, Path, part }) => {
|
||||||
|
points.from = new Point(123, 456)
|
||||||
|
points.to = new Point(19, 76)
|
||||||
|
paths.test = new Path().move(points.from).line(points.to)
|
||||||
|
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const design = new Design({ parts: [ part ]})
|
||||||
|
const pattern = new design({ margin: 5 })
|
||||||
|
pattern.draft().render()
|
||||||
|
expect(pattern.stacks.test.topLeft.x).to.equal(14)
|
||||||
|
expect(pattern.stacks.test.topLeft.y).to.equal(71)
|
||||||
|
expect(pattern.stacks.test.bottomRight.x).to.equal(128)
|
||||||
|
expect(pattern.stacks.test.bottomRight.y).to.equal(461)
|
||||||
|
expect(pattern.stacks.test.width).to.equal(114)
|
||||||
|
expect(pattern.stacks.test.height).to.equal(390)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should calculate the part boundary for paperless', () => {
|
||||||
|
const part = {
|
||||||
|
name: 'test',
|
||||||
|
draft: ({points, Point, paths, Path, part }) => {
|
||||||
|
points.from = new Point(123, 456)
|
||||||
|
points.to = new Point(19, 76)
|
||||||
|
paths.test = new Path().move(points.from).line(points.to)
|
||||||
|
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const design = new Design({ parts: [ part ]})
|
||||||
|
const pattern = new design({ paperless: true })
|
||||||
|
pattern.draft().render()
|
||||||
|
expect(pattern.stacks.test.topLeft.x).to.equal(9)
|
||||||
|
expect(pattern.stacks.test.topLeft.y).to.equal(66)
|
||||||
|
})
|
||||||
|
it('Should generate the part transforms', () => {
|
||||||
|
const part = {
|
||||||
|
name: 'test',
|
||||||
|
draft: ({points, Point, paths, Path, part }) => {
|
||||||
|
points.from = new Point(2, 2)
|
||||||
|
points.to = new Point(19, 76)
|
||||||
|
paths.test = new Path().move(points.from).line(points.to)
|
||||||
|
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const design = new Design({ parts: [ part ] })
|
||||||
|
const pattern = new design()
|
||||||
|
pattern.draft().render()
|
||||||
|
pattern.stacks.test.generateTransform({
|
||||||
|
move: {
|
||||||
|
x: 10,
|
||||||
|
y: 20,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(pattern.stacks.test.attributes.list.transform.length).to.equal(1)
|
||||||
|
expect(pattern.stacks.test.attributes.list.transform[0]).to.equal('translate(10 20)')
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
|
@ -28,7 +28,7 @@ import {
|
||||||
rad2deg,
|
rad2deg,
|
||||||
pctBasedOn,
|
pctBasedOn,
|
||||||
Bezier,
|
Bezier,
|
||||||
generatePartTransform,
|
generateStackTransform,
|
||||||
macroName,
|
macroName,
|
||||||
Design,
|
Design,
|
||||||
} from '../src/index.mjs'
|
} from '../src/index.mjs'
|
||||||
|
|
|
@ -27,7 +27,7 @@ const LabDraft = props => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{(!patternProps || patternProps.events?.error?.length > 0)
|
{(!patternProps || patternProps.logs?.error?.length > 0)
|
||||||
? <Error {...{ draft, patternProps, updateGist }} />
|
? <Error {...{ draft, patternProps, updateGist }} />
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
20
sites/shared/components/workbench/draft/stack.js
Normal file
20
sites/shared/components/workbench/draft/stack.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import Part from './part'
|
||||||
|
import { getProps } from './utils'
|
||||||
|
|
||||||
|
const Stack = props => {
|
||||||
|
const { stackName, stack, patternProps, gist, app, updateGist, unsetGist, showInfo } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g {...getProps(stack)} id={`stack-${stackName}`}>
|
||||||
|
{[...stack.parts].map((part) => (
|
||||||
|
<Part {...{ app, gist, updateGist, unsetGist, showInfo }}
|
||||||
|
key={part.name}
|
||||||
|
partName={part.name}
|
||||||
|
part={part}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</g>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Stack
|
|
@ -2,7 +2,7 @@ import { SizeMe } from 'react-sizeme'
|
||||||
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch"
|
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch"
|
||||||
import Svg from './svg'
|
import Svg from './svg'
|
||||||
import Defs from './defs'
|
import Defs from './defs'
|
||||||
import Part from './part'
|
import Stack from './stack'
|
||||||
|
|
||||||
/* What's with all the wrapping?
|
/* What's with all the wrapping?
|
||||||
*
|
*
|
||||||
|
@ -40,11 +40,11 @@ const SvgWrapper = props => {
|
||||||
<Defs {...patternProps} />
|
<Defs {...patternProps} />
|
||||||
<style>{`:root { --pattern-scale: ${gist.scale || 1}} ${patternProps.svg.style}`}</style>
|
<style>{`:root { --pattern-scale: ${gist.scale || 1}} ${patternProps.svg.style}`}</style>
|
||||||
<g>
|
<g>
|
||||||
{Object.keys(patternProps.parts).map((name) => (
|
{Object.keys(patternProps.stacks).map((stackName) => (
|
||||||
<Part {...{ app, gist, updateGist, unsetGist, showInfo }}
|
<Stack {...{ app, gist, updateGist, unsetGist, showInfo, patternProps }}
|
||||||
key={name}
|
key={stackName}
|
||||||
partName={name}
|
stackName={stackName}
|
||||||
part={patternProps.parts[name]}
|
stack={patternProps.stacks[stackName]}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</g>
|
</g>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue