2022-09-12 20:10:22 +02:00
|
|
|
import { Attributes } from './attributes.mjs'
|
|
|
|
import { Point } from './point.mjs'
|
|
|
|
import * as utils from './utils.mjs'
|
|
|
|
|
2022-09-13 17:56:01 +02:00
|
|
|
export function Stack(name = null) {
|
2022-09-12 20:10:22 +02:00
|
|
|
// Non-enumerable properties
|
2022-09-18 15:11:10 +02:00
|
|
|
utils.__addNonEnumProp(this, 'freeId', 0)
|
|
|
|
utils.__addNonEnumProp(this, 'layout', { move: { x: 0, y: 0 } })
|
2022-09-12 20:10:22 +02:00
|
|
|
|
|
|
|
// Enumerable properties
|
|
|
|
this.attributes = new Attributes()
|
|
|
|
this.parts = new Set()
|
|
|
|
this.name = name
|
2022-09-14 12:24:09 +02:00
|
|
|
this.topLeft = false
|
|
|
|
this.bottomRight = false
|
|
|
|
this.width = false
|
|
|
|
this.height = false
|
2022-09-12 20:10:22 +02:00
|
|
|
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Adds a part to the stack */
|
2022-09-13 17:56:01 +02:00
|
|
|
Stack.prototype.addPart = function (part) {
|
2022-09-12 20:10:22 +02:00
|
|
|
if (part) this.parts.add(part)
|
|
|
|
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2022-09-24 18:37:49 +02:00
|
|
|
/* Returns a stack object suitbale for renderprops */
|
feat(core): Added Pattern.getLogs() and updated Pattern.getRenderProps()
The data returned by `Pattern.getRenderProps()` was not serializable as
we were returning `this` all over the place, thereby including marcors,
log methods, cyclic object references, and so on.
This commit changes that by implementing a `.asRenderProp()` method on
all of the various objects (stack, part, path, point, snippet,
attributes, svg) and only including data that can be serialized.
In addition, we no longer include the logs in the renderProps because
they are not related to rendering the pattern.
Instead, the new method `Pattern.getLogs()` gives you the logs.
2023-06-01 16:45:13 +02:00
|
|
|
Stack.prototype.asRenderProps = function () {
|
2022-09-24 18:37:49 +02:00
|
|
|
return {
|
feat(core): Added Pattern.getLogs() and updated Pattern.getRenderProps()
The data returned by `Pattern.getRenderProps()` was not serializable as
we were returning `this` all over the place, thereby including marcors,
log methods, cyclic object references, and so on.
This commit changes that by implementing a `.asRenderProp()` method on
all of the various objects (stack, part, path, point, snippet,
attributes, svg) and only including data that can be serialized.
In addition, we no longer include the logs in the renderProps because
they are not related to rendering the pattern.
Instead, the new method `Pattern.getLogs()` gives you the logs.
2023-06-01 16:45:13 +02:00
|
|
|
name: this.name,
|
|
|
|
attributes: this.attributes.asRenderProps(),
|
|
|
|
topLeft: this.topLeft,
|
|
|
|
bottomRight: this.bottomRight,
|
|
|
|
width: this.width,
|
|
|
|
height: this.height,
|
|
|
|
parts: [...this.parts].map((part) => part.asRenderProps()),
|
2022-09-24 18:37:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-12 20:10:22 +02:00
|
|
|
/* Returns a list of parts in this stack */
|
2022-09-15 06:29:10 +02:00
|
|
|
Stack.prototype.getPartList = function () {
|
2022-09-12 20:10:22 +02:00
|
|
|
return [...this.parts]
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns a list of names of parts in this stack */
|
2022-09-15 06:29:10 +02:00
|
|
|
Stack.prototype.getPartNames = function () {
|
2022-09-13 17:56:01 +02:00
|
|
|
return [...this.parts].map((p) => p.name)
|
2022-09-12 20:10:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Calculates the stack's bounding box and sets it */
|
|
|
|
Stack.prototype.home = function () {
|
2022-09-13 17:56:01 +02:00
|
|
|
if (this.topLeft) return this // Cached
|
2022-09-12 20:10:22 +02:00
|
|
|
this.topLeft = new Point(Infinity, Infinity)
|
|
|
|
this.bottomRight = new Point(-Infinity, -Infinity)
|
|
|
|
for (const part of this.getPartList()) {
|
2022-09-18 15:11:10 +02:00
|
|
|
part.__boundary()
|
2023-04-14 15:42:22 -04:00
|
|
|
|
2023-04-28 15:46:33 -04:00
|
|
|
const { tl, br } = utils.getTransformedBounds(part, part.attributes.getAsArray('transform'))
|
|
|
|
|
|
|
|
if (!tl) {
|
|
|
|
continue
|
2023-04-14 15:42:22 -04:00
|
|
|
}
|
|
|
|
|
2023-04-14 23:49:45 -04:00
|
|
|
// get the top left, the minimum x and y values of any corner
|
2023-04-28 15:46:33 -04:00
|
|
|
this.topLeft.x = Math.min(this.topLeft.x, tl.x)
|
|
|
|
this.topLeft.y = Math.min(this.topLeft.y, tl.y)
|
2023-04-14 23:49:45 -04:00
|
|
|
// get the bottom right, the maximum x and y values of any corner
|
2023-04-28 15:46:33 -04:00
|
|
|
this.bottomRight.x = Math.max(this.bottomRight.x, br.x)
|
|
|
|
this.bottomRight.y = Math.max(this.bottomRight.y, br.y)
|
2022-09-12 20:10:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
2022-09-13 17:56:01 +02:00
|
|
|
// Add margin
|
2022-09-17 10:24:13 +02:00
|
|
|
let margin = 0
|
|
|
|
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
|
|
|
|
}
|
2022-09-13 17:56:01 +02:00
|
|
|
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
|
2022-09-12 20:10:22 +02:00
|
|
|
this.width = this.bottomRight.x - this.topLeft.x
|
|
|
|
this.height = this.bottomRight.y - this.topLeft.y
|
|
|
|
|
2022-09-13 17:56:01 +02:00
|
|
|
// Add transform
|
2022-09-17 10:24:13 +02:00
|
|
|
//this.anchor = this.getAnchor()
|
|
|
|
// FIXME: Can we be certain this is always (0,0) /
|
|
|
|
this.anchor = new Point(0, 0)
|
2022-09-13 17:56:01 +02:00
|
|
|
|
|
|
|
if (this.topLeft.x === this.anchor.x && this.topLeft.y === this.anchor.y) return this
|
|
|
|
else {
|
2022-09-14 15:04:24 +02:00
|
|
|
this.attr(
|
|
|
|
'transform',
|
|
|
|
`translate(${this.anchor.x - this.topLeft.x}, ${this.anchor.y - this.topLeft.y})`
|
|
|
|
)
|
2022-09-13 17:56:01 +02:00
|
|
|
this.layout.move.x = this.anchor.x - this.topLeft.x
|
|
|
|
this.layout.move.y = this.anchor.y - this.topLeft.y
|
|
|
|
}
|
|
|
|
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2022-09-14 12:24:09 +02:00
|
|
|
/** Finds the anchor to align parts in this stack */
|
2022-09-14 15:04:24 +02:00
|
|
|
Stack.prototype.getAnchor = function () {
|
2022-09-13 17:56:01 +02:00
|
|
|
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
|
|
|
|
|
2022-09-14 15:04:24 +02:00
|
|
|
return new Point(0, 0)
|
2022-09-13 17:56:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** 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)
|
|
|
|
|
2022-09-12 20:10:22 +02:00
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2023-04-28 15:46:33 -04:00
|
|
|
/**
|
|
|
|
* Generates the transforms for a stack and sets them as attributes
|
|
|
|
* @param {Object} transforms a transform config object
|
|
|
|
* @param {Object} transforms.move x and y coordinates for how far to translate the stack
|
|
|
|
* @param {Number} transfroms.rotate the number of degrees to rotate the stack around its center
|
|
|
|
* @param {Boolean} tranforms.flipX whether to flip the stack along the X axis
|
|
|
|
* @param {Boolean} transforms.flipY whether to flip the stack along the Y axis
|
|
|
|
*/
|
2022-09-14 12:24:09 +02:00
|
|
|
Stack.prototype.generateTransform = function (transforms) {
|
|
|
|
const { move, rotate, flipX, flipY } = transforms
|
2022-09-25 09:18:41 +02:00
|
|
|
const generated = utils.generateStackTransform(move?.x, move?.y, rotate, flipX, flipY, this)
|
2022-09-14 12:24:09 +02:00
|
|
|
|
2023-04-28 15:46:33 -04:00
|
|
|
this.attributes.remove('transform')
|
|
|
|
generated.forEach((t) => this.attr('transform', t))
|
|
|
|
|
|
|
|
return this
|
2022-09-14 12:24:09 +02:00
|
|
|
}
|
|
|
|
|
2022-09-12 20:10:22 +02:00
|
|
|
export default Stack
|