1
0
Fork 0

use svg transforms to flip and rotate cutlist parts

This commit is contained in:
Enoch Riese 2023-04-15 14:40:51 -04:00
parent cf3d85b223
commit 5c80c8dbc3
3 changed files with 50 additions and 100 deletions

View file

@ -17,16 +17,16 @@ export const cutonfoldHooks = {
} }
// Export macros // Export macros
export const cutonfoldMacros = { export const cutonfoldMacros = {
cutonfold: function (so, { points, paths, Path, complete, setCutOnFold, setGrain, scale }) { cutonfold: function (so, { points, paths, Path, complete, store, scale }) {
if (so === false) { if (so === false) {
delete points.cutonfoldFrom delete points.cutonfoldFrom
delete points.cutonfoldTo delete points.cutonfoldTo
delete points.cutonfoldVia1 delete points.cutonfoldVia1
delete points.cutonfoldVia2 delete points.cutonfoldVia2
delete paths.cutonfold delete paths.cutonfoldCutonfold
// setCutOnFold relies on plugin-cutlist // setCutOnFold relies on plugin-cutlist
if (typeof setCutOnFold === 'function') { if (typeof store.cutlist?.setCutOnFold === 'function') {
setCutOnFold(false) // Restore default store.cutlist.setCutOnFold(false) // Restore default
} }
return true return true
} }
@ -36,9 +36,9 @@ export const cutonfoldMacros = {
prefix: 'cutonfold', prefix: 'cutonfold',
...so, ...so,
} }
if (typeof setCutOnFold === 'function') { if (typeof store.cutlist?.setCutOnFold === 'function') {
setCutOnFold(so.from, so.to) store.cutlist.setCutOnFold(so.from, so.to)
if (so.grainline) setGrain(so.from.angle(so.to)) if (so.grainline) store.cutlist.setGrain(so.from.angle(so.to))
} }
if (complete) { if (complete) {
points[so.prefix + 'From'] = so.from.shiftFractionTowards(so.to, so.margin / 100) points[so.prefix + 'From'] = so.from.shiftFractionTowards(so.to, so.margin / 100)

View file

@ -17,12 +17,12 @@ export const grainlineHooks = {
], ],
} }
export const grainlineMacros = { export const grainlineMacros = {
grainline: function (so = {}, { points, paths, Path, complete, setGrain }) { grainline: function (so = {}, { points, paths, Path, complete, store }) {
if (so === false) { if (so === false) {
delete points.grainlineFrom delete points.grainlineFrom
delete points.grainlineTo delete points.grainlineTo
delete paths.grainline delete paths.grainline
setGrain(90) // Restoring default if (store.cutlist?.setGrain) store.cutlist.setGrain(90) // Restoring default
return true return true
} }
so = { so = {
@ -30,8 +30,8 @@ export const grainlineMacros = {
...so, ...so,
} }
// setGrain relies on plugin-cutlist // setGrain relies on plugin-cutlist
if (typeof setGrain === 'function') { if (typeof store.cutlist?.setGrain === 'function') {
setGrain(so.from.angle(so.to)) store.cutlist.setGrain(so.from.angle(so.to))
} }
if (complete) { if (complete) {
points.grainlineFrom = so.from.shiftFractionTowards(so.to, 0.05) points.grainlineFrom = so.from.shiftFractionTowards(so.to, 0.05)

View file

@ -1,5 +1,4 @@
import { addToOnly } from '../plugin-layout-part.mjs' import { addToOnly } from '../plugin-layout-part.mjs'
import { pluginFlip } from '@freesewing/plugin-flip'
import { pluginMirror } from '@freesewing/plugin-mirror' import { pluginMirror } from '@freesewing/plugin-mirror'
const prefix = 'mirroredOnFold' const prefix = 'mirroredOnFold'
@ -52,49 +51,39 @@ export const cutLayoutPlugin = function (material, grainAngle) {
part.hide() part.hide()
// for each set of cutting instructions for this material // for each set of cutting instructions for this material
matCutConfig.forEach(({ cut, identical, bias, ignoreOnFold }, i) => { matCutConfig.forEach((instruction, i) => {
// get the grain angle for the part for this set of instructions
const grainSpec = partCutlist.grain ? partCutlist.grain + (bias ? 45 : 0) : undefined
// for each piece that should be cut // for each piece that should be cut
for (let c = 0; c < cut; c++) { for (let c = 0; c < instruction.cut; c++) {
const dupPartName = `cut.${pattern.activePart}.${material}_${c + i + 1}` const dupPartName = `cut.${pattern.activePart}.${material}_${c + i + 1}`
// make a new part that will follow these cutting instructions // make a new part that will follow these cutting instructions
pattern.addPart({ pattern.addPart({
name: dupPartName, name: dupPartName,
from: activePartConfig, from: activePartConfig,
draft: ({ part, macro, store, points }) => { draft: ({ part, macro, store, points, utils }) => {
// the amount to rotate is the difference between this part's grain angle (as drafted) and the fabric's grain angle part.attributes.remove('transform')
let toRotate = grainSpec === undefined ? 0 : grainAngle - grainSpec
// don't over rotate
toRotate = toRotate % 180
if (toRotate < 0) toRotate += 180
// handle fold and grain for these cutting instructions
const titleSo = store.get(['title', part.name])
// if they shouldn't be identical, flip every other piece // if they shouldn't be identical, flip every other piece
if (!identical && c % 2 === 1) macro('flip') const flipped = !instruction.identical && c % 2 === 1
if (flipped) {
part.attributes.add(
'transform',
grainAngle === 90 ? 'scale(-1, 1)' : 'scale(1, -1)'
)
}
macro('handleFoldAndGrain', { macro('handleFoldAndGrain', {
partCutlist, partCutlist,
toRotate, instruction,
ignoreOnFold, flipped,
grainSpec,
bias,
}) })
if (titleSo) { // combine the transforms
const newTitleSo = { const combinedTransform = utils.combineTransforms(
...titleSo, part.attributes.getAsArray('transform')
at: points[`_${titleSo.prefix || ''}_titleNr`], )
append: false, part.attributes.set('transform', combinedTransform)
}
if (titleSo.rotation !== undefined)
newTitleSo.rotation = titleSo.rotation - toRotate
macro('title', newTitleSo)
}
return part return part
}, },
}) })
@ -106,18 +95,17 @@ export const cutLayoutPlugin = function (material, grainAngle) {
}, },
}, },
macros: { macros: {
...pluginFlip.macros,
...pluginMirror.macros, ...pluginMirror.macros,
// handle mirroring on the fold and rotating to sit along the grain or bias // handle mirroring on the fold and rotating to sit along the grain or bias
handleFoldAndGrain: ( handleFoldAndGrain: ({ partCutlist, instruction, flipped }, { points, macro }) => {
{ partCutlist, toRotate, grainSpec, ignoreOnFold, bias }, // get the grain angle for the part for this set of instructions
{ points, macro } const grainSpec = partCutlist.grain
) => { ? partCutlist.grain + (instruction.bias ? 45 : 0)
// if there's a grain angle, rotate the part to be along it : undefined
// if the part has cutonfold instructions // if the part has cutonfold instructions
if (partCutlist.cutOnFold) { if (partCutlist.cutOnFold) {
// if we're not meant to igore those instructions, mirror on the fold // if we're not meant to igore those instructions, mirror on the fold
if (!ignoreOnFold) macro('mirrorOnFold', { fold: partCutlist.cutOnFold }) if (!instruction.ignoreOnFold) macro('mirrorOnFold', { fold: partCutlist.cutOnFold })
// if we are meant to ignore those instructions, but there's a grainline // if we are meant to ignore those instructions, but there's a grainline
else if (grainSpec !== undefined) { else if (grainSpec !== undefined) {
// replace the cutonfold with a grainline // replace the cutonfold with a grainline
@ -126,7 +114,8 @@ export const cutLayoutPlugin = function (material, grainAngle) {
} }
} }
macro('rotateToGrain', { toRotate, bias }) // if there's a grain angle, rotate the part to be along it
macro('rotateToGrain', { bias: instruction.bias, flipped, grainSpec })
}, },
// mirror the part across the line indicated by cutonfold // mirror the part across the line indicated by cutonfold
mirrorOnFold: ({ fold }, { paths, snippets, utils, macro, points }) => { mirrorOnFold: ({ fold }, { paths, snippets, utils, macro, points }) => {
@ -134,7 +123,7 @@ export const cutLayoutPlugin = function (material, grainAngle) {
const mirrorPaths = [] const mirrorPaths = []
for (const p in paths) { for (const p in paths) {
// skip ones that are hidden // skip ones that are hidden
if (!paths[p].hidden && !p.match(avoidRegx)) mirrorPaths.push(paths[p]) if (!paths[p].hidden && !p.match(avoidRegx)) mirrorPaths.push(p)
} }
// store all the points to mirror // store all the points to mirror
@ -169,7 +158,6 @@ export const cutLayoutPlugin = function (material, grainAngle) {
}, },
}) })
console.log(mirrorPoints, snippetsByType, points)
// sprinkle the snippets // sprinkle the snippets
for (var def in snippetsByType) { for (var def in snippetsByType) {
macro('sprinkle', { macro('sprinkle', {
@ -183,63 +171,25 @@ export const cutLayoutPlugin = function (material, grainAngle) {
* if the part should be on the bias, this rotates the part to lie on the bias * if the part should be on the bias, this rotates the part to lie on the bias
* while keeping the grainline annotation along the grain * while keeping the grainline annotation along the grain
*/ */
rotateToGrain: ( rotateToGrain: ({ bias, flipped, grainSpec }, { part, paths, points }) => {
{ toRotate, bias }, // the amount to rotate is the difference between this part's grain angle (as drafted) and the fabric's grain angle
{ paths, snippets, Point, points, part, store, macro } let toRotate = grainSpec === undefined ? 0 : grainAngle + grainSpec
) => { // don't over rotate
toRotate = toRotate % 180
if (toRotate < 0) toRotate += 180
// if there's no difference, don't rotate // if there's no difference, don't rotate
if (toRotate === 0) return if (toRotate === 0) return
// we'll pivot rotations along the grainline to point, with a fallback
const pivot = points.grainlineTo || new Point(0, 0)
// go through all the paths if (paths.grainline && bias) {
for (const pathName in paths) { const pivot = points.grainlineFrom || new Point(0, 0)
const path = paths[pathName] paths.grainline.ops.forEach((op) => {
// don't rotate hidden paths
if (paths[pathName].hidden) continue
// we want the grainline indicator to always go in the fabric grain direction
// so if this part is on the bias and this path is the grainline indicator
// we'll rotate it 45 degrees less than necessary
let thisRotation = toRotate
if (pathName === 'grainline' && bias) thisRotation -= 45
// replace all the points in all the ops of this path with ones that have been rotated
path.ops.forEach((op) => {
opTypes.forEach((t) => { opTypes.forEach((t) => {
if (op[t]) op[t] = op[t].rotate(thisRotation, pivot) if (op[t]) op[t] = op[t].rotate(45, pivot)
}) })
}) })
} }
// replace all snippet anchors with ones that have been rotated part.attributes.add('transform', `rotate(${toRotate})`)
for (const snippetName in snippets) {
snippets[snippetName].anchor = snippets[snippetName].anchor.rotate(toRotate, pivot)
}
for (const pointName in points) {
const point = points[pointName]
const pointAttrs = point.attributes.clone()
points[pointName] = point.rotate(toRotate, pivot)
points[pointName].attributes = pointAttrs
let textTransform = pointAttrs.get('data-text-transform')
const textTransformRotation = textTransform ? textTransform.match(/rotate\(\s*\d+/) : null
let textRotationAmt = -toRotate
if (textTransformRotation) {
const textRotation = textTransformRotation[0]
textRotationAmt = parseFloat(textRotation.replace(/rotate\(\s*/, ''))
}
let newTransform = `rotate(${textRotationAmt} ${points[pointName].x} ${points[pointName].y})`
if (textTransform)
newTransform = textTransform.replace(/rotate\((\s*\d+\.*\d*,*){3}\)/, newTransform)
points[pointName].attr('data-text-transform', newTransform, true)
}
}, },
}, },
} }