diff --git a/plugins/plugin-annotations/src/grainline.mjs b/plugins/plugin-annotations/src/grainline.mjs index be4c57024ee..fee769b6b82 100644 --- a/plugins/plugin-annotations/src/grainline.mjs +++ b/plugins/plugin-annotations/src/grainline.mjs @@ -1,52 +1,105 @@ +import { getIds } from './utils.mjs' + +/* + * Defaults for the grainline macro + */ +const macroDefaults = { + classes: { + line: 'note', + text: 'center fill-note', + }, + id: 'grainline', + margin: 0.05, + text: 'grainline', +} + // Export defs export const grainlineDefs = [ { name: 'grainlineFrom', def: ` - - + + `, }, { name: 'grainlineTo', def: ` - - + + `, }, ] -const dflts = { text: 'grainline' } +/* + * The rmgrainline macro + */ +const rmgrainline = function (id = macroDefaults.id, { paths, store, part }) { + for (const pid of Object.values( + store.get(['parts', part.name, 'macros', 'grainline', 'ids', id, 'paths'], {}) + )) + delete paths[pid] +} + +/* + * The grainline macro + */ +const grainline = function (config = {}, { points, paths, Path, complete, store, part }) { + /* + * Don't add a cutonfold indicator when complete is false, unless force is true + */ + if (!complete && !config.force) return + + /* + * Merge macro defaults with user-provided config to create the macro config (mc) + */ + const mc = { + ...macroDefaults, + ...config, + classes: macroDefaults.classes, + } + if (config.classes) mc.classes = { ...mc.classes, ...config.classes } + + /* + * Make sure mc.from and mc.to are Point instances + */ + if (!mc.from || typeof mc.from.attr !== 'function') { + log.warn(`Grainline macro called without a valid from point. Using (0,0) for from.`) + mc.from = new Point(0, 0) + } + if (!mc.to || typeof mc.to.attr !== 'function') { + log.warn(`Grainline macro called without a valid to point. Using (666,666) for to.`) + mc.to = new Point(666, 666) + } + + /* + * Store angle for use in cutlist + */ + store.cutlist.setGrain(mc.from.angle(mc.to)) + + /* + * Get the list of IDs + */ + const ids = getIds(['line'], mc.id, 'grainline') + + /* + * Draw the path + */ + const from = mc.from.shiftFractionTowards(mc.to, 0.05) + const to = mc.to.shiftFractionTowards(mc.from, 0.05) + paths[ids.line] = new Path() + .move(from) + .line(to) + .attr('class', mc.classes.line) + .attr('marker-start', 'url(#grainlineFrom)') + .attr('marker-end', 'url(#grainlineTo)') + .addText(mc.text, mc.classes.text) + + /* + * Store all IDs in the store so we can remove this macro with rmgrainline + */ + store.set(['parts', part.name, 'macros', 'grainline', 'ids', mc.id, 'paths'], ids) +} // Export macros -export const grainlineMacros = { - grainline: function (so = {}, { points, paths, Path, complete, store }) { - if (so === false) { - delete points.grainlineFrom - delete points.grainlineTo - delete paths.grainline - if (store.cutlist?.setGrain) store.cutlist.setGrain(90) // Restoring default - return true - } - so = { - ...dflts, - ...so, - } - - // store in cutlist - store.cutlist.setGrain(so.from.angle(so.to)) - - if (complete) { - points.grainlineFrom = so.from.shiftFractionTowards(so.to, 0.05) - points.grainlineTo = so.to.shiftFractionTowards(so.from, 0.05) - paths.grainline = new Path() - .move(points.grainlineFrom) - .line(points.grainlineTo) - .attr('class', 'note') - .attr('marker-start', 'url(#grainlineFrom)') - .attr('marker-end', 'url(#grainlineTo)') - .attr('data-text', so.text) - .attr('data-text-class', 'center fill-note') - } - }, -} +export const grainlineMacros = { grainline, rmgrainline }