@freesewing/core
Version:
A library for creating made-to-measure sewing patterns
164 lines (144 loc) • 4.69 kB
JavaScript
import { Svg } from '../svg.mjs'
import { Stack } from '../stack.mjs'
/**
* A class for handling layout and rendering for a pattern
* @param {Pattern} pattern the pattern to layout or render
*/
export function PatternRenderer(pattern) {
this.pattern = pattern
this.autoLayout = pattern.autoLayout
}
/**
* Renders the pattern to SVG
*
* @return {string} svg - The rendered SVG
*/
PatternRenderer.prototype.render = function () {
this.__startRender()
this.pattern.svg = this.svg
return this.svg.render()
}
/** Returns props required to render this pattern through
* an external renderer (eg. a React component)
*
* @return {object} this - The Pattern instance
*/
PatternRenderer.prototype.getRenderProps = function () {
this.pattern.store.log.info('Gathering render props')
// Run pre-render hook
this.__startRender()
this.svg.__runHooks('preRender')
const props = {
svg: this.svg.asRenderProps(),
width: this.pattern.width,
height: this.pattern.height,
autoLayout: this.pattern.autoLayout,
settings: this.pattern.settings,
stacks: {},
}
for (let s in this.pattern.stacks) {
if (!this.pattern.__isStackHidden(s)) {
props.stacks[s] = this.pattern.stacks[s].asRenderProps()
} else this.pattern.store.log.info(`Stack ${s} is hidden. Skipping in render props.`)
}
this.svg.__runHooks('postRender')
return props
}
PatternRenderer.prototype.__startRender = function () {
this.svg = new Svg(this.pattern)
this.svg.hooks = this.pattern.plugins.hooks
this.__pack()
return this
}
PatternRenderer.prototype.__stack = function () {
// First, create all stacks
this.stacks = {}
const settings = this.pattern.settings
for (const set in settings) {
for (const [name, part] of Object.entries(this.pattern.parts[set])) {
const stackName =
settings[set].stackPrefix +
(typeof part.stack === 'function' ? part.stack(settings[set], name) : part.stack)
if (typeof this.stacks[stackName] === 'undefined')
this.stacks[stackName] = this.__createStackWithContext(stackName, set)
this.stacks[stackName].addPart(part)
}
}
this.pattern.stacks = this.stacks
}
/**
* Packs stacks in a 2D space and sets pattern size
*
* @private
* @return {Pattern} this - The Pattern instance
*/
PatternRenderer.prototype.__pack = function () {
this.pattern.__runHooks('preLayout')
const { settings, setStores, activeSet } = this.pattern
for (const set in settings) {
if (setStores[set].logs.error.length > 0) {
setStores[set].log.warn(`One or more errors occured. Not packing pattern parts`)
return this
}
}
this.__stack()
let bins = []
for (const [key, stack] of Object.entries(this.stacks)) {
// Avoid multiple render calls to cause addition of transforms
stack.attributes.remove('transform')
if (!this.pattern.__isStackHidden(key)) {
stack.home()
if (settings[activeSet].layout === true)
bins.push({ id: key, width: stack.width, height: stack.height })
}
}
if (settings[activeSet].layout === true) {
// store.pack is provided by a plugin
const size = bins.length > 0 ? this.pattern.store.pack(bins, this) : { width: 0, height: 0 }
this.autoLayout.width = size.width
this.autoLayout.height = size.height
for (let bin of bins) {
let stack = this.stacks[bin.id]
this.autoLayout.stacks[bin.id] = {
move: {
x: bin.x + stack.layout.move.x,
y: bin.y + stack.layout.move.y,
},
}
}
}
const packedLayout =
typeof settings[activeSet].layout === 'object' ? settings[activeSet].layout : this.autoLayout
this.width = packedLayout.width
this.height = packedLayout.height
for (let stackId of Object.keys(packedLayout.stacks)) {
// Some parts are added by late-stage plugins
if (this.stacks[stackId]) {
let transforms = packedLayout.stacks[stackId]
this.stacks[stackId].generateTransform(transforms)
}
}
this.pattern.width = this.width
this.pattern.height = this.height
this.pattern.autoLayout = this.autoLayout
this.pattern.__runHooks('postLayout')
return this
}
/**
* Instantiates a new Stack instance and populates it with the pattern context
*
* @private
* @param {string} name - The name of the stack
* @return {Stack} stack - The instantiated Stack
*/
PatternRenderer.prototype.__createStackWithContext = function (name) {
// Context object to add to Stack closure
const stack = new Stack()
stack.name = name
stack.context = {
config: this.pattern.config,
settings: this.pattern.settings,
setStores: this.pattern.setStores,
}
return stack
}