1
0
Fork 0

chore(plugin-annotations): Refactor plugin structure

This commit is contained in:
joostdecock 2023-03-11 19:01:40 +01:00
parent 607bc010da
commit 94d8ae4cab
11 changed files with 611 additions and 706 deletions

View file

@ -1,5 +1,3 @@
import { version, name } from '../data.mjs'
// Method that draws the actual bartack // Method that draws the actual bartack
const drawBartack = (pointList, { Path }) => { const drawBartack = (pointList, { Path }) => {
let path = new Path().move(pointList.path1[0]) let path = new Path().move(pointList.path1[0])
@ -102,33 +100,25 @@ function createBartack(so, props) {
return true return true
} }
// The actual plugin // Export macros
export const bartack = { export const bartackMacros = {
name, bartack: function (so, props) {
version, return createBartack(so, props)
macros: { },
bartack: function (so, props) { bartackAlong: function (so, props) {
return createBartack(so, props) so.bartackFractionAlong = false
}, so.bartackAlong = true
bartackAlong: function (so, props) { so.anchor = false
so.bartackFractionAlong = false so.from = false
so.bartackAlong = true so.to = false
so.anchor = false return createBartack(so, props)
so.from = false },
so.to = false bartackFractionAlong: function (so, props) {
return createBartack(so, props) so.bartackFractionAlong = true
}, so.bartackAlong = false
bartackFractionAlong: function (so, props) { so.anchor = false
so.bartackFractionAlong = true so.from = false
so.bartackAlong = false so.to = false
so.anchor = false return createBartack(so, props)
so.from = false
so.to = false
return createBartack(so, props)
},
}, },
} }
// More specifically named exports
export const bartackPlugin = bartack
export const pluginBartack = bartack

View file

@ -1,5 +1,3 @@
import { name, version } from '../data.mjs'
const defs = [ const defs = [
// button // button
` `
@ -65,18 +63,13 @@ const defs = [
</g>`, </g>`,
] ]
export const buttons = { // Export hooks
name, export const buttonsHooks = {
version, preRender: [
hooks: { function (svg) {
preRender: function (svg) {
for (const def of defs) { for (const def of defs) {
if (svg.defs.indexOf(def) === -1) svg.defs += def if (svg.defs.indexOf(def) === -1) svg.defs += def
} }
}, },
}, ],
} }
// More specifically named exports
export const buttonsPlugin = buttons
export const pluginButtons = buttons

View file

@ -1,62 +1,53 @@
import { name, version } from '../data.mjs' // Export macros
export const crossboxMacros = {
crossbox: function (so, { points, Point, paths, Path, getId }) {
let id = getId()
let shiftFraction = 0.1
points[id + '_boxTopLeft'] = so.from.copy()
points[id + '_boxBottomRight'] = so.to.copy()
points[id + '_boxTopRight'] = new Point(so.to.x, so.from.y)
points[id + '_boxBottomLeft'] = new Point(so.from.x, so.to.y)
export const crossbox = { points[id + '_topCrossTL'] = points[id + '_boxTopLeft'].shiftFractionTowards(
name: 'crossbox', points[id + '_boxBottomRight'],
version, shiftFraction
macros: { )
crossbox: function (so, { points, Point, paths, Path, getId }) { points[id + '_topCrossTR'] = points[id + '_boxTopRight'].shiftFractionTowards(
let id = getId() points[id + '_boxBottomLeft'],
let shiftFraction = 0.1 shiftFraction
points[id + '_boxTopLeft'] = so.from.copy() )
points[id + '_boxBottomRight'] = so.to.copy() points[id + '_topCrossBL'] = points[id + '_boxBottomLeft'].shiftFractionTowards(
points[id + '_boxTopRight'] = new Point(so.to.x, so.from.y) points[id + '_boxTopRight'],
points[id + '_boxBottomLeft'] = new Point(so.from.x, so.to.y) shiftFraction
)
points[id + '_topCrossBR'] = points[id + '_boxBottomRight'].shiftFractionTowards(
points[id + '_boxTopLeft'],
shiftFraction
)
points[id + '_topCrossTL'] = points[id + '_boxTopLeft'].shiftFractionTowards( paths[id + 'crossBox'] = new Path()
points[id + '_boxBottomRight'], .move(points[id + '_boxTopLeft'])
shiftFraction .line(points[id + '_boxTopRight'])
) .line(points[id + '_boxBottomRight'])
points[id + '_topCrossTR'] = points[id + '_boxTopRight'].shiftFractionTowards( .line(points[id + '_boxBottomLeft'])
points[id + '_boxBottomLeft'], .line(points[id + '_boxTopLeft'])
shiftFraction .close()
) .attr('class', 'lining dotted stroke-sm')
points[id + '_topCrossBL'] = points[id + '_boxBottomLeft'].shiftFractionTowards( paths[id + '_topCross'] = new Path()
points[id + '_boxTopRight'], .move(points[id + '_topCrossTL'])
shiftFraction .line(points[id + '_topCrossBR'])
) .line(points[id + '_topCrossTR'])
points[id + '_topCrossBR'] = points[id + '_boxBottomRight'].shiftFractionTowards( .line(points[id + '_topCrossBL'])
points[id + '_boxTopLeft'], .line(points[id + '_topCrossTL'])
shiftFraction .line(points[id + '_topCrossTR'])
) .move(points[id + '_topCrossBR'])
.line(points[id + '_topCrossBL'])
paths[id + 'crossBox'] = new Path() .attr('class', 'lining dotted stroke-sm')
.move(points[id + '_boxTopLeft']) if (typeof so.text === 'string') {
.line(points[id + '_boxTopRight']) points.textAnchor = points[id + '_boxTopLeft']
.line(points[id + '_boxBottomRight']) .shiftFractionTowards(points[id + '_boxBottomRight'], 0.5)
.line(points[id + '_boxBottomLeft']) .attr('data-text', so.text)
.line(points[id + '_boxTopLeft']) .attr('data-text-class', 'center')
.close() }
.attr('class', 'lining dotted stroke-sm')
paths[id + '_topCross'] = new Path()
.move(points[id + '_topCrossTL'])
.line(points[id + '_topCrossBR'])
.line(points[id + '_topCrossTR'])
.line(points[id + '_topCrossBL'])
.line(points[id + '_topCrossTL'])
.line(points[id + '_topCrossTR'])
.move(points[id + '_topCrossBR'])
.line(points[id + '_topCrossBL'])
.attr('class', 'lining dotted stroke-sm')
if (typeof so.text === 'string') {
points.textAnchor = points[id + '_boxTopLeft']
.shiftFractionTowards(points[id + '_boxBottomRight'], 0.5)
.attr('data-text', so.text)
.attr('data-text-class', 'center')
}
},
}, },
} }
// More specifically named exports
export const crossboxPlugin = crossbox
export const pluginCrossbox = crossbox

View file

@ -1,5 +1,3 @@
import { name, version } from '../data.mjs'
const markers = ` const markers = `
<marker orient="auto" refY="4.0" refX="0.0" id="cutonfoldFrom" style="overflow:visible;" markerWidth="12" markerHeight="8"> <marker orient="auto" refY="4.0" refX="0.0" id="cutonfoldFrom" style="overflow:visible;" markerWidth="12" markerHeight="8">
<path class="note fill-note" d="M 0,4 L 12,0 C 10,2 10,6 12,8 z" /> <path class="note fill-note" d="M 0,4 L 12,0 C 10,2 10,6 12,8 z" />
@ -9,63 +7,59 @@ const markers = `
</marker> </marker>
` `
export const cutonfold = { // Export hooks
name, export const cutonfoldHooks = {
version, preRender: [
hooks: { function (svg) {
preRender: (svg) => {
if (svg.defs.indexOf(markers) === -1) svg.defs += markers if (svg.defs.indexOf(markers) === -1) svg.defs += markers
}, },
}, ],
macros: { }
cutonfold: function (so, { points, paths, Path, complete, setCutOnFold, setGrain, scale }) { // Export macros
if (so === false) { export const cutonfoldMacros = {
delete points.cutonfoldFrom cutonfold: function (so, { points, paths, Path, complete, setCutOnFold, setGrain, scale }) {
delete points.cutonfoldTo if (so === false) {
delete points.cutonfoldVia1 delete points.cutonfoldFrom
delete points.cutonfoldVia2 delete points.cutonfoldTo
delete paths.cutonfold delete points.cutonfoldVia1
// setCutOnFold relies on plugin-cutlist delete points.cutonfoldVia2
if (typeof setCutOnFold === 'function') { delete paths.cutonfold
setCutOnFold(false) // Restore default // setCutOnFold relies on plugin-cutlist
}
return true
}
so = {
offset: 15,
margin: 5,
prefix: 'cutonfold',
...so,
}
if (typeof setCutOnFold === 'function') { if (typeof setCutOnFold === 'function') {
setCutOnFold(so.from, so.to) setCutOnFold(false) // Restore default
if (so.grainline) setGrain(so.from.angle(so.to))
} }
if (complete) { return true
points[so.prefix + 'From'] = so.from.shiftFractionTowards(so.to, so.margin / 100) }
points[so.prefix + 'To'] = so.to.shiftFractionTowards(so.from, so.margin / 100) so = {
points[so.prefix + 'Via1'] = points[so.prefix + 'From'] offset: 15,
.shiftTowards(so.from, so.offset * scale) margin: 5,
.rotate(-90, points[so.prefix + 'From']) prefix: 'cutonfold',
points[so.prefix + 'Via2'] = points[so.prefix + 'To'] ...so,
.shiftTowards(so.to, so.offset * scale) }
.rotate(90, points[so.prefix + 'To']) if (typeof setCutOnFold === 'function') {
const text = so.grainline ? 'cutOnFoldAndGrainline' : 'cutOnFold' setCutOnFold(so.from, so.to)
paths[so.prefix + 'Cutonfold'] = new Path() if (so.grainline) setGrain(so.from.angle(so.to))
.move(points[so.prefix + 'From']) }
.line(points[so.prefix + 'Via1']) if (complete) {
.line(points[so.prefix + 'Via2']) points[so.prefix + 'From'] = so.from.shiftFractionTowards(so.to, so.margin / 100)
.line(points[so.prefix + 'To']) points[so.prefix + 'To'] = so.to.shiftFractionTowards(so.from, so.margin / 100)
.attr('class', 'note') points[so.prefix + 'Via1'] = points[so.prefix + 'From']
.attr('marker-start', 'url(#cutonfoldFrom)') .shiftTowards(so.from, so.offset * scale)
.attr('marker-end', 'url(#cutonfoldTo)') .rotate(-90, points[so.prefix + 'From'])
.attr('data-text', text) points[so.prefix + 'Via2'] = points[so.prefix + 'To']
.attr('data-text-class', 'center fill-note') .shiftTowards(so.to, so.offset * scale)
} .rotate(90, points[so.prefix + 'To'])
}, const text = so.grainline ? 'cutOnFoldAndGrainline' : 'cutOnFold'
paths[so.prefix + 'Cutonfold'] = new Path()
.move(points[so.prefix + 'From'])
.line(points[so.prefix + 'Via1'])
.line(points[so.prefix + 'Via2'])
.line(points[so.prefix + 'To'])
.attr('class', 'note')
.attr('marker-start', 'url(#cutonfoldFrom)')
.attr('marker-end', 'url(#cutonfoldTo)')
.attr('data-text', text)
.attr('data-text-class', 'center fill-note')
}
}, },
} }
// More specifically named exports
export const cutonfoldPlugin = cutonfold
export const pluginCutonfold = cutonfold

View file

@ -1,5 +1,3 @@
import { name, version } from '../data.mjs'
const markers = ` const markers = `
<marker orient="auto" refY="4.0" refX="10.0" id="grainlineFrom" style="overflow:visible;" markerWidth="12" markerHeight="8"> <marker orient="auto" refY="4.0" refX="10.0" id="grainlineFrom" style="overflow:visible;" markerWidth="12" markerHeight="8">
<path class="note fill-note" d="M 0,4 L 12,0 C 10,2 10,6 12,8 z" /> <path class="note fill-note" d="M 0,4 L 12,0 C 10,2 10,6 12,8 z" />
@ -10,47 +8,42 @@ const markers = `
const dflts = { text: 'grainline' } const dflts = { text: 'grainline' }
export const grainline = { // Export hooks and macros
name, export const grainlineHooks = {
version, preRender: [
hooks: { function (svg) {
preRender: (svg) => {
if (svg.defs.indexOf(markers) === -1) svg.defs += markers if (svg.defs.indexOf(markers) === -1) svg.defs += markers
}, },
}, ],
macros: { }
grainline: function (so = {}, { points, paths, Path, complete, setGrain }) { export const grainlineMacros = {
if (so === false) { grainline: function (so = {}, { points, paths, Path, complete, setGrain }) {
delete points.grainlineFrom if (so === false) {
delete points.grainlineTo delete points.grainlineFrom
delete paths.grainline delete points.grainlineTo
setGrain(90) // Restoring default delete paths.grainline
return true setGrain(90) // Restoring default
} return true
so = { }
...dflts, so = {
...so, ...dflts,
} ...so,
// setGrain relies on plugin-cutlist }
if (typeof setGrain === 'function') { // setGrain relies on plugin-cutlist
setGrain(so.from.angle(so.to)) if (typeof setGrain === 'function') {
} setGrain(so.from.angle(so.to))
if (complete) { }
points.grainlineFrom = so.from.shiftFractionTowards(so.to, 0.05) if (complete) {
points.grainlineTo = so.to.shiftFractionTowards(so.from, 0.05) points.grainlineFrom = so.from.shiftFractionTowards(so.to, 0.05)
paths.grainline = new Path() points.grainlineTo = so.to.shiftFractionTowards(so.from, 0.05)
.move(points.grainlineFrom) paths.grainline = new Path()
.line(points.grainlineTo) .move(points.grainlineFrom)
.attr('class', 'note') .line(points.grainlineTo)
.attr('marker-start', 'url(#grainlineFrom)') .attr('class', 'note')
.attr('marker-end', 'url(#grainlineTo)') .attr('marker-start', 'url(#grainlineFrom)')
.attr('data-text', so.text) .attr('marker-end', 'url(#grainlineTo)')
.attr('data-text-class', 'center fill-note') .attr('data-text', so.text)
} .attr('data-text-class', 'center fill-note')
}, }
}, },
} }
// More specifically named exports
export const grainlinePlugin = grainline
export const pluginGrainline = grainline

View file

@ -1,59 +1,41 @@
import { name, version } from '../data.mjs' import { name, version } from '../data.mjs'
// Hooks only
import { bartackPlugin } from './bartack.mjs' import { buttonsHooks } from './buttons.mjs'
import { buttonsPlugin } from './buttons.mjs' import { logoHooks } from './logo.mjs'
import { crossboxPlugin } from './crossbox.mjs' import { notchesHooks } from './notches.mjs'
import { cutonfoldPlugin } from './cutonfold.mjs' // Macros only
import { grainlinePlugin } from './grainline.mjs' import { bartackMacros } from './bartack.mjs'
import { logoPlugin } from './logo.mjs' import { crossboxMacros } from './crossbox.mjs'
import { notchesPlugin } from './notches.mjs' import { scaleboxMacros } from './scalebox.mjs'
import { pleatPlugin } from './pleat.mjs' // Hooks and Macros
import { scaleboxPlugin } from './scalebox.mjs' import { cutonfoldMacros, cutonfoldHooks } from './cutonfold.mjs'
import { sewtogetherPlugin } from './sewtogether.mjs' import { grainlineMacros, grainlineHooks } from './grainline.mjs'
import { pleatMacros, pleatHooks } from './pleat.mjs'
const annotationPlugins = [ import { sewtogetherMacros, sewtogetherHooks } from './sewtogether.mjs'
bartackPlugin,
buttonsPlugin,
crossboxPlugin,
cutonfoldPlugin,
grainlinePlugin,
logoPlugin,
notchesPlugin,
pleatPlugin,
scaleboxPlugin,
sewtogetherPlugin,
]
function annotationHooks() {
const hooks = {}
for (const plugin of annotationPlugins) {
for (const i in plugin.hooks) {
if (typeof hooks[i] === 'undefined') hooks[i] = []
const hook = plugin.hooks[i]
if (typeof hook === 'function') hooks[i].push(hook)
else if (typeof hook === 'object') {
for (let method of hook) hooks[i].push(method)
}
}
}
return hooks
}
function annotationMacros() {
const macros = {}
for (const plugin of annotationPlugins) {
for (const i in plugin.macros) macros[i] = plugin.macros[i]
}
return macros
}
export const plugin = { export const plugin = {
name, name,
version, version,
hooks: annotationHooks(), hooks: {
macros: annotationMacros(), preRender: [
...buttonsHooks.preRender,
...logoHooks.preRender,
...notchesHooks.preRender,
...cutonfoldHooks.preRender,
...grainlineHooks.preRender,
...pleatHooks.preRender,
...sewtogetherHooks.preRender,
],
},
macros: {
...bartackMacros,
...crossboxMacros,
...scaleboxMacros,
...cutonfoldMacros,
...grainlineMacros,
...pleatMacros,
...sewtogetherMacros,
},
} }
export const annotationPlugin = plugin export const annotationPlugin = plugin

File diff suppressed because one or more lines are too long

View file

@ -1,5 +1,3 @@
import { name, version } from '../data.mjs'
const markers = ` const markers = `
<g id="notch"> <g id="notch">
<circle cy="0" cx="0" r="1.4" class="fill-note" /> <circle cy="0" cx="0" r="1.4" class="fill-note" />
@ -10,16 +8,11 @@ const markers = `
<circle cy="0" cx="0" r="2.8" class="note" /> <circle cy="0" cx="0" r="2.8" class="note" />
</g>` </g>`
export const notches = { // Export hooks
name, export const notchesHooks = {
version, preRender: [
hooks: { function (svg) {
preRender: function (svg) {
if (svg.defs.indexOf(`id="notch"`) === -1) svg.defs += markers if (svg.defs.indexOf(`id="notch"`) === -1) svg.defs += markers
}, },
}, ],
} }
// More specifically named exports
export const notchesPlugin = notches
export const pluginNotches = notches

View file

@ -1,76 +1,71 @@
import { name, version } from '../data.mjs'
const markers = ` const markers = `
<marker id="pleatTo" markerWidth="12" markerHeight="8" orient="auto" refY="4" refX="12"> <marker id="pleatTo" markerWidth="12" markerHeight="8" orient="auto" refY="4" refX="12">
<path class="note fill-note" d="M 12,4 L 0,0 C 2,2 2,6 0,8 z" /> <path class="note fill-note" d="M 12,4 L 0,0 C 2,2 2,6 0,8 z" />
</marker> </marker>
` `
export const pleat = { // Export hooks
name, export const pleatHooks = {
version, preRender: [
hooks: { function (svg) {
preRender: (svg) => {
if (svg.defs.indexOf(markers) === -1) svg.defs += markers if (svg.defs.indexOf(markers) === -1) svg.defs += markers
}, },
}, ],
macros: {
pleat: function (so, { points, paths, Path, complete, scale }) {
if (so === false) {
delete points.pleatFrom
delete points.pleatFromIn
delete points.pleatTo
delete points.pleatToIn
delete paths.pleatTo
delete paths.pleatFrom
delete paths.pleatArrow
return true
}
so = {
margin: 35,
prefix: 'pleat',
reverse: false,
...so,
}
if (complete) {
points[so.prefix + 'From'] = so.from
points[so.prefix + 'To'] = so.to
points[so.prefix + 'FromIn'] = points[so.prefix + 'From'].shift(
so.from.shiftTowards(so.to, 0.1).angle(so.from) + 270,
so.margin * scale
)
points[so.prefix + 'ToIn'] = points[so.prefix + 'To'].shift(
so.from.shiftTowards(so.to, 0.1).angle(so.to) + 90,
so.margin * scale
)
paths[so.prefix + 'PleatFrom'] = new Path()
.move(points[so.prefix + 'From'])
.line(points[so.prefix + 'FromIn'])
.attr('class', 'note' + (so.reverse ? ' dashed' : ''))
paths[so.prefix + 'PleatTo'] = new Path()
.move(points[so.prefix + 'To'])
.line(points[so.prefix + 'ToIn'])
.attr('class', 'note' + (so.reverse ? '' : ' dashed'))
paths[so.prefix + 'PleatArrow'] = new Path()
.move(
points[so.prefix + (so.reverse ? 'To' : 'From')].shiftFractionTowards(
points[so.prefix + (so.reverse ? 'ToIn' : 'FromIn')],
0.25
)
)
.line(
points[so.prefix + (so.reverse ? 'From' : 'To')].shiftFractionTowards(
points[so.prefix + (so.reverse ? 'FromIn' : 'ToIn')],
0.25
)
)
.attr('class', 'note')
.attr('marker-end', 'url(#pleatTo)')
}
},
},
} }
// More specifically named exports // Export macros
export const pleatPlugin = pleat export const pleatMacros = {
export const pluginPleat = pleat pleat: function (so, { points, paths, Path, complete, scale }) {
if (so === false) {
delete points.pleatFrom
delete points.pleatFromIn
delete points.pleatTo
delete points.pleatToIn
delete paths.pleatTo
delete paths.pleatFrom
delete paths.pleatArrow
return true
}
so = {
margin: 35,
prefix: 'pleat',
reverse: false,
...so,
}
if (complete) {
points[so.prefix + 'From'] = so.from
points[so.prefix + 'To'] = so.to
points[so.prefix + 'FromIn'] = points[so.prefix + 'From'].shift(
so.from.shiftTowards(so.to, 0.1).angle(so.from) + 270,
so.margin * scale
)
points[so.prefix + 'ToIn'] = points[so.prefix + 'To'].shift(
so.from.shiftTowards(so.to, 0.1).angle(so.to) + 90,
so.margin * scale
)
paths[so.prefix + 'PleatFrom'] = new Path()
.move(points[so.prefix + 'From'])
.line(points[so.prefix + 'FromIn'])
.attr('class', 'note' + (so.reverse ? ' dashed' : ''))
paths[so.prefix + 'PleatTo'] = new Path()
.move(points[so.prefix + 'To'])
.line(points[so.prefix + 'ToIn'])
.attr('class', 'note' + (so.reverse ? '' : ' dashed'))
paths[so.prefix + 'PleatArrow'] = new Path()
.move(
points[so.prefix + (so.reverse ? 'To' : 'From')].shiftFractionTowards(
points[so.prefix + (so.reverse ? 'ToIn' : 'FromIn')],
0.25
)
)
.line(
points[so.prefix + (so.reverse ? 'From' : 'To')].shiftFractionTowards(
points[so.prefix + (so.reverse ? 'FromIn' : 'ToIn')],
0.25
)
)
.attr('class', 'note')
.attr('marker-end', 'url(#pleatTo)')
}
},
}

View file

@ -1,297 +1,282 @@
import { name, version } from '../data.mjs' // Export macros
export const scaleboxMacros = {
scalebox: function (so, { store, points, paths, scale, Point, Path }) {
// Passing `false` will remove the scalebox
if (so === false) {
for (let id of [
'__scaleboxMetricTopLeft',
'__scaleboxMetricTopRight',
'__scaleboxMetricBottomRight',
'__scaleboxMetricBottomLeft',
'__scaleboxImperialTopLeft',
'__scaleboxImperialTopRight',
'__scaleboxImperialBottomRight',
'__scaleboxImperialBottomLeft',
'__scaleboxLead',
'__scaleboxTitle',
'__scaleboxText',
'__scaleboxLink',
'__scaleboxMetric',
'__scaleboxImperial',
])
delete points[id]
for (let id of ['__scaleboxMetric', '__scaleboxImperial']) delete paths[id]
return true
}
const markers = ` // Convert scale to a value between 0 and 9, inclusive.
<marker id="pleatTo" markerWidth="12" markerHeight="8" orient="auto" refY="4" refX="12"> const scaleIndex = Math.round(10 * Math.max(0.1, Math.min(1, scale))) - 1
<path class="note fill-note" d="M 12,4 L 0,0 C 2,2 2,6 0,8 z" />
</marker>
`
export const scalebox = { // Metric width and height in mm and display width and height for each scale index.
name, const metricSizes = [
version, [10, 5, '1cm', '0.5cm'],
macros: { [20, 10, '2cm', '1cm'],
scalebox: function (so, { store, points, paths, scale, Point, Path }) { [30, 15, '3cm', '1.5cm'],
// Passing `false` will remove the scalebox [40, 20, '4cm', '2cm'],
if (so === false) { [50, 25, '5cm', '2.5cm'],
for (let id of [ [60, 30, '6cm', '3cm'],
'__scaleboxMetricTopLeft', [70, 35, '7cm', '3.5cm'],
'__scaleboxMetricTopRight', [80, 40, '8cm', '4cm'],
'__scaleboxMetricBottomRight', [90, 45, '9cm', '4.5cm'],
'__scaleboxMetricBottomLeft', [100, 50, '10cm', '5cm'],
'__scaleboxImperialTopLeft', ]
'__scaleboxImperialTopRight',
'__scaleboxImperialBottomRight',
'__scaleboxImperialBottomLeft',
'__scaleboxLead',
'__scaleboxTitle',
'__scaleboxText',
'__scaleboxLink',
'__scaleboxMetric',
'__scaleboxImperial',
])
delete points[id]
for (let id of ['__scaleboxMetric', '__scaleboxImperial']) delete paths[id]
return true
}
// Convert scale to a value between 0 and 9, inclusive. const metricWidth = metricSizes[scaleIndex][0]
const scaleIndex = Math.round(10 * Math.max(0.1, Math.min(1, scale))) - 1 const metricHeight = metricSizes[scaleIndex][1]
const metricDisplayWidth = metricSizes[scaleIndex][2]
const metricDisplayHeight = metricSizes[scaleIndex][3]
// Metric width and height in mm and display width and height for each scale index. // Imperial width and height in mm and display width and heigth for each scale index.
const metricSizes = [ const imperialSizes = [
[10, 5, '1cm', '0.5cm'], [25.4 * 0.5, 25.4 * 0.25, '½″', '¼″'],
[20, 10, '2cm', '1cm'], [25.4 * 0.875, 25.4 * 0.5, '⅞″', '½″'],
[30, 15, '3cm', '1.5cm'], [25.4 * 1.25, 25.4 * 0.625, '1 ¼″', '⅝″'],
[40, 20, '4cm', '2cm'], [25.4 * 1.625, 25.4 * 0.875, '1 ⅝″', '⅞″'],
[50, 25, '5cm', '2.5cm'], [25.4 * 2, 25.4 * 1, '2″', '1″'],
[60, 30, '6cm', '3cm'], [25.4 * 2.375, 25.4 * 1.25, '2 ⅜″', '1 ¼″'],
[70, 35, '7cm', '3.5cm'], [25.4 * 2.875, 25.4 * 1.5, '2 ⅞″', '1 ½″'],
[80, 40, '8cm', '4cm'], [25.4 * 3.25, 25.4 * 1.625, '3 ¼″', '1 ⅝″'],
[90, 45, '9cm', '4.5cm'], [25.4 * 3.625, 25.4 * 1.875, '3 ⅝″', '1 ⅞″'],
[100, 50, '10cm', '5cm'], [25.4 * 4, 25.4 * 2, '4″', '2″'],
]
const imperialWidth = imperialSizes[scaleIndex][0]
const imperialHeight = imperialSizes[scaleIndex][1]
const imperialDisplayWidth = imperialSizes[scaleIndex][2]
const imperialDisplayHeight = imperialSizes[scaleIndex][3]
// Box points
points.__scaleboxMetricTopLeft = new Point(
so.at.x - metricWidth / 2,
so.at.y - metricHeight / 2
)
points.__scaleboxMetricTopRight = new Point(
so.at.x + metricWidth / 2,
so.at.y - metricHeight / 2
)
points.__scaleboxMetricBottomLeft = new Point(
so.at.x - metricWidth / 2,
so.at.y + metricHeight / 2
)
points.__scaleboxMetricBottomRight = new Point(
so.at.x + metricWidth / 2,
so.at.y + metricHeight / 2
)
points.__scaleboxImperialTopLeft = new Point(
so.at.x - imperialWidth / 2,
so.at.y - imperialHeight / 2
)
points.__scaleboxImperialTopRight = new Point(
so.at.x + imperialWidth / 2,
so.at.y - imperialHeight / 2
)
points.__scaleboxImperialBottomLeft = new Point(
so.at.x - imperialWidth / 2,
so.at.y + imperialHeight / 2
)
points.__scaleboxImperialBottomRight = new Point(
so.at.x + imperialWidth / 2,
so.at.y + imperialHeight / 2
)
// Text anchor points
points.__scaleboxLead = new Point(so.at.x - 45 * scale, so.at.y - 15 * scale)
points.__scaleboxTitle = points.__scaleboxLead.shift(-90, 10 * scale)
points.__scaleboxText = points.__scaleboxTitle.shift(-90, 12 * scale)
points.__scaleboxLink = points.__scaleboxText.shift(-90, 5 * scale)
points.__scaleboxMetric = new Point(so.at.x, so.at.y + 20 * scale)
points.__scaleboxImperial = new Point(so.at.x, so.at.y + 24 * scale)
// Rotation
if (so.rotate) {
so.rotate = Number(so.rotate)
let toRotate = [
'__scaleboxMetricTopLeft',
'__scaleboxMetricTopRight',
'__scaleboxMetricBottomLeft',
'__scaleboxMetricBottomRight',
'__scaleboxImperialTopLeft',
'__scaleboxImperialTopRight',
'__scaleboxImperialBottomLeft',
'__scaleboxImperialBottomRight',
'__scaleboxLead',
'__scaleboxTitle',
'__scaleboxText',
'__scaleboxLink',
'__scaleboxMetric',
'__scaleboxImperial',
] ]
for (let pid of toRotate) points[pid] = points[pid].rotate(so.rotate, so.at)
for (let pid of toRotate.slice(8)) {
points[pid].attributes.set(
'data-text-transform',
`rotate(${so.rotate * -1}, ${points[pid].x}, ${points[pid].y})`
)
}
}
// Paths
paths.__scaleboxImperial = new Path()
.attr('class', 'scalebox imperial fill-current')
.move(points.__scaleboxImperialTopLeft)
.line(points.__scaleboxImperialBottomLeft)
.line(points.__scaleboxImperialBottomRight)
.line(points.__scaleboxImperialTopRight)
.close()
paths.__scaleboxMetric = new Path()
.attr('class', 'scalebox metric fill-bg')
.move(points.__scaleboxMetricTopLeft)
.line(points.__scaleboxMetricBottomLeft)
.line(points.__scaleboxMetricBottomRight)
.line(points.__scaleboxMetricTopRight)
.close()
// Lead
points.__scaleboxLead = points.__scaleboxLead
.attr('data-text', so.lead || 'FreeSewing')
.attr('data-text-class', 'text-sm')
// Title
if (so.title) points.__scaleboxTitle.attributes.set('data-text', so.title)
else {
let name = store.data?.name || 'No Name'
if (name.indexOf('@freesewing/') !== -1) name = name.replace('@freesewing/', '')
points.__scaleboxTitle = points.__scaleboxTitle
.attr('data-text', name)
.attr('data-text', 'v' + (store.data?.version || 'No Version'))
}
points.__scaleboxTitle.attributes.add('data-text-class', 'text-lg')
// Text
if (typeof so.text === 'string') {
points.__scaleboxText.attr('data-text', so.text)
} else {
points.__scaleboxText.attr('data-text', 'supportFreesewingBecomeAPatron')
points.__scaleboxLink = points.__scaleboxLink
.attr('data-text', 'freesewing.org/patrons/join')
.attr('data-text-class', 'text-sm fill-note')
}
points.__scaleboxText.attr('data-text-class', 'text-xs').attr('data-text-lineheight', 4)
// Instructions
points.__scaleboxMetric = points.__scaleboxMetric
.attr('data-text', 'theWhiteInsideOfThisBoxShouldMeasure')
.attr('data-text', `${metricDisplayWidth}`)
.attr('data-text', 'x')
.attr('data-text', `${metricDisplayHeight}`)
.attr('data-text-class', 'text-xs center')
points.__scaleboxImperial = points.__scaleboxImperial
.attr('data-text', 'theBlackOutsideOfThisBoxShouldMeasure')
.attr('data-text', `${imperialDisplayWidth}`)
.attr('data-text', 'x')
.attr('data-text', `${imperialDisplayHeight}`)
.attr('data-text-class', 'text-xs center ')
},
miniscale(so, { store, points, paths, scale, Point, Path }) {
// Passing `false` will remove the miniscale
if (so === false) {
for (const id of [
'__miniscaleMetricTopLeft',
'__miniscaleMetricTopRight',
'__miniscaleMetricBottomRight',
'__miniscaleMetricBottomLeft',
'__miniscaleImperialTopLeft',
'__miniscaleImperialTopRight',
'__miniscaleImperialBottomRight',
'__miniscaleImperialBottomLeft',
'__miniscaleMetric',
'__miniscaleImperial',
])
delete points[id]
for (const id of ['__miniscaleMetric', '__miniscaleImperial']) delete paths[id]
return true
}
const metricWidth = metricSizes[scaleIndex][0] // Convert scale to a value between 0 and 5, inclusive.
const metricHeight = metricSizes[scaleIndex][1] const scaleIndex = Math.ceil(6 * Math.max(0.1, Math.min(1, scale))) - 1
const metricDisplayWidth = metricSizes[scaleIndex][2]
const metricDisplayHeight = metricSizes[scaleIndex][3]
// Imperial width and height in mm and display width and heigth for each scale index. // Metric size in mm / display value and imperial size in mm / display value for each scale index.
const imperialSizes = [ const sizes = [
[25.4 * 0.5, 25.4 * 0.25, '½″', '¼″'], [10, '1cm', 25.4 * 0.375, '⅜″'],
[25.4 * 0.875, 25.4 * 0.5, '⅞″', '½″'], [13, '1.3cm', 25.4 * 0.5, '½″'],
[25.4 * 1.25, 25.4 * 0.625, '1 ¼″', '⅝″'], [16, '1.6cm', 25.4 * 0.625, '⅝″'],
[25.4 * 1.625, 25.4 * 0.875, '1 ⅝″', '⅞″'], [19, '1.9cm', 25.4 * 0.75, '¾″'],
[25.4 * 2, 25.4 * 1, '2″', '1″'], [22, '2.2cm', 25.4 * 0.875, '⅞″'],
[25.4 * 2.375, 25.4 * 1.25, '2 ⅜″', '1 ¼″'], [25, '2.5cm', 25.4 * 1, '1″'],
[25.4 * 2.875, 25.4 * 1.5, '2 ⅞″', '1 ½″'], ]
[25.4 * 3.25, 25.4 * 1.625, '3 ¼″', '1 ⅝″'], const m = sizes[scaleIndex][0] / 2
[25.4 * 3.625, 25.4 * 1.875, '3 ⅝″', '1 ⅞″'], const i = sizes[scaleIndex][2] / 2
[25.4 * 4, 25.4 * 2, '4″', '2″'], const metricDisplaySize = sizes[scaleIndex][1]
const imperialDisplaySize = sizes[scaleIndex][3]
// Box points
points.__miniscaleMetricTopLeft = new Point(so.at.x - m, so.at.y - m)
points.__miniscaleMetricTopRight = new Point(so.at.x + m, so.at.y - m)
points.__miniscaleMetricBottomLeft = new Point(so.at.x - m, so.at.y + m)
points.__miniscaleMetricBottomRight = new Point(so.at.x + m, so.at.y + m)
points.__miniscaleImperialTopLeft = new Point(so.at.x - i, so.at.y - i)
points.__miniscaleImperialTopRight = new Point(so.at.x + i, so.at.y - i)
points.__miniscaleImperialBottomLeft = new Point(so.at.x - i, so.at.y + i)
points.__miniscaleImperialBottomRight = new Point(so.at.x + i, so.at.y + i)
// Text anchor points
points.__miniscaleMetric = new Point(so.at.x, so.at.y - 2 * scale)
points.__miniscaleImperial = new Point(so.at.x, so.at.y + 8 * scale)
// Rotation
if (so.rotate) {
so.rotate = Number(so.rotate)
let toRotate = [
'__miniscaleMetricTopLeft',
'__miniscaleMetricTopRight',
'__miniscaleMetricBottomLeft',
'__miniscaleMetricBottomRight',
'__miniscaleImperialTopLeft',
'__miniscaleImperialTopRight',
'__miniscaleImperialBottomLeft',
'__miniscaleImperialBottomRight',
'__miniscaleMetric',
'__miniscaleImperial',
] ]
for (const pid of toRotate) points[pid] = points[pid].rotate(so.rotate, so.at)
const imperialWidth = imperialSizes[scaleIndex][0] for (const pid of toRotate.slice(8)) {
const imperialHeight = imperialSizes[scaleIndex][1] points[pid].attributes.set(
const imperialDisplayWidth = imperialSizes[scaleIndex][2] 'data-text-transform',
const imperialDisplayHeight = imperialSizes[scaleIndex][3] `rotate(${so.rotate * -1}, ${points[pid].x}, ${points[pid].y})`
)
// Box points
points.__scaleboxMetricTopLeft = new Point(
so.at.x - metricWidth / 2,
so.at.y - metricHeight / 2
)
points.__scaleboxMetricTopRight = new Point(
so.at.x + metricWidth / 2,
so.at.y - metricHeight / 2
)
points.__scaleboxMetricBottomLeft = new Point(
so.at.x - metricWidth / 2,
so.at.y + metricHeight / 2
)
points.__scaleboxMetricBottomRight = new Point(
so.at.x + metricWidth / 2,
so.at.y + metricHeight / 2
)
points.__scaleboxImperialTopLeft = new Point(
so.at.x - imperialWidth / 2,
so.at.y - imperialHeight / 2
)
points.__scaleboxImperialTopRight = new Point(
so.at.x + imperialWidth / 2,
so.at.y - imperialHeight / 2
)
points.__scaleboxImperialBottomLeft = new Point(
so.at.x - imperialWidth / 2,
so.at.y + imperialHeight / 2
)
points.__scaleboxImperialBottomRight = new Point(
so.at.x + imperialWidth / 2,
so.at.y + imperialHeight / 2
)
// Text anchor points
points.__scaleboxLead = new Point(so.at.x - 45 * scale, so.at.y - 15 * scale)
points.__scaleboxTitle = points.__scaleboxLead.shift(-90, 10 * scale)
points.__scaleboxText = points.__scaleboxTitle.shift(-90, 12 * scale)
points.__scaleboxLink = points.__scaleboxText.shift(-90, 5 * scale)
points.__scaleboxMetric = new Point(so.at.x, so.at.y + 20 * scale)
points.__scaleboxImperial = new Point(so.at.x, so.at.y + 24 * scale)
// Rotation
if (so.rotate) {
so.rotate = Number(so.rotate)
let toRotate = [
'__scaleboxMetricTopLeft',
'__scaleboxMetricTopRight',
'__scaleboxMetricBottomLeft',
'__scaleboxMetricBottomRight',
'__scaleboxImperialTopLeft',
'__scaleboxImperialTopRight',
'__scaleboxImperialBottomLeft',
'__scaleboxImperialBottomRight',
'__scaleboxLead',
'__scaleboxTitle',
'__scaleboxText',
'__scaleboxLink',
'__scaleboxMetric',
'__scaleboxImperial',
]
for (let pid of toRotate) points[pid] = points[pid].rotate(so.rotate, so.at)
for (let pid of toRotate.slice(8)) {
points[pid].attributes.set(
'data-text-transform',
`rotate(${so.rotate * -1}, ${points[pid].x}, ${points[pid].y})`
)
}
} }
// Paths }
paths.__scaleboxImperial = new Path() // Paths
.attr('class', 'scalebox imperial fill-current') paths.__miniscaleImperial = new Path()
.move(points.__scaleboxImperialTopLeft) .attr('class', 'scalebox imperial fill-current')
.line(points.__scaleboxImperialBottomLeft) .move(points.__miniscaleImperialTopLeft)
.line(points.__scaleboxImperialBottomRight) .line(points.__miniscaleImperialBottomLeft)
.line(points.__scaleboxImperialTopRight) .line(points.__miniscaleImperialBottomRight)
.close() .line(points.__miniscaleImperialTopRight)
paths.__scaleboxMetric = new Path() .close()
.attr('class', 'scalebox metric fill-bg') paths.__miniscaleMetric = new Path()
.move(points.__scaleboxMetricTopLeft) .attr('class', 'scalebox metric fill-bg')
.line(points.__scaleboxMetricBottomLeft) .move(points.__miniscaleMetricTopLeft)
.line(points.__scaleboxMetricBottomRight) .line(points.__miniscaleMetricBottomLeft)
.line(points.__scaleboxMetricTopRight) .line(points.__miniscaleMetricBottomRight)
.close() .line(points.__miniscaleMetricTopRight)
// Lead .close()
points.__scaleboxLead = points.__scaleboxLead // Text
.attr('data-text', so.lead || 'FreeSewing') points.__miniscaleMetric = points.__miniscaleMetric
.attr('data-text-class', 'text-sm') .attr('data-text', `${metricDisplaySize} x ${metricDisplaySize}`)
// Title .attr('data-text-class', 'text-xs center')
if (so.title) points.__scaleboxTitle.attributes.set('data-text', so.title) points.__miniscaleImperial = points.__miniscaleImperial
else { .attr('data-text', `${imperialDisplaySize} x ${imperialDisplaySize}`)
let name = store.data?.name || 'No Name' .attr('data-text-class', 'text-xs center ')
if (name.indexOf('@freesewing/') !== -1) name = name.replace('@freesewing/', '')
points.__scaleboxTitle = points.__scaleboxTitle
.attr('data-text', name)
.attr('data-text', 'v' + (store.data?.version || 'No Version'))
}
points.__scaleboxTitle.attributes.add('data-text-class', 'text-lg')
// Text
if (typeof so.text === 'string') {
points.__scaleboxText.attr('data-text', so.text)
} else {
points.__scaleboxText.attr('data-text', 'supportFreesewingBecomeAPatron')
points.__scaleboxLink = points.__scaleboxLink
.attr('data-text', 'freesewing.org/patrons/join')
.attr('data-text-class', 'text-sm fill-note')
}
points.__scaleboxText.attr('data-text-class', 'text-xs').attr('data-text-lineheight', 4)
// Instructions
points.__scaleboxMetric = points.__scaleboxMetric
.attr('data-text', 'theWhiteInsideOfThisBoxShouldMeasure')
.attr('data-text', `${metricDisplayWidth}`)
.attr('data-text', 'x')
.attr('data-text', `${metricDisplayHeight}`)
.attr('data-text-class', 'text-xs center')
points.__scaleboxImperial = points.__scaleboxImperial
.attr('data-text', 'theBlackOutsideOfThisBoxShouldMeasure')
.attr('data-text', `${imperialDisplayWidth}`)
.attr('data-text', 'x')
.attr('data-text', `${imperialDisplayHeight}`)
.attr('data-text-class', 'text-xs center ')
},
miniscale(so, { store, points, paths, scale, Point, Path }) {
// Passing `false` will remove the miniscale
if (so === false) {
for (const id of [
'__miniscaleMetricTopLeft',
'__miniscaleMetricTopRight',
'__miniscaleMetricBottomRight',
'__miniscaleMetricBottomLeft',
'__miniscaleImperialTopLeft',
'__miniscaleImperialTopRight',
'__miniscaleImperialBottomRight',
'__miniscaleImperialBottomLeft',
'__miniscaleMetric',
'__miniscaleImperial',
])
delete points[id]
for (const id of ['__miniscaleMetric', '__miniscaleImperial']) delete paths[id]
return true
}
// Convert scale to a value between 0 and 5, inclusive.
const scaleIndex = Math.ceil(6 * Math.max(0.1, Math.min(1, scale))) - 1
// Metric size in mm / display value and imperial size in mm / display value for each scale index.
const sizes = [
[10, '1cm', 25.4 * 0.375, '⅜″'],
[13, '1.3cm', 25.4 * 0.5, '½″'],
[16, '1.6cm', 25.4 * 0.625, '⅝″'],
[19, '1.9cm', 25.4 * 0.75, '¾″'],
[22, '2.2cm', 25.4 * 0.875, '⅞″'],
[25, '2.5cm', 25.4 * 1, '1″'],
]
const m = sizes[scaleIndex][0] / 2
const i = sizes[scaleIndex][2] / 2
const metricDisplaySize = sizes[scaleIndex][1]
const imperialDisplaySize = sizes[scaleIndex][3]
// Box points
points.__miniscaleMetricTopLeft = new Point(so.at.x - m, so.at.y - m)
points.__miniscaleMetricTopRight = new Point(so.at.x + m, so.at.y - m)
points.__miniscaleMetricBottomLeft = new Point(so.at.x - m, so.at.y + m)
points.__miniscaleMetricBottomRight = new Point(so.at.x + m, so.at.y + m)
points.__miniscaleImperialTopLeft = new Point(so.at.x - i, so.at.y - i)
points.__miniscaleImperialTopRight = new Point(so.at.x + i, so.at.y - i)
points.__miniscaleImperialBottomLeft = new Point(so.at.x - i, so.at.y + i)
points.__miniscaleImperialBottomRight = new Point(so.at.x + i, so.at.y + i)
// Text anchor points
points.__miniscaleMetric = new Point(so.at.x, so.at.y - 2 * scale)
points.__miniscaleImperial = new Point(so.at.x, so.at.y + 8 * scale)
// Rotation
if (so.rotate) {
so.rotate = Number(so.rotate)
let toRotate = [
'__miniscaleMetricTopLeft',
'__miniscaleMetricTopRight',
'__miniscaleMetricBottomLeft',
'__miniscaleMetricBottomRight',
'__miniscaleImperialTopLeft',
'__miniscaleImperialTopRight',
'__miniscaleImperialBottomLeft',
'__miniscaleImperialBottomRight',
'__miniscaleMetric',
'__miniscaleImperial',
]
for (const pid of toRotate) points[pid] = points[pid].rotate(so.rotate, so.at)
for (const pid of toRotate.slice(8)) {
points[pid].attributes.set(
'data-text-transform',
`rotate(${so.rotate * -1}, ${points[pid].x}, ${points[pid].y})`
)
}
}
// Paths
paths.__miniscaleImperial = new Path()
.attr('class', 'scalebox imperial fill-current')
.move(points.__miniscaleImperialTopLeft)
.line(points.__miniscaleImperialBottomLeft)
.line(points.__miniscaleImperialBottomRight)
.line(points.__miniscaleImperialTopRight)
.close()
paths.__miniscaleMetric = new Path()
.attr('class', 'scalebox metric fill-bg')
.move(points.__miniscaleMetricTopLeft)
.line(points.__miniscaleMetricBottomLeft)
.line(points.__miniscaleMetricBottomRight)
.line(points.__miniscaleMetricTopRight)
.close()
// Text
points.__miniscaleMetric = points.__miniscaleMetric
.attr('data-text', `${metricDisplaySize} x ${metricDisplaySize}`)
.attr('data-text-class', 'text-xs center')
points.__miniscaleImperial = points.__miniscaleImperial
.attr('data-text', `${imperialDisplaySize} x ${imperialDisplaySize}`)
.attr('data-text-class', 'text-xs center ')
},
}, },
} }
// More specifically named exports
export const scaleboxPlugin = scalebox
export const pluginScalebox = scalebox

View file

@ -1,5 +1,3 @@
import { name, version } from '../data.mjs'
const markers = ` const markers = `
<marker id="sewTogetherStart" markerWidth="4" markerHeight="4" orient="auto" refX="0" refY="2"> <marker id="sewTogetherStart" markerWidth="4" markerHeight="4" orient="auto" refX="0" refY="2">
<path class="note stroke-sm" d="M4,4 L0,2 4,0" /> <path class="note stroke-sm" d="M4,4 L0,2 4,0" />
@ -12,83 +10,80 @@ const markers = `
</marker> </marker>
` `
export const sewtogether = { // Export hooks
name, export const sewtogetherHooks = {
version, preRender: [
hooks: { function (svg) {
preRender: (svg) => {
if (svg.defs.indexOf(markers) === -1) svg.defs += markers if (svg.defs.indexOf(markers) === -1) svg.defs += markers
}, },
}, ],
macros: {
sewtogether: function (so, { points, paths, Path, complete, scale, sa }) {
if (so === false) {
delete points.sewtogetherFrom
delete points.sewtogetherFromCp
delete points.sewtogetherMiddle
delete points.sewtogetherTo
delete points.sewtogetherHinge
delete points.sewtogetherToCp
delete paths.sewtogetherSewTogetherHinge
delete paths.sewtogetherSewTogether
return true
}
so = {
prefix: 'sewtogether',
...so,
}
if (complete) {
if (null == so.middle) {
so.middle = so.from.shiftFractionTowards(so.to, 0.5)
}
points[so.prefix + 'From'] = so.from
points[so.prefix + 'Middle'] = so.middle
points[so.prefix + 'To'] = so.to
points[so.prefix + 'FromCp'] = points[so.prefix + 'From'].shift(
points[so.prefix + 'From'].angle(points[so.prefix + 'Middle']) + 90,
points[so.prefix + 'From'].dist(points[so.prefix + 'Middle']) / 1.5
)
points[so.prefix + 'ToCp'] = points[so.prefix + 'To'].shift(
points[so.prefix + 'To'].angle(points[so.prefix + 'Middle']) - 90,
points[so.prefix + 'To'].dist(points[so.prefix + 'Middle']) / 1.5
)
if (so.hinge) {
points[so.prefix + 'Hinge'] = points[so.prefix + 'Middle'].shift(
points[so.prefix + 'Middle'].angle(points[so.prefix + 'To']) +
Math.abs(
points[so.prefix + 'Middle'].angle(points[so.prefix + 'From']) -
points[so.prefix + 'Middle'].angle(points[so.prefix + 'To'])
) /
2 +
(sa ? 180 : 0),
sa
? sa
: Math.min(
points[so.prefix + 'From'].dist(points[so.prefix + 'Middle']),
points[so.prefix + 'From'].dist(points[so.prefix + 'Middle'])
) / 4
)
paths[so.prefix + 'SewTogetherHinge'] = new Path()
.move(points[so.prefix + 'Middle'])
.line(points[so.prefix + 'Hinge'])
.attr('marker-start', 'url(#sewTogetherCross)')
.attr('class', 'dotted note stroke-sm')
}
paths[so.prefix + 'SewTogether'] = new Path()
.move(points[so.prefix + 'From'])
.curve(points[so.prefix + 'FromCp'], points[so.prefix + 'ToCp'], points[so.prefix + 'To'])
.attr('class', 'dotted note stroke-sm')
.attr('marker-start', 'url(#sewTogetherStart)')
.attr('marker-end', 'url(#sewTogetherEnd)')
.attr('data-text', 'sewTogether')
.attr('data-text-class', 'center fill-note text-xs')
}
},
},
} }
// More specifically named exports // Export macros
export const sewtogetherPlugin = sewtogether export const sewtogetherMacros = {
export const pluginSewtogether = sewtogether sewtogether: function (so, { points, paths, Path, complete, scale, sa }) {
if (so === false) {
delete points.sewtogetherFrom
delete points.sewtogetherFromCp
delete points.sewtogetherMiddle
delete points.sewtogetherTo
delete points.sewtogetherHinge
delete points.sewtogetherToCp
delete paths.sewtogetherSewTogetherHinge
delete paths.sewtogetherSewTogether
return true
}
so = {
prefix: 'sewtogether',
...so,
}
if (complete) {
if (null == so.middle) {
so.middle = so.from.shiftFractionTowards(so.to, 0.5)
}
points[so.prefix + 'From'] = so.from
points[so.prefix + 'Middle'] = so.middle
points[so.prefix + 'To'] = so.to
points[so.prefix + 'FromCp'] = points[so.prefix + 'From'].shift(
points[so.prefix + 'From'].angle(points[so.prefix + 'Middle']) + 90,
points[so.prefix + 'From'].dist(points[so.prefix + 'Middle']) / 1.5
)
points[so.prefix + 'ToCp'] = points[so.prefix + 'To'].shift(
points[so.prefix + 'To'].angle(points[so.prefix + 'Middle']) - 90,
points[so.prefix + 'To'].dist(points[so.prefix + 'Middle']) / 1.5
)
if (so.hinge) {
points[so.prefix + 'Hinge'] = points[so.prefix + 'Middle'].shift(
points[so.prefix + 'Middle'].angle(points[so.prefix + 'To']) +
Math.abs(
points[so.prefix + 'Middle'].angle(points[so.prefix + 'From']) -
points[so.prefix + 'Middle'].angle(points[so.prefix + 'To'])
) /
2 +
(sa ? 180 : 0),
sa
? sa
: Math.min(
points[so.prefix + 'From'].dist(points[so.prefix + 'Middle']),
points[so.prefix + 'From'].dist(points[so.prefix + 'Middle'])
) / 4
)
paths[so.prefix + 'SewTogetherHinge'] = new Path()
.move(points[so.prefix + 'Middle'])
.line(points[so.prefix + 'Hinge'])
.attr('marker-start', 'url(#sewTogetherCross)')
.attr('class', 'dotted note stroke-sm')
}
paths[so.prefix + 'SewTogether'] = new Path()
.move(points[so.prefix + 'From'])
.curve(points[so.prefix + 'FromCp'], points[so.prefix + 'ToCp'], points[so.prefix + 'To'])
.attr('class', 'dotted note stroke-sm')
.attr('marker-start', 'url(#sewTogetherStart)')
.attr('marker-end', 'url(#sewTogetherEnd)')
.attr('data-text', 'sewTogether')
.attr('data-text-class', 'center fill-note text-xs')
}
},
}