1
0
Fork 0

breaking: New annotation plugin replaces others

This wraps up the initial work on a new annotations plugin that replaces
other plugins that provide annotations. As a result, the following
plugins are now no longer available:

- plugin-banner
- plugin-bartack
- plugin-buttons
- plugin-cutonfold
- plugin-dimension
- plugin-grainine
- plugin-logo
- plugin-notches
- plugin-scalebox
- plugin-title

The new plugin-annotations package provides all these plugins used to
provide. In addition, it also includes the following new macros:

- bannerbox
- pleat
- sewTogether
This commit is contained in:
joostdecock 2023-04-15 15:14:56 +02:00
parent ad18463e2a
commit d0447c0f77
39 changed files with 655 additions and 926 deletions

View file

@ -9,6 +9,7 @@ import { bannerboxMacros } from './bannerbox.mjs'
import { bartackMacros } from './bartack.mjs'
import { crossboxMacros } from './crossbox.mjs'
import { scaleboxMacros } from './scalebox.mjs'
import { titleMacros } from './title.mjs'
// Hooks and Macros
import { cutonfoldMacros, cutonfoldHooks } from './cutonfold.mjs'
import { dimensionsMacros, dimensionsHooks } from './dimensions.mjs'
@ -42,6 +43,7 @@ export const plugin = {
...grainlineMacros,
...pleatMacros,
...sewtogetherMacros,
...titleMacros,
},
}

View file

