1
0
Fork 0
freesewing/packages/core/src/stack.mjs
joostdecock a2800dddda 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

157 lines
4.9 KiB
JavaScript

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 stack object suitbale for renderprops */
Stack.prototype.asRenderProps = function () {
return {
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()),
}
}
/* Returns a list of parts in this stack */
Stack.prototype.getPartList = function () {
return [...this.parts]
}
/* Returns a list of names of parts in this stack */
Stack.prototype.getPartNames = function () {
return [...this.parts].map((p) => p.name)
}
/** 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()
const { tl, br } = utils.getTransformedBounds(part, part.attributes.getAsArray('transform'))
if (!tl) {
continue
}
// get the top left, the minimum x and y values of any corner
this.topLeft.x = Math.min(this.topLeft.x, tl.x)
this.topLeft.y = Math.min(this.topLeft.y, tl.y)
// get the bottom right, the maximum x and y values of any corner
this.bottomRight.x = Math.max(this.bottomRight.x, br.x)
this.bottomRight.y = Math.max(this.bottomRight.y, br.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 = 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
}
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()
// 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
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 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
*/
Stack.prototype.generateTransform = function (transforms) {
const { move, rotate, flipX, flipY } = transforms
const generated = utils.generateStackTransform(move?.x, move?.y, rotate, flipX, flipY, this)
this.attributes.remove('transform')
generated.forEach((t) => this.attr('transform', t))
return this
}
export default Stack