diff --git a/packages/core/src/index.mjs b/packages/core/src/index.mjs index 9a3aa19091d..2707e475770 100644 --- a/packages/core/src/index.mjs +++ b/packages/core/src/index.mjs @@ -32,7 +32,7 @@ import { rad2deg, pctBasedOn, Bezier, - generatePartTransform, + generateStackTransform, macroName, } from './utils.mjs' import { version } from '../data.mjs' @@ -71,7 +71,7 @@ export { deg2rad, rad2deg, pctBasedOn, - generatePartTransform, + generateStackTransform, macroName, isCoord, version, diff --git a/packages/core/src/part.mjs b/packages/core/src/part.mjs index 99f12603d41..4c1e5bd9c8f 100644 --- a/packages/core/src/part.mjs +++ b/packages/core/src/part.mjs @@ -26,6 +26,7 @@ export function Part() { this.points = {} this.paths = {} this.snippets = {} + this.name = null return this } @@ -110,31 +111,15 @@ Part.prototype.boundary = function () { if (topLeft.y === Infinity) topLeft.y = 0 if (bottomRight.x === -Infinity) bottomRight.x = 0 if (bottomRight.y === -Infinity) bottomRight.y = 0 - // Add margin - let margin = this.context.settings.margin - if (this.context.settings.paperless && margin < 10) margin = 10 - this.topLeft = new Point(topLeft.x - margin, topLeft.y - margin) - this.bottomRight = new Point(bottomRight.x + margin, bottomRight.y + margin) + + this.topLeft = topLeft + this.bottomRight = bottomRight this.width = this.bottomRight.x - this.topLeft.x this.height = this.bottomRight.y - this.topLeft.y 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 */ Part.prototype.attr = function (name, value, overwrite = false) { if (overwrite) this.attributes.set(name, value) @@ -351,15 +336,6 @@ Part.prototype.shorthand = function () { 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 () { if (Object.keys(this.snippets).length > 0) return false diff --git a/packages/core/src/pattern.mjs b/packages/core/src/pattern.mjs index 9322d644ce3..ca57b70c1a6 100644 --- a/packages/core/src/pattern.mjs +++ b/packages/core/src/pattern.mjs @@ -9,6 +9,7 @@ import { mergeDependencies, } from './utils.mjs' import { Part } from './part.mjs' +import { Stack } from './stack.mjs' import { Point } from './point.mjs' import { Path } from './path.mjs' import { Snippet } from './snippet.mjs' @@ -23,7 +24,7 @@ export function Pattern(config) { addNonEnumProp(this, 'plugins', {}) addNonEnumProp(this, 'width', 0) addNonEnumProp(this, 'height', 0) - addNonEnumProp(this, 'autoLayout', { parts: {} }) + addNonEnumProp(this, 'autoLayout', { stacks: {} }) addNonEnumProp(this, 'is', '') addNonEnumProp(this, 'hooks', new Hooks()) addNonEnumProp(this, 'Point', Point) @@ -41,6 +42,7 @@ export function Pattern(config) { // Enumerable properties this.config = config // Design config this.parts = {} // Drafted parts container + this.stacks = {} // Drafted stacks container this.store = new Store() // Store for sharing data across parts return this @@ -86,6 +88,7 @@ Pattern.prototype.__createPartWithContext = function (name) { // Context object to add to Part closure const part = new Part() part.name = name + part.stack = this.__parts[name]?.stack || name part.context = { parts: this.parts, config: this.config, @@ -101,6 +104,19 @@ Pattern.prototype.__createPartWithContext = function (name) { 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 Pattern.prototype.__loadOptionDefaults = function () { if (Object.keys(this.config.options).length < 1) return this @@ -536,40 +552,48 @@ Pattern.prototype.macro = function (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 () { if (this.store.logs.error.length > 0) { this.store.log.warning(`One or more errors occured. Not packing pattern parts`) 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 = [] - for (let key in this.parts) { - let part = this.parts[key] - // Avoid multiple render calls to cause stacking of transforms - part.attributes.remove('transform') - if (part.render) { - part.stack() - let width = part.bottomRight.x - part.topLeft.x - let height = part.bottomRight.y - part.topLeft.y - if (this.settings.layout === true) bins.push({ id: key, width, height }) + for (const [key, stack] of Object.entries(this.stacks)) { + // Avoid multiple render calls to cause addition of transforms + stack.attributes.remove('transform') + if (!this.isStackHidden(key)) { + stack.home() + if (this.settings.layout === true) + bins.push({ id: key, width: stack.width, height: stack.height }) else { - if (this.width < width) this.width = width - if (this.height < height) this.height = height + if (this.width < stack.width) this.width = stack.width + if (this.height < stack.height) this.height = stack.height } } } if (this.settings.layout === true) { let size = pack(bins, { inPlace: true }) for (let bin of bins) { - this.autoLayout.parts[bin.id] = { move: {} } - let part = this.parts[bin.id] + this.autoLayout.stacks[bin.id] = { move: {} } + let stack = this.stacks[bin.id] 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.parts[bin.id].move = { - x: bin.x + part.layout.move.x, - y: bin.y + part.layout.move.y, + this.autoLayout.stacks[bin.id].move = { + x: bin.x + stack.layout.move.x, + y: bin.y + stack.layout.move.y, } } this.width = size.width @@ -577,11 +601,11 @@ Pattern.prototype.pack = function () { } else if (typeof this.settings.layout === 'object') { this.width = this.settings.layout.width 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 - if (this.parts[partId]) { - let transforms = this.settings.layout.parts[partId] - this.parts[partId].generateTransform(transforms) + if (this.stacks[stackId]) { + let transforms = this.settings.layout.stacks[stackId] + this.stacks[stackId].generateTransform(transforms) } } } @@ -783,6 +807,23 @@ Pattern.prototype.isHidden = function (partName) { 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 * 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 } diff --git a/packages/core/src/stack.mjs b/packages/core/src/stack.mjs new file mode 100644 index 00000000000..134aa835ef3 --- /dev/null +++ b/packages/core/src/stack.mjs @@ -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 diff --git a/packages/core/src/store.mjs b/packages/core/src/store.mjs index b1ff749afe3..bf162122916 100644 --- a/packages/core/src/store.mjs +++ b/packages/core/src/store.mjs @@ -5,12 +5,6 @@ import get from 'lodash.get' const avoid = ['set', 'setIfUnset', 'push', 'unset', 'get', 'extend'] 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 * You can override these with a plugin @@ -37,6 +31,12 @@ export function Store(methods = []) { } 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 } diff --git a/packages/core/src/utils.mjs b/packages/core/src/utils.mjs index 7a9aed989e6..824dcc52842 100644 --- a/packages/core/src/utils.mjs +++ b/packages/core/src/utils.mjs @@ -361,8 +361,8 @@ export function pctBasedOn(measurement) { } } -/** Generates the transform attributes needed for a given part */ -export const generatePartTransform = (x, y, rotate, flipX, flipY, part) => { +/** Generates the transform attributes needed for a given stack */ +export const generateStackTransform = (x, y, rotate, flipX, flipY, part) => { const transforms = [] let xTotal = x || 0 let yTotal = y || 0 diff --git a/packages/core/tests/part.test.mjs b/packages/core/tests/part.test.mjs index 659bd95c9ac..3dcf6b99d4e 100644 --- a/packages/core/tests/part.test.mjs +++ b/packages/core/tests/part.test.mjs @@ -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 pattern = new design() const part = pattern.__createPartWithContext() @@ -134,52 +134,15 @@ describe('Part', () => { 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(17) - expect(boundary.topLeft.y).to.equal(74) - expect(boundary.bottomRight.x).to.equal(125) - expect(boundary.bottomRight.y).to.equal(458) + expect(boundary.topLeft.x).to.equal(19) + expect(boundary.topLeft.y).to.equal(76) + expect(boundary.bottomRight.x).to.equal(123) + expect(boundary.bottomRight.y).to.equal(456) boundary = part.boundary() - expect(boundary.width).to.equal(108) - expect(boundary.height).to.equal(384) + expect(boundary.width).to.equal(104) + 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', () => { const part = { @@ -195,7 +158,7 @@ describe('Part', () => { const design = new Design({ parts: [ part ]}) const pattern = new design({ paperless: true }) pattern.draft() - pattern.parts.test.stack() + pattern.parts.test.home() console.log(pattern.parts.test.attributes) 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.to = new short.Point(19, 76) 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) - part.stack() + part.home() 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', () => { it('Should return true if the part has no paths or snippets', () => { const design = new Design() diff --git a/packages/core/tests/pattern-init.test.mjs b/packages/core/tests/pattern-init.test.mjs index 258ac0cb1d7..f1a094e9a2d 100644 --- a/packages/core/tests/pattern-init.test.mjs +++ b/packages/core/tests/pattern-init.test.mjs @@ -18,7 +18,7 @@ describe('Pattern', () => { expect(typeof pattern.config).to.equal('object') expect(typeof pattern.parts).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', () => { diff --git a/packages/core/tests/stacks.test.mjs b/packages/core/tests/stacks.test.mjs new file mode 100644 index 00000000000..6191ca93e0e --- /dev/null +++ b/packages/core/tests/stacks.test.mjs @@ -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)') + }) + + }) +}) diff --git a/packages/core/tests/utils.test.mjs b/packages/core/tests/utils.test.mjs index 22880596544..87ef4e349ba 100644 --- a/packages/core/tests/utils.test.mjs +++ b/packages/core/tests/utils.test.mjs @@ -28,7 +28,7 @@ import { rad2deg, pctBasedOn, Bezier, - generatePartTransform, + generateStackTransform, macroName, Design, } from '../src/index.mjs' diff --git a/sites/shared/components/workbench/draft/index.js b/sites/shared/components/workbench/draft/index.js index 4e6a3959d2d..b0625c095e8 100644 --- a/sites/shared/components/workbench/draft/index.js +++ b/sites/shared/components/workbench/draft/index.js @@ -27,7 +27,7 @@ const LabDraft = props => { return ( <> - {(!patternProps || patternProps.events?.error?.length > 0) + {(!patternProps || patternProps.logs?.error?.length > 0) ? : null } diff --git a/sites/shared/components/workbench/draft/stack.js b/sites/shared/components/workbench/draft/stack.js new file mode 100644 index 00000000000..e733e205759 --- /dev/null +++ b/sites/shared/components/workbench/draft/stack.js @@ -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 ( + + {[...stack.parts].map((part) => ( + + ))} + + ) +} + +export default Stack diff --git a/sites/shared/components/workbench/draft/svg-wrapper.js b/sites/shared/components/workbench/draft/svg-wrapper.js index 8f737841084..6aeaf14a799 100644 --- a/sites/shared/components/workbench/draft/svg-wrapper.js +++ b/sites/shared/components/workbench/draft/svg-wrapper.js @@ -2,7 +2,7 @@ import { SizeMe } from 'react-sizeme' import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch" import Svg from './svg' import Defs from './defs' -import Part from './part' +import Stack from './stack' /* What's with all the wrapping? * @@ -40,11 +40,11 @@ const SvgWrapper = props => { - {Object.keys(patternProps.parts).map((name) => ( - ( + ))}