@ -5,8 +5,8 @@ const logo = (scale) =>
export const logoHooks = {
preRender: [
function (svg) {
if (svg.defs.indexOf('id="logo"') === -1) {
svg.defs += logo(svg.pattern.settings[0].scale)
for (const def of svg.defs) {
if (svg.defs.indexOf('id="logo"') === -1) svg.defs += logo(svg.pattern.settings[0].scale)
}
},
],

View file

@ -21,7 +21,7 @@ export const sewtogetherHooks = {
// Export macros
export const sewtogetherMacros = {
sewtogether: function (so, { points, paths, Path, complete, scale, sa }) {
sewTogether: function (so, { points, paths, Path, complete, scale, sa }) {
if (so === false) {
delete points.sewtogetherFrom
delete points.sewtogetherFromCp

View file

@ -0,0 +1,107 @@
const style = `
text.title-nr {
font-size: 24pt;
font-weight: 700;
text-anchor: middle;
dominant-baseline: reset-size;
}
text.title-name {
font-size: 7pt;
font-weight: 500;
text-anchor: middle;
dominant-baseline: reset-size;
}
text.title-pattern {
font-size: 4pt;
font-weight: 500;
dominant-baseline: reset-size;
text-anchor: middle;
font-style: italic;
}
`
const titleMacro = function (so, { points, scale, locale, store }) {
const prefix = so.prefix || ''
// Passing `false` will remove the title
if (so === false) {
for (const id of [
`_${prefix}_titleNr`,
`_${prefix}_titleName`,
`_${prefix}_titlePattern`,
`_${prefix}_titleFor`,
`_${prefix}_exportDate`,
])
delete points[id]
return true
}
const transform = function (anchor) {
const cx = anchor.x - so.scale * anchor.x
const cy = anchor.y - so.scale * anchor.y
return `matrix(${so.scale}, 0, 0, ${so.scale}, ${cx}, ${cy}) rotate(${so.rotation} ${anchor.x} ${anchor.y})`
}
const defaults = {
scale: 1,
rotation: 0,
}
so = { ...defaults, ...so }
so.scale = so.scale * scale
let overwrite = true
if (so.append) overwrite = false
points[`_${prefix}_titleNr`] = so.at
.clone()
.attr('data-text', so.nr, overwrite)
.attr('data-text-class', 'text-4xl fill-note font-bold')
.attr('data-text-transform', transform(so.at))
let shift = 8
if (so.title) {
points[`_${prefix}_titleName`] = so.at
.shift(-90 - so.rotation, shift * so.scale)
.attr('data-text', so.title)
.attr('data-text-class', 'text-lg fill-current font-bold')
.attr('data-text-transform', transform(so.at.shift(-90 - so.rotation, 13 * so.scale)))
shift += 8
}
let name = store.data?.name || 'No Name'
name = name.replace('@freesewing/', '')
points[`_${prefix}_titlePattern`] = so.at
.shift(-90 - so.rotation, shift * so.scale)
.attr('data-text', name)
.attr('data-text', 'v' + (store.data?.version || 'No Version'))
.attr('data-text-class', 'fill-note')
.attr('data-text-transform', transform(so.at.shift(-90 - so.rotation, shift * so.scale)))
if (store.data.for) {
shift += 8
points[`_${prefix}_titleFor`] = so.at
.shift(-90 - so.rotation, shift * so.scale)
.attr('data-text', '( ' + store.data.for + ' )')
.attr('data-text-class', 'fill-current font-bold')
.attr('data-text-transform', transform(so.at.shift(-90 - so.rotation, shift * so.scale)))
}
shift += 6
const now = new Date()
let hours = now.getHours()
let mins = now.getMinutes()
if (hours < 10) hours = `0${hours}`
if (mins < 10) mins = `0${mins}`
points[`_${prefix}_exportDate`] = so.at
.shift(-90 - so.rotation, shift * so.scale)
.attr(
'data-text',
now.toLocaleDateString(locale || 'en', {
weekday: 'long',
year: 'numeric',
month: 'short',
day: 'numeric',
})
)
.attr('data-text', `@ ${hours}:${mins}`)
.attr('data-text-class', 'text-sm')
.attr('data-text-transform', transform(so.at.shift(-90 - so.rotation, shift * so.scale)))
}
// Export macros
export const titleMacros = { title: titleMacro }

View file

@ -0,0 +1,87 @@
import chai from 'chai'
import { Design } from '@freesewing/core'
import { annotationsPlugin } from '../src/index.mjs'
const expect = chai.expect
describe('Banner Plugin Tests', () => {
it('Should add repeating text to a path', () => {
const part = {
name: 'test',
draft: ({ Point, points, Path, paths, macro, part }) => {
points.from = new Point(30, 30)
points.to = new Point(30, 100)
paths.example = new Path().move(points.from).line(points.to)
macro('banner', {
text: 'foo',
path: paths.example,
})
return part
},
plugins: [annotationsPlugin],
}
const design = new Design({ parts: [part] })
const pattern = new design()
pattern.draft()
const c = pattern.parts[0].test.paths.example
expect(c.attributes.get('data-text')).to.equal(
'&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;foo&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;foo&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;foo&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;foo&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;foo&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;foo&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;foo&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;foo&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;foo&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;foo&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;'
)
expect(c.attributes.get('data-text-class').trim()).to.equal('center')
expect(c.attributes.get('data-text-dy')).to.equal('-1')
})
it('Number of spaces should be configurable', () => {
const part = {
name: 'test',
draft: ({ Point, points, Path, paths, macro, part }) => {
points.from = new Point(30, 30)
points.to = new Point(30, 100)
paths.example2 = new Path().move(points.from).line(points.to)
macro('banner', {
text: 'foo',
path: paths.example2,
spaces: 2,
repeat: 2,
})
return part
},
plugins: [annotationsPlugin],
}
const design = new Design({ parts: [part] })
const pattern = new design()
pattern.draft()
const c = pattern.parts[0].test.paths.example2
expect(c.attributes.get('data-text')).to.equal('&#160;&#160;foo&#160;&#160;foo&#160;&#160;')
})
it('Number of repetitions should be configurable', () => {
const part = {
name: 'test',
draft: ({ Point, points, Path, paths, macro, part }) => {
points.from = new Point(30, 30)
points.to = new Point(30, 100)
paths.example3 = new Path().move(points.from).line(points.to)
macro('banner', {
text: 'foo',
path: paths.example3,
spaces: 1,
repeat: 4,
})
return part
},
plugins: [annotationsPlugin],
}
const design = new Design({ parts: [part] })
const pattern = new design()
pattern.draft()
const c = pattern.parts[0].test.paths.example3
expect(c.attributes.get('data-text')).to.equal('&#160;foo&#160;foo&#160;foo&#160;foo&#160;')
})
})

View file

@ -0,0 +1,240 @@
import chai from 'chai'
import { Design, round } from '@freesewing/core'
import { annotationsPlugin } from '../src/index.mjs'
const expect = chai.expect
describe('Dimension Plugin Tests', () => {
describe('Measures horizontal dimensions', function () {
const part = {
name: 'test',
draft: ({ Point, points, macro, part }) => {
points.from = new Point(10, 20)
points.to = new Point(200, 20)
macro('hd', {
from: points.from,
to: points.to,
y: 35,
})
return part
},
plugins: [annotationsPlugin],
}
const Test = new Design({ parts: [part] })
const pattern = new Test()
pattern.draft()
it('should draw a line and add text to indicate its length', () => {
const c = pattern.parts[0].test.paths['__paperless1']
expect(c.attributes.get('class')).to.equal('mark')
expect(c.attributes.get('marker-start')).to.equal('url(#dimensionFrom)')
expect(c.attributes.get('marker-end')).to.equal('url(#dimensionTo)')
expect(c.attributes.get('data-text')).to.equal('19cm')
expect(c.attributes.get('data-text-class')).to.equal('fill-mark center')
expect(c.ops[0].type).to.equal('move')
expect(c.ops[1].type).to.equal('line')
expect(c.ops[0].to.x).to.equal(10)
expect(c.ops[0].to.y).to.equal(35)
expect(c.ops[1].to.x).to.equal(200)
expect(c.ops[1].to.y).to.equal(35)
})
it('should draw the start marker', () => {
const c = pattern.parts[0].test.paths['__paperless1_ls']
expect(c.attributes.get('class')).to.equal('mark dotted')
expect(c.ops[0].type).to.equal('move')
expect(c.ops[1].type).to.equal('line')
expect(c.ops[0].to.x).to.equal(10)
expect(c.ops[0].to.y).to.equal(20)
expect(c.ops[1].to.x).to.equal(10)
expect(c.ops[1].to.y).to.equal(35)
})
it('should draw the end marker', () => {
const c = pattern.parts[0].test.paths['__paperless1_le']
expect(c.attributes.get('class')).to.equal('mark dotted')
expect(c.ops[0].type).to.equal('move')
expect(c.ops[1].type).to.equal('line')
expect(c.ops[0].to.x).to.equal(200)
expect(c.ops[0].to.y).to.equal(20)
expect(c.ops[1].to.x).to.equal(200)
expect(c.ops[1].to.y).to.equal(35)
})
})
describe('Measures vertical dimensions', () => {
const part = {
name: 'test',
draft: ({ Point, points, macro, part }) => {
points.from = new Point(10, 20)
points.to = new Point(10, 200)
macro('vd', {
from: points.from,
to: points.to,
x: 25,
})
return part
},
plugins: [annotationsPlugin],
}
const Test = new Design({ parts: [part] })
const pattern = new Test()
pattern.draft()
it('Should draw a line and add text to indicate its length', () => {
const c = pattern.parts[0].test.paths['__paperless1']
expect(c.attributes.get('class')).to.equal('mark')
expect(c.attributes.get('marker-start')).to.equal('url(#dimensionFrom)')
expect(c.attributes.get('marker-end')).to.equal('url(#dimensionTo)')
expect(c.attributes.get('data-text')).to.equal('18cm')
expect(c.attributes.get('data-text-class')).to.equal('fill-mark center')
expect(c.ops[0].type).to.equal('move')
expect(c.ops[1].type).to.equal('line')
expect(c.ops[0].to.x).to.equal(25)
expect(c.ops[0].to.y).to.equal(20)
expect(c.ops[1].to.x).to.equal(25)
expect(c.ops[1].to.y).to.equal(200)
})
it('Should draw the start marker', () => {
const c = pattern.parts[0].test.paths['__paperless1_ls']
expect(c.attributes.get('class')).to.equal('mark dotted')
expect(c.ops[0].type).to.equal('move')
expect(c.ops[1].type).to.equal('line')
expect(c.ops[0].to.x).to.equal(10)
expect(c.ops[0].to.y).to.equal(20)
expect(c.ops[1].to.x).to.equal(25)
expect(c.ops[1].to.y).to.equal(20)
})
it('Should draw the end marker', () => {
const c = pattern.parts[0].test.paths['__paperless1_le']
expect(c.attributes.get('class')).to.equal('mark dotted')
expect(c.ops[0].type).to.equal('move')
expect(c.ops[1].type).to.equal('line')
expect(c.ops[0].to.x).to.equal(10)
expect(c.ops[0].to.y).to.equal(200)
expect(c.ops[1].to.x).to.equal(25)
expect(c.ops[1].to.y).to.equal(200)
})
})
describe('Measures the length of straight lines', () => {
const part = {
name: 'test',
draft: ({ Point, points, macro, part }) => {
points.from = new Point(10, 10)
points.to = new Point(100, 100)
macro('ld', {
from: points.from,
to: points.to,
d: 15,
})
return part
},
plugins: [annotationsPlugin],
}
const Test = new Design({ parts: [part] })
const pattern = new Test()
pattern.draft()
it('Should draw a line and add text to indicate its length', () => {
const c = pattern.parts[0].test.paths['__paperless1']
expect(c.attributes.get('class')).to.equal('mark')
expect(c.attributes.get('marker-start')).to.equal('url(#dimensionFrom)')
expect(c.attributes.get('marker-end')).to.equal('url(#dimensionTo)')
expect(c.attributes.get('data-text')).to.equal('12.73cm')
expect(c.attributes.get('data-text-class')).to.equal('fill-mark center')
expect(c.ops[0].type).to.equal('move')
expect(c.ops[1].type).to.equal('line')
expect(round(c.ops[0].to.x)).to.equal(20.61)
expect(round(c.ops[0].to.y)).to.equal(-0.61)
expect(round(c.ops[1].to.x)).to.equal(110.61)
expect(round(c.ops[1].to.y)).to.equal(89.39)
})
it('Should draw the start marker', () => {
const c = pattern.parts[0].test.paths['__paperless1_ls']
expect(c.attributes.get('class')).to.equal('mark dotted')
expect(c.ops[0].type).to.equal('move')
expect(c.ops[1].type).to.equal('line')
expect(round(c.ops[0].to.x)).to.equal(10)
expect(round(c.ops[0].to.y)).to.equal(10)
expect(round(c.ops[1].to.x)).to.equal(20.61)
expect(round(c.ops[1].to.y)).to.equal(-0.61)
})
it('Should draw the end marker', () => {
const c = pattern.parts[0].test.paths['__paperless1_le']
expect(c.attributes.get('class')).to.equal('mark dotted')
expect(c.ops[0].type).to.equal('move')
expect(c.ops[1].type).to.equal('line')
expect(round(c.ops[0].to.x)).to.equal(100)
expect(round(c.ops[0].to.y)).to.equal(100)
expect(round(c.ops[1].to.x)).to.equal(110.61)
expect(round(c.ops[1].to.y)).to.equal(89.39)
})
})
describe('Measures curved lines', () => {
const part = {
name: 'test',
draft: ({ Point, points, macro, Path, part }) => {
points.from = new Point(10, 10)
points.cp1 = new Point(100, 10)
points.cp2 = new Point(10, 100)
points.to = new Point(100, 100)
macro('pd', {
path: new Path().move(points.from).curve(points.cp1, points.cp2, points.to),
d: 15,
})
return part
},
plugins: [annotationsPlugin],
}
const Test = new Design({ parts: [part] })
const pattern = new Test()
pattern.draft()
it('Should draw a line and add text to indicate the length', () => {
const c = pattern.parts[0].test.paths['__paperless1']
expect(c.attributes.get('class')).to.equal('mark')
expect(c.attributes.get('marker-start')).to.equal('url(#dimensionFrom)')
expect(c.attributes.get('marker-end')).to.equal('url(#dimensionTo)')
expect(c.attributes.get('data-text')).to.equal('15.09cm')
expect(c.attributes.get('data-text-class')).to.equal('fill-mark center')
expect(c.ops[0].type).to.equal('move')
expect(c.ops[1].type).to.equal('curve')
expect(round(c.ops[0].to.x)).to.equal(10)
expect(round(c.ops[0].to.y)).to.equal(25)
expect(round(c.ops[1].to.x)).to.equal(37.15)
expect(round(c.ops[1].to.y)).to.equal(32.79)
})
it('Should draw the start marker', () => {
const c = pattern.parts[0].test.paths['__paperless1_ls']
expect(c.attributes.get('class')).to.equal('mark dotted')
expect(c.ops[0].type).to.equal('move')
expect(c.ops[1].type).to.equal('line')
expect(c.ops[0].to.x).to.equal(10)
expect(c.ops[0].to.y).to.equal(10)
expect(c.ops[1].to.x).to.equal(10)
expect(c.ops[1].to.y).to.equal(25)
})
it('Should draw the end marker', () => {
const c = pattern.parts[0].test.paths['__paperless1_le']
expect(c.attributes.get('class')).to.equal('mark dotted')
expect(c.ops[0].type).to.equal('move')
expect(c.ops[1].type).to.equal('line')
expect(c.ops[0].to.x).to.equal(100)
expect(c.ops[0].to.y).to.equal(100)
expect(c.ops[1].to.x).to.equal(100)
expect(c.ops[1].to.y).to.equal(115)
})
})
})

View file

@ -0,0 +1,119 @@
import chai from 'chai'
import { Design } from '@freesewing/core'
import { annotationsPlugin } from '../src/index.mjs'
const expect = chai.expect
describe('Title Plugin Tests', () => {
it('Should run the title macro', () => {
const part = {
name: 'test',
draft: ({ points, Point, macro, part }) => {
points.anchor = new Point(-12, -34)
macro('title', {
at: points.anchor,
nr: 3,
title: 'unitTest',
})
return part
},
plugins: [annotationsPlugin],
}
const Pattern = new Design({
data: { name: 'testPattern', version: 99 },
parts: [part],
})
const pattern = new Pattern()
pattern.draft().render()
let p = pattern.parts[0].test.points.__titleNr
expect(p.x).to.equal(-12)
expect(p.y).to.equal(-34)
expect(p.attributes.get('data-text')).to.equal('3')
expect(p.attributes.get('data-text-class')).to.equal('text-4xl fill-note font-bold')
expect(p.attributes.get('data-text-x')).to.equal('-12')
expect(p.attributes.get('data-text-y')).to.equal('-34')
p = pattern.parts[0].test.points.__titleName
expect(p.attributes.get('data-text')).to.equal('unitTest')
expect(p.attributes.get('data-text-class')).to.equal('text-lg fill-current font-bold')
expect(p.attributes.get('data-text-x')).to.equal('-12')
expect(p.attributes.get('data-text-y')).to.equal('-26')
p = pattern.parts[0].test.points.__titlePattern
expect(p.attributes.get('data-text')).to.equal('testPattern v99')
expect(p.attributes.get('data-text-class')).to.equal('fill-note')
expect(p.attributes.get('data-text-x')).to.equal('-12')
expect(p.attributes.get('data-text-y')).to.equal('-18')
})
it('Should run the title macro with append flag', () => {
const part = {
name: 'test',
draft: ({ points, Point, macro, part }) => {
points.anchor = new Point(-12, -34).attr('data-text', '#')
macro('title', {
at: points.anchor,
nr: 3,
title: 'unitTest',
append: true,
})
return part
},
plugins: [annotationsPlugin],
}
const Pattern = new Design({
data: { name: 'testPattern', version: 99 },
parts: [part],
})
const pattern = new Pattern()
pattern.draft().render()
let p = pattern.parts[0].test.points.__titleNr
expect(p.x).to.equal(-12)
expect(p.y).to.equal(-34)
expect(p.attributes.get('data-text')).to.equal('# 3')
expect(p.attributes.get('data-text-class')).to.equal('text-4xl fill-note font-bold')
expect(p.attributes.get('data-text-x')).to.equal('-12')
expect(p.attributes.get('data-text-y')).to.equal('-34')
})
it('Should run the title macro with point prefix', () => {
const part = {
name: 'test',
draft: ({ points, Point, macro, part }) => {
points.anchor = new Point(-12, -34).attr('data-text', '#')
macro('title', {
at: points.anchor,
nr: 3,
title: 'unitTest',
prefix: 'foo',
})
return part
},
plugins: [annotationsPlugin],
}
const Pattern = new Design({
data: { name: 'testPattern', version: 99 },
parts: [part],
})
const pattern = new Pattern()
pattern.draft().render()
let p = pattern.parts[0].test.points._foo_titleNr
expect(p.x).to.equal(-12)
expect(p.y).to.equal(-34)
expect(p.attributes.get('data-text')).to.equal('3')
expect(p.attributes.get('data-text-class')).to.equal('text-4xl fill-note font-bold')
expect(p.attributes.get('data-text-x')).to.equal('-12')
expect(p.attributes.get('data-text-y')).to.equal('-34')
p = pattern.parts[0].test.points._foo_titleName
expect(p.attributes.get('data-text')).to.equal('unitTest')
expect(p.attributes.get('data-text-class')).to.equal('text-lg fill-current font-bold')
expect(p.attributes.get('data-text-x')).to.equal('-12')
expect(p.attributes.get('data-text-y')).to.equal('-26')
p = pattern.parts[0].test.points._foo_titlePattern
expect(p.attributes.get('data-text')).to.equal('testPattern v99')
expect(p.attributes.get('data-text-class')).to.equal('fill-note')
expect(p.attributes.get('data-text-x')).to.equal('-12')
expect(p.attributes.get('data-text-y')).to.equal('-18')
})
})