Reduce repetition in themes slightly

This commit is contained in:
David Evans 2018-05-05 01:11:42 +01:00
parent 73cc61d430
commit 5c35d7e894
11 changed files with 629 additions and 797 deletions

View File

@ -34,8 +34,24 @@
} }
class BaseTheme { class BaseTheme {
constructor(svg) { constructor(svg, baseFontAttrs) {
this.svg = svg; this.svg = svg;
this.baseFontAttrs = baseFontAttrs;
this.fontSize = this.baseFontAttrs['font-size'];
this.connectLines = new Map();
}
addConnectLine(type, {
attrs = {},
pattern = null,
} = {}) {
const base = this.connectLines.get('solid') || {attrs: {}};
const fullAttrs = Object.assign({'fill': 'none'}, base.attrs, attrs);
this.connectLines.set(type, {
attrs: fullAttrs,
renderFlat: this.renderFlatConnect.bind(this, pattern, fullAttrs),
renderRev: this.renderRevConnect.bind(this, pattern, fullAttrs),
});
} }
// PUBLIC API // PUBLIC API
@ -48,6 +64,18 @@
// No-op // No-op
} }
getTitleAttrs() {
return Object.assign({}, this.baseFontAttrs, {
'font-size': this.fontSize * 2.5,
'text-anchor': 'middle',
});
}
getConnectLine(type) {
const lines = this.connectLines;
return lines.get(type) || lines.get('solid');
}
getBlock(type) { getBlock(type) {
return this.blocks[type] || this.blocks['']; return this.blocks[type] || this.blocks[''];
} }
@ -355,8 +383,6 @@
const FONT = 'Helvetica,Arial,Liberation Sans,sans-serif'; const FONT = 'Helvetica,Arial,Liberation Sans,sans-serif';
const LINE_HEIGHT = 1.3; const LINE_HEIGHT = 1.3;
const WAVE = new WavePattern(6, 0.5);
const NOTE_ATTRS = { const NOTE_ATTRS = {
'font-family': FONT, 'font-family': FONT,
'font-size': 8, 'font-size': 8,
@ -372,7 +398,11 @@
class BasicTheme extends BaseTheme { class BasicTheme extends BaseTheme {
constructor(svg) { constructor(svg) {
super(svg); super(svg, {
'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
});
const sharedBlockSection = { const sharedBlockSection = {
padding: { padding: {
@ -496,38 +526,6 @@
connect: { connect: {
loopbackRadius: 6, loopbackRadius: 6,
line: {
'solid': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'dash': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
'stroke-dasharray': '4, 2',
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'wave': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
},
renderFlat: this.renderFlatConnect.bind(this, WAVE),
renderRev: this.renderRevConnect.bind(this, WAVE),
},
},
arrow: { arrow: {
'single': { 'single': {
width: 5, width: 5,
@ -593,14 +591,6 @@
}, },
}, },
titleAttrs: {
'font-family': FONT,
'font-size': 20,
'line-height': LINE_HEIGHT,
'text-anchor': 'middle',
'class': 'title',
},
agentLineAttrs: { agentLineAttrs: {
'': { '': {
'fill': 'none', 'fill': 'none',
@ -738,6 +728,21 @@
}, },
}, },
}); });
this.addConnectLine('solid', {attrs: {
'stroke': '#000000',
'stroke-width': 1,
}});
this.addConnectLine('dash', {attrs: {
'stroke-dasharray': '4, 2',
}});
this.addConnectLine('wave', {
attrs: {
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
},
pattern: new WavePattern(6, 0.5),
});
} }
} }
@ -756,8 +761,6 @@
const FONT$1 = 'Helvetica,Arial,Liberation Sans,sans-serif'; const FONT$1 = 'Helvetica,Arial,Liberation Sans,sans-serif';
const LINE_HEIGHT$1 = 1.3; const LINE_HEIGHT$1 = 1.3;
const WAVE$1 = new WavePattern(10, 1);
const NOTE_ATTRS$1 = { const NOTE_ATTRS$1 = {
'font-family': FONT$1, 'font-family': FONT$1,
'font-size': 8, 'font-size': 8,
@ -773,7 +776,11 @@
class ChunkyTheme extends BaseTheme { class ChunkyTheme extends BaseTheme {
constructor(svg) { constructor(svg) {
super(svg); super(svg, {
'font-family': FONT$1,
'font-size': 8,
'line-height': LINE_HEIGHT$1,
});
const sharedBlockSection = { const sharedBlockSection = {
padding: { padding: {
@ -904,38 +911,6 @@
connect: { connect: {
loopbackRadius: 8, loopbackRadius: 8,
line: {
'solid': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 3,
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'dash': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 3,
'stroke-dasharray': '10, 4',
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'wave': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 3,
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
},
renderFlat: this.renderFlatConnect.bind(this, WAVE$1),
renderRev: this.renderRevConnect.bind(this, WAVE$1),
},
},
arrow: { arrow: {
'single': { 'single': {
width: 10, width: 10,
@ -1005,15 +980,6 @@
}, },
}, },
titleAttrs: {
'font-family': FONT$1,
'font-weight': 'bolder',
'font-size': 20,
'line-height': LINE_HEIGHT$1,
'text-anchor': 'middle',
'class': 'title',
},
agentLineAttrs: { agentLineAttrs: {
'': { '': {
'fill': 'none', 'fill': 'none',
@ -1156,6 +1122,27 @@
}, },
}, },
}); });
this.addConnectLine('solid', {attrs: {
'stroke': '#000000',
'stroke-width': 3,
}});
this.addConnectLine('dash', {attrs: {
'stroke-dasharray': '10, 4',
}});
this.addConnectLine('wave', {
attrs: {
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
},
pattern: new WavePattern(10, 1),
});
}
getTitleAttrs() {
return Object.assign(super.getTitleAttrs(), {
'font-weight': 'bolder',
});
} }
} }
@ -2758,17 +2745,6 @@
const FONT$2 = 'Courier New,Liberation Mono,monospace'; const FONT$2 = 'Courier New,Liberation Mono,monospace';
const LINE_HEIGHT$2 = 1.3; const LINE_HEIGHT$2 = 1.3;
const WAVE$2 = new WavePattern(6, [
+0,
-0.25,
-0.5,
-0.25,
+0,
+0.25,
+0.5,
+0.25,
]);
const NOTE_ATTRS$2 = { const NOTE_ATTRS$2 = {
'font-family': FONT$2, 'font-family': FONT$2,
'font-size': 8, 'font-size': 8,
@ -2784,7 +2760,11 @@
class MonospaceTheme extends BaseTheme { class MonospaceTheme extends BaseTheme {
constructor(svg) { constructor(svg) {
super(svg); super(svg, {
'font-family': FONT$2,
'font-size': 8,
'line-height': LINE_HEIGHT$2,
});
const sharedBlockSection = { const sharedBlockSection = {
padding: { padding: {
@ -2908,36 +2888,6 @@
connect: { connect: {
loopbackRadius: 4, loopbackRadius: 4,
line: {
'solid': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'dash': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
'stroke-dasharray': '4, 4',
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'wave': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
},
renderFlat: this.renderFlatConnect.bind(this, WAVE$2),
renderRev: this.renderRevConnect.bind(this, WAVE$2),
},
},
arrow: { arrow: {
'single': { 'single': {
width: 4, width: 4,
@ -3003,14 +2953,6 @@
}, },
}, },
titleAttrs: {
'font-family': FONT$2,
'font-size': 20,
'line-height': LINE_HEIGHT$2,
'text-anchor': 'middle',
'class': 'title',
},
agentLineAttrs: { agentLineAttrs: {
'': { '': {
'fill': 'none', 'fill': 'none',
@ -3142,6 +3084,26 @@
}, },
}, },
}); });
this.addConnectLine('solid', {attrs: {
'stroke': '#000000',
'stroke-width': 1,
}});
this.addConnectLine('dash', {attrs: {
'stroke-dasharray': '4, 4',
}});
this.addConnectLine('wave', {
pattern: new WavePattern(6, [
+0,
-0.25,
-0.5,
-0.25,
+0,
+0.25,
+0.5,
+0.25,
]),
});
} }
} }
@ -5691,13 +5653,13 @@
return this.getConfig(theme).height; return this.getConfig(theme).height;
} }
lineGap(theme, lineAttrs) { lineGap(theme, line) {
const arrow = this.getConfig(theme); const arrow = this.getConfig(theme);
const short = this.short(theme); const short = this.short(theme);
if(arrow.attrs.fill === 'none') { if(arrow.attrs.fill === 'none') {
const h = arrow.height / 2; const h = arrow.height / 2;
const w = arrow.width; const w = arrow.width;
const safe = short + (lineAttrs['stroke-width'] / 2) * (w / h); const safe = short + (line.attrs['stroke-width'] / 2) * (w / h);
return (short + safe) / 2; return (short + safe) / 2;
} else { } else {
return short + arrow.width / 2; return short + arrow.width / 2;
@ -5815,15 +5777,14 @@
} }
renderRevArrowLine({x1, y1, x2, y2, xR}, options, env, clickable) { renderRevArrowLine({x1, y1, x2, y2, xR}, options, env, clickable) {
const config = env.theme.connect; const line = env.theme.getConnectLine(options.line);
const line = config.line[options.line];
const lArrow = ARROWHEADS[options.left]; const lArrow = ARROWHEADS[options.left];
const rArrow = ARROWHEADS[options.right]; const rArrow = ARROWHEADS[options.right];
const dx1 = lArrow.lineGap(env.theme, line.attrs); const dx1 = lArrow.lineGap(env.theme, line);
const dx2 = rArrow.lineGap(env.theme, line.attrs); const dx2 = rArrow.lineGap(env.theme, line);
const rendered = line.renderRev(line.attrs, { const rendered = line.renderRev({
rad: config.loopbackRadius, rad: env.theme.connect.loopbackRadius,
x1: x1 + dx1, x1: x1 + dx1,
x2: x2 + dx2, x2: x2 + dx2,
xR, xR,
@ -5914,8 +5875,7 @@
} }
renderArrowLine({x1, y1, x2, y2}, options, env, clickable) { renderArrowLine({x1, y1, x2, y2}, options, env, clickable) {
const config = env.theme.connect; const line = env.theme.getConnectLine(options.line);
const line = config.line[options.line];
const lArrow = ARROWHEADS[options.left]; const lArrow = ARROWHEADS[options.left];
const rArrow = ARROWHEADS[options.right]; const rArrow = ARROWHEADS[options.right];
@ -5923,12 +5883,12 @@
(x2 - x1) * (x2 - x1) + (x2 - x1) * (x2 - x1) +
(y2 - y1) * (y2 - y1) (y2 - y1) * (y2 - y1)
); );
const d1 = lArrow.lineGap(env.theme, line.attrs); const d1 = lArrow.lineGap(env.theme, line);
const d2 = rArrow.lineGap(env.theme, line.attrs); const d2 = rArrow.lineGap(env.theme, line);
const dx = (x2 - x1) / len; const dx = (x2 - x1) / len;
const dy = (y2 - y1) / len; const dy = (y2 - y1) / len;
const rendered = line.renderFlat(line.attrs, { const rendered = line.renderFlat({
x1: x1 + d1 * dx, x1: x1 + d1 * dx,
x2: x2 - d2 * dx, x2: x2 - d2 * dx,
y1: y1 + d1 * dy, y1: y1 + d1 * dy,
@ -8103,7 +8063,9 @@
this.theme.addDefs(this.addThemeDef); this.theme.addDefs(this.addThemeDef);
this.title.set({ this.title.set({
attrs: this.theme.titleAttrs, attrs: Object.assign({
'class': 'title',
}, this.theme.getTitleAttrs()),
formatted: sequence.meta.title, formatted: sequence.meta.title,
}); });
this.svg.textSizer.expectMeasure(this.title); this.svg.textSizer.expectMeasure(this.title);
@ -8722,11 +8684,15 @@
class SketchTheme extends BaseTheme { class SketchTheme extends BaseTheme {
constructor(svg, handedness = RIGHT) { constructor(svg, handedness = RIGHT) {
super(svg); super(svg, {
'font-family': FONT$3,
'font-size': 8,
'line-height': LINE_HEIGHT$3,
});
this.handedness = (handedness === RIGHT) ? 1 : -1; this.handedness = (handedness === RIGHT) ? 1 : -1;
this.random = new Random(); this.random = new Random();
this.wave = new SketchWavePattern(4, handedness); const wave = new SketchWavePattern(4, handedness);
const sharedBlockSection = { const sharedBlockSection = {
padding: { padding: {
@ -8830,32 +8796,6 @@
connect: { connect: {
loopbackRadius: 6, loopbackRadius: 6,
line: {
'solid': {
attrs: Object.assign({
'fill': 'none',
}, PENCIL.normal),
renderFlat: this.renderFlatConnect.bind(this),
renderRev: this.renderRevConnect.bind(this),
},
'dash': {
attrs: Object.assign({
'fill': 'none',
'stroke-dasharray': '4, 2',
}, PENCIL.normal),
renderFlat: this.renderFlatConnect.bind(this),
renderRev: this.renderRevConnect.bind(this),
},
'wave': {
attrs: Object.assign({
'fill': 'none',
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
}, PENCIL.normal),
renderFlat: this.renderFlatConnectWave.bind(this),
renderRev: this.renderRevConnectWave.bind(this),
},
},
arrow: { arrow: {
'single': { 'single': {
width: 5, width: 5,
@ -8912,14 +8852,6 @@
}, },
}, },
titleAttrs: {
'font-family': FONT_FAMILY,
'font-size': 20,
'line-height': LINE_HEIGHT$3,
'text-anchor': 'middle',
'class': 'title',
},
agentLineAttrs: { agentLineAttrs: {
'': Object.assign({ '': Object.assign({
'fill': 'none', 'fill': 'none',
@ -9006,12 +8938,24 @@
render: this.renderTearDivider.bind(this, { render: this.renderTearDivider.bind(this, {
fadeBegin: 5, fadeBegin: 5,
fadeSize: 10, fadeSize: 10,
pattern: this.wave, pattern: wave,
lineAttrs: PENCIL.normal, lineAttrs: PENCIL.normal,
}), }),
}, },
}, },
}); });
this.addConnectLine('solid', {attrs: PENCIL.normal});
this.addConnectLine('dash', {attrs: {
'stroke-dasharray': '4, 2',
}});
this.addConnectLine('wave', {
attrs: {
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
},
pattern: wave,
});
} }
reset() { reset() {
@ -9216,85 +9160,85 @@
return {shape}; return {shape};
} }
renderFlatConnect(attrs, {x1, y1, x2, y2}) { renderFlatConnect(pattern, attrs, {x1, y1, x2, y2}) {
const ln = this.lineNodes( if(pattern) {
{x: x1, y: y1}, const x1v = x1 + this.vary(0.3);
{x: x2, y: y2}, const x2v = x2 + this.vary(0.3);
{varX: 0.3} const y1v = y1 + this.vary(1);
); const y2v = y2 + this.vary(1);
return { return {
shape: this.svg.el('path').attr('d', ln.nodes).attrs(attrs), shape: this.svg.el('path')
p1: ln.p1, .attr('d', this.svg.patternedLine(pattern)
p2: ln.p2, .move(x1v, y1v)
}; .line(x2v, y2v)
.cap()
.asPath())
.attrs(attrs),
p1: {x: x1v, y: y1v},
p2: {x: x2v, y: y2v},
};
} else {
const ln = this.lineNodes(
{x: x1, y: y1},
{x: x2, y: y2},
{varX: 0.3}
);
return {
shape: this.svg.el('path').attr('d', ln.nodes).attrs(attrs),
p1: ln.p1,
p2: ln.p2,
};
}
} }
renderRevConnect(attrs, {x1, y1, x2, y2, xR}) { renderRevConnect(pattern, attrs, {x1, y1, x2, y2, xR}) {
const variance = Math.min((xR - x1) * 0.06, 3); if(pattern) {
const overshoot = Math.min((xR - x1) * 0.5, 6); const x1v = x1 + this.vary(0.3);
const p1x = x1 + this.vary(variance, -1); const x2v = x2 + this.vary(0.3);
const p1y = y1 + this.vary(variance, -1); const y1v = y1 + this.vary(1);
const b1x = xR - overshoot * this.vary(0.2, 1); const y2v = y2 + this.vary(1);
const b1y = y1 - this.vary(1, 2); return {
const p2x = xR; shape: this.svg.el('path')
const p2y = y1 + this.vary(1, 1); .attr('d', this.svg.patternedLine(pattern)
const b2x = xR; .move(x1v, y1v)
const b2y = y2 + this.vary(2); .line(xR, y1)
const p3x = x2 + this.vary(variance, -1); .arc(xR, (y1 + y2) / 2, Math.PI)
const p3y = y2 + this.vary(variance, -1); .line(x2v, y2v)
.cap()
.asPath())
.attrs(attrs),
p1: {x: x1v, y: y1v},
p2: {x: x2v, y: y2v},
};
} else {
const variance = Math.min((xR - x1) * 0.06, 3);
const overshoot = Math.min((xR - x1) * 0.5, 6);
const p1x = x1 + this.vary(variance, -1);
const p1y = y1 + this.vary(variance, -1);
const b1x = xR - overshoot * this.vary(0.2, 1);
const b1y = y1 - this.vary(1, 2);
const p2x = xR;
const p2y = y1 + this.vary(1, 1);
const b2x = xR;
const b2y = y2 + this.vary(2);
const p3x = x2 + this.vary(variance, -1);
const p3y = y2 + this.vary(variance, -1);
return { return {
shape: this.svg.el('path') shape: this.svg.el('path')
.attr('d', ( .attr('d', (
'M' + p1x + ' ' + p1y + 'M' + p1x + ' ' + p1y +
'C' + p1x + ' ' + p1y + 'C' + p1x + ' ' + p1y +
',' + b1x + ' ' + b1y + ',' + b1x + ' ' + b1y +
',' + p2x + ' ' + p2y + ',' + p2x + ' ' + p2y +
'S' + b2x + ' ' + b2y + 'S' + b2x + ' ' + b2y +
',' + p3x + ' ' + p3y ',' + p3x + ' ' + p3y
)) ))
.attrs(attrs), .attrs(attrs),
p1: {x: p1x, y: p1y}, p1: {x: p1x, y: p1y},
p2: {x: p3x, y: p3y}, p2: {x: p3x, y: p3y},
}; };
} }
renderFlatConnectWave(attrs, {x1, y1, x2, y2}) {
const x1v = x1 + this.vary(0.3);
const x2v = x2 + this.vary(0.3);
const y1v = y1 + this.vary(1);
const y2v = y2 + this.vary(1);
return {
shape: this.svg.el('path')
.attr('d', this.svg.patternedLine(this.wave)
.move(x1v, y1v)
.line(x2v, y2v)
.cap()
.asPath())
.attrs(attrs),
p1: {x: x1v, y: y1v},
p2: {x: x2v, y: y2v},
};
}
renderRevConnectWave(attrs, {x1, y1, x2, y2, xR}) {
const x1v = x1 + this.vary(0.3);
const x2v = x2 + this.vary(0.3);
const y1v = y1 + this.vary(1);
const y2v = y2 + this.vary(1);
return {
shape: this.svg.el('path')
.attr('d', this.svg.patternedLine(this.wave)
.move(x1v, y1v)
.line(xR, y1)
.arc(xR, (y1 + y2) / 2, Math.PI)
.line(x2v, y2v)
.cap()
.asPath())
.attrs(attrs),
p1: {x: x1v, y: y1v},
p2: {x: x2v, y: y2v},
};
} }
renderArrowHead(attrs, {x, y, width, height, dir}) { renderArrowHead(attrs, {x, y, width, height, dir}) {

File diff suppressed because one or more lines are too long

View File

@ -34,8 +34,24 @@
} }
class BaseTheme { class BaseTheme {
constructor(svg) { constructor(svg, baseFontAttrs) {
this.svg = svg; this.svg = svg;
this.baseFontAttrs = baseFontAttrs;
this.fontSize = this.baseFontAttrs['font-size'];
this.connectLines = new Map();
}
addConnectLine(type, {
attrs = {},
pattern = null,
} = {}) {
const base = this.connectLines.get('solid') || {attrs: {}};
const fullAttrs = Object.assign({'fill': 'none'}, base.attrs, attrs);
this.connectLines.set(type, {
attrs: fullAttrs,
renderFlat: this.renderFlatConnect.bind(this, pattern, fullAttrs),
renderRev: this.renderRevConnect.bind(this, pattern, fullAttrs),
});
} }
// PUBLIC API // PUBLIC API
@ -48,6 +64,18 @@
// No-op // No-op
} }
getTitleAttrs() {
return Object.assign({}, this.baseFontAttrs, {
'font-size': this.fontSize * 2.5,
'text-anchor': 'middle',
});
}
getConnectLine(type) {
const lines = this.connectLines;
return lines.get(type) || lines.get('solid');
}
getBlock(type) { getBlock(type) {
return this.blocks[type] || this.blocks['']; return this.blocks[type] || this.blocks[''];
} }
@ -355,8 +383,6 @@
const FONT = 'Helvetica,Arial,Liberation Sans,sans-serif'; const FONT = 'Helvetica,Arial,Liberation Sans,sans-serif';
const LINE_HEIGHT = 1.3; const LINE_HEIGHT = 1.3;
const WAVE = new WavePattern(6, 0.5);
const NOTE_ATTRS = { const NOTE_ATTRS = {
'font-family': FONT, 'font-family': FONT,
'font-size': 8, 'font-size': 8,
@ -372,7 +398,11 @@
class BasicTheme extends BaseTheme { class BasicTheme extends BaseTheme {
constructor(svg) { constructor(svg) {
super(svg); super(svg, {
'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
});
const sharedBlockSection = { const sharedBlockSection = {
padding: { padding: {
@ -496,38 +526,6 @@
connect: { connect: {
loopbackRadius: 6, loopbackRadius: 6,
line: {
'solid': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'dash': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
'stroke-dasharray': '4, 2',
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'wave': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
},
renderFlat: this.renderFlatConnect.bind(this, WAVE),
renderRev: this.renderRevConnect.bind(this, WAVE),
},
},
arrow: { arrow: {
'single': { 'single': {
width: 5, width: 5,
@ -593,14 +591,6 @@
}, },
}, },
titleAttrs: {
'font-family': FONT,
'font-size': 20,
'line-height': LINE_HEIGHT,
'text-anchor': 'middle',
'class': 'title',
},
agentLineAttrs: { agentLineAttrs: {
'': { '': {
'fill': 'none', 'fill': 'none',
@ -738,6 +728,21 @@
}, },
}, },
}); });
this.addConnectLine('solid', {attrs: {
'stroke': '#000000',
'stroke-width': 1,
}});
this.addConnectLine('dash', {attrs: {
'stroke-dasharray': '4, 2',
}});
this.addConnectLine('wave', {
attrs: {
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
},
pattern: new WavePattern(6, 0.5),
});
} }
} }
@ -756,8 +761,6 @@
const FONT$1 = 'Helvetica,Arial,Liberation Sans,sans-serif'; const FONT$1 = 'Helvetica,Arial,Liberation Sans,sans-serif';
const LINE_HEIGHT$1 = 1.3; const LINE_HEIGHT$1 = 1.3;
const WAVE$1 = new WavePattern(10, 1);
const NOTE_ATTRS$1 = { const NOTE_ATTRS$1 = {
'font-family': FONT$1, 'font-family': FONT$1,
'font-size': 8, 'font-size': 8,
@ -773,7 +776,11 @@
class ChunkyTheme extends BaseTheme { class ChunkyTheme extends BaseTheme {
constructor(svg) { constructor(svg) {
super(svg); super(svg, {
'font-family': FONT$1,
'font-size': 8,
'line-height': LINE_HEIGHT$1,
});
const sharedBlockSection = { const sharedBlockSection = {
padding: { padding: {
@ -904,38 +911,6 @@
connect: { connect: {
loopbackRadius: 8, loopbackRadius: 8,
line: {
'solid': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 3,
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'dash': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 3,
'stroke-dasharray': '10, 4',
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'wave': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 3,
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
},
renderFlat: this.renderFlatConnect.bind(this, WAVE$1),
renderRev: this.renderRevConnect.bind(this, WAVE$1),
},
},
arrow: { arrow: {
'single': { 'single': {
width: 10, width: 10,
@ -1005,15 +980,6 @@
}, },
}, },
titleAttrs: {
'font-family': FONT$1,
'font-weight': 'bolder',
'font-size': 20,
'line-height': LINE_HEIGHT$1,
'text-anchor': 'middle',
'class': 'title',
},
agentLineAttrs: { agentLineAttrs: {
'': { '': {
'fill': 'none', 'fill': 'none',
@ -1156,6 +1122,27 @@
}, },
}, },
}); });
this.addConnectLine('solid', {attrs: {
'stroke': '#000000',
'stroke-width': 3,
}});
this.addConnectLine('dash', {attrs: {
'stroke-dasharray': '10, 4',
}});
this.addConnectLine('wave', {
attrs: {
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
},
pattern: new WavePattern(10, 1),
});
}
getTitleAttrs() {
return Object.assign(super.getTitleAttrs(), {
'font-weight': 'bolder',
});
} }
} }
@ -2758,17 +2745,6 @@
const FONT$2 = 'Courier New,Liberation Mono,monospace'; const FONT$2 = 'Courier New,Liberation Mono,monospace';
const LINE_HEIGHT$2 = 1.3; const LINE_HEIGHT$2 = 1.3;
const WAVE$2 = new WavePattern(6, [
+0,
-0.25,
-0.5,
-0.25,
+0,
+0.25,
+0.5,
+0.25,
]);
const NOTE_ATTRS$2 = { const NOTE_ATTRS$2 = {
'font-family': FONT$2, 'font-family': FONT$2,
'font-size': 8, 'font-size': 8,
@ -2784,7 +2760,11 @@
class MonospaceTheme extends BaseTheme { class MonospaceTheme extends BaseTheme {
constructor(svg) { constructor(svg) {
super(svg); super(svg, {
'font-family': FONT$2,
'font-size': 8,
'line-height': LINE_HEIGHT$2,
});
const sharedBlockSection = { const sharedBlockSection = {
padding: { padding: {
@ -2908,36 +2888,6 @@
connect: { connect: {
loopbackRadius: 4, loopbackRadius: 4,
line: {
'solid': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'dash': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
'stroke-dasharray': '4, 4',
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'wave': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
},
renderFlat: this.renderFlatConnect.bind(this, WAVE$2),
renderRev: this.renderRevConnect.bind(this, WAVE$2),
},
},
arrow: { arrow: {
'single': { 'single': {
width: 4, width: 4,
@ -3003,14 +2953,6 @@
}, },
}, },
titleAttrs: {
'font-family': FONT$2,
'font-size': 20,
'line-height': LINE_HEIGHT$2,
'text-anchor': 'middle',
'class': 'title',
},
agentLineAttrs: { agentLineAttrs: {
'': { '': {
'fill': 'none', 'fill': 'none',
@ -3142,6 +3084,26 @@
}, },
}, },
}); });
this.addConnectLine('solid', {attrs: {
'stroke': '#000000',
'stroke-width': 1,
}});
this.addConnectLine('dash', {attrs: {
'stroke-dasharray': '4, 4',
}});
this.addConnectLine('wave', {
pattern: new WavePattern(6, [
+0,
-0.25,
-0.5,
-0.25,
+0,
+0.25,
+0.5,
+0.25,
]),
});
} }
} }
@ -5691,13 +5653,13 @@
return this.getConfig(theme).height; return this.getConfig(theme).height;
} }
lineGap(theme, lineAttrs) { lineGap(theme, line) {
const arrow = this.getConfig(theme); const arrow = this.getConfig(theme);
const short = this.short(theme); const short = this.short(theme);
if(arrow.attrs.fill === 'none') { if(arrow.attrs.fill === 'none') {
const h = arrow.height / 2; const h = arrow.height / 2;
const w = arrow.width; const w = arrow.width;
const safe = short + (lineAttrs['stroke-width'] / 2) * (w / h); const safe = short + (line.attrs['stroke-width'] / 2) * (w / h);
return (short + safe) / 2; return (short + safe) / 2;
} else { } else {
return short + arrow.width / 2; return short + arrow.width / 2;
@ -5815,15 +5777,14 @@
} }
renderRevArrowLine({x1, y1, x2, y2, xR}, options, env, clickable) { renderRevArrowLine({x1, y1, x2, y2, xR}, options, env, clickable) {
const config = env.theme.connect; const line = env.theme.getConnectLine(options.line);
const line = config.line[options.line];
const lArrow = ARROWHEADS[options.left]; const lArrow = ARROWHEADS[options.left];
const rArrow = ARROWHEADS[options.right]; const rArrow = ARROWHEADS[options.right];
const dx1 = lArrow.lineGap(env.theme, line.attrs); const dx1 = lArrow.lineGap(env.theme, line);
const dx2 = rArrow.lineGap(env.theme, line.attrs); const dx2 = rArrow.lineGap(env.theme, line);
const rendered = line.renderRev(line.attrs, { const rendered = line.renderRev({
rad: config.loopbackRadius, rad: env.theme.connect.loopbackRadius,
x1: x1 + dx1, x1: x1 + dx1,
x2: x2 + dx2, x2: x2 + dx2,
xR, xR,
@ -5914,8 +5875,7 @@
} }
renderArrowLine({x1, y1, x2, y2}, options, env, clickable) { renderArrowLine({x1, y1, x2, y2}, options, env, clickable) {
const config = env.theme.connect; const line = env.theme.getConnectLine(options.line);
const line = config.line[options.line];
const lArrow = ARROWHEADS[options.left]; const lArrow = ARROWHEADS[options.left];
const rArrow = ARROWHEADS[options.right]; const rArrow = ARROWHEADS[options.right];
@ -5923,12 +5883,12 @@
(x2 - x1) * (x2 - x1) + (x2 - x1) * (x2 - x1) +
(y2 - y1) * (y2 - y1) (y2 - y1) * (y2 - y1)
); );
const d1 = lArrow.lineGap(env.theme, line.attrs); const d1 = lArrow.lineGap(env.theme, line);
const d2 = rArrow.lineGap(env.theme, line.attrs); const d2 = rArrow.lineGap(env.theme, line);
const dx = (x2 - x1) / len; const dx = (x2 - x1) / len;
const dy = (y2 - y1) / len; const dy = (y2 - y1) / len;
const rendered = line.renderFlat(line.attrs, { const rendered = line.renderFlat({
x1: x1 + d1 * dx, x1: x1 + d1 * dx,
x2: x2 - d2 * dx, x2: x2 - d2 * dx,
y1: y1 + d1 * dy, y1: y1 + d1 * dy,
@ -8103,7 +8063,9 @@
this.theme.addDefs(this.addThemeDef); this.theme.addDefs(this.addThemeDef);
this.title.set({ this.title.set({
attrs: this.theme.titleAttrs, attrs: Object.assign({
'class': 'title',
}, this.theme.getTitleAttrs()),
formatted: sequence.meta.title, formatted: sequence.meta.title,
}); });
this.svg.textSizer.expectMeasure(this.title); this.svg.textSizer.expectMeasure(this.title);
@ -8722,11 +8684,15 @@
class SketchTheme extends BaseTheme { class SketchTheme extends BaseTheme {
constructor(svg, handedness = RIGHT) { constructor(svg, handedness = RIGHT) {
super(svg); super(svg, {
'font-family': FONT$3,
'font-size': 8,
'line-height': LINE_HEIGHT$3,
});
this.handedness = (handedness === RIGHT) ? 1 : -1; this.handedness = (handedness === RIGHT) ? 1 : -1;
this.random = new Random(); this.random = new Random();
this.wave = new SketchWavePattern(4, handedness); const wave = new SketchWavePattern(4, handedness);
const sharedBlockSection = { const sharedBlockSection = {
padding: { padding: {
@ -8830,32 +8796,6 @@
connect: { connect: {
loopbackRadius: 6, loopbackRadius: 6,
line: {
'solid': {
attrs: Object.assign({
'fill': 'none',
}, PENCIL.normal),
renderFlat: this.renderFlatConnect.bind(this),
renderRev: this.renderRevConnect.bind(this),
},
'dash': {
attrs: Object.assign({
'fill': 'none',
'stroke-dasharray': '4, 2',
}, PENCIL.normal),
renderFlat: this.renderFlatConnect.bind(this),
renderRev: this.renderRevConnect.bind(this),
},
'wave': {
attrs: Object.assign({
'fill': 'none',
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
}, PENCIL.normal),
renderFlat: this.renderFlatConnectWave.bind(this),
renderRev: this.renderRevConnectWave.bind(this),
},
},
arrow: { arrow: {
'single': { 'single': {
width: 5, width: 5,
@ -8912,14 +8852,6 @@
}, },
}, },
titleAttrs: {
'font-family': FONT_FAMILY,
'font-size': 20,
'line-height': LINE_HEIGHT$3,
'text-anchor': 'middle',
'class': 'title',
},
agentLineAttrs: { agentLineAttrs: {
'': Object.assign({ '': Object.assign({
'fill': 'none', 'fill': 'none',
@ -9006,12 +8938,24 @@
render: this.renderTearDivider.bind(this, { render: this.renderTearDivider.bind(this, {
fadeBegin: 5, fadeBegin: 5,
fadeSize: 10, fadeSize: 10,
pattern: this.wave, pattern: wave,
lineAttrs: PENCIL.normal, lineAttrs: PENCIL.normal,
}), }),
}, },
}, },
}); });
this.addConnectLine('solid', {attrs: PENCIL.normal});
this.addConnectLine('dash', {attrs: {
'stroke-dasharray': '4, 2',
}});
this.addConnectLine('wave', {
attrs: {
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
},
pattern: wave,
});
} }
reset() { reset() {
@ -9216,85 +9160,85 @@
return {shape}; return {shape};
} }
renderFlatConnect(attrs, {x1, y1, x2, y2}) { renderFlatConnect(pattern, attrs, {x1, y1, x2, y2}) {
const ln = this.lineNodes( if(pattern) {
{x: x1, y: y1}, const x1v = x1 + this.vary(0.3);
{x: x2, y: y2}, const x2v = x2 + this.vary(0.3);
{varX: 0.3} const y1v = y1 + this.vary(1);
); const y2v = y2 + this.vary(1);
return { return {
shape: this.svg.el('path').attr('d', ln.nodes).attrs(attrs), shape: this.svg.el('path')
p1: ln.p1, .attr('d', this.svg.patternedLine(pattern)
p2: ln.p2, .move(x1v, y1v)
}; .line(x2v, y2v)
.cap()
.asPath())
.attrs(attrs),
p1: {x: x1v, y: y1v},
p2: {x: x2v, y: y2v},
};
} else {
const ln = this.lineNodes(
{x: x1, y: y1},
{x: x2, y: y2},
{varX: 0.3}
);
return {
shape: this.svg.el('path').attr('d', ln.nodes).attrs(attrs),
p1: ln.p1,
p2: ln.p2,
};
}
} }
renderRevConnect(attrs, {x1, y1, x2, y2, xR}) { renderRevConnect(pattern, attrs, {x1, y1, x2, y2, xR}) {
const variance = Math.min((xR - x1) * 0.06, 3); if(pattern) {
const overshoot = Math.min((xR - x1) * 0.5, 6); const x1v = x1 + this.vary(0.3);
const p1x = x1 + this.vary(variance, -1); const x2v = x2 + this.vary(0.3);
const p1y = y1 + this.vary(variance, -1); const y1v = y1 + this.vary(1);
const b1x = xR - overshoot * this.vary(0.2, 1); const y2v = y2 + this.vary(1);
const b1y = y1 - this.vary(1, 2); return {
const p2x = xR; shape: this.svg.el('path')
const p2y = y1 + this.vary(1, 1); .attr('d', this.svg.patternedLine(pattern)
const b2x = xR; .move(x1v, y1v)
const b2y = y2 + this.vary(2); .line(xR, y1)
const p3x = x2 + this.vary(variance, -1); .arc(xR, (y1 + y2) / 2, Math.PI)
const p3y = y2 + this.vary(variance, -1); .line(x2v, y2v)
.cap()
.asPath())
.attrs(attrs),
p1: {x: x1v, y: y1v},
p2: {x: x2v, y: y2v},
};
} else {
const variance = Math.min((xR - x1) * 0.06, 3);
const overshoot = Math.min((xR - x1) * 0.5, 6);
const p1x = x1 + this.vary(variance, -1);
const p1y = y1 + this.vary(variance, -1);
const b1x = xR - overshoot * this.vary(0.2, 1);
const b1y = y1 - this.vary(1, 2);
const p2x = xR;
const p2y = y1 + this.vary(1, 1);
const b2x = xR;
const b2y = y2 + this.vary(2);
const p3x = x2 + this.vary(variance, -1);
const p3y = y2 + this.vary(variance, -1);
return { return {
shape: this.svg.el('path') shape: this.svg.el('path')
.attr('d', ( .attr('d', (
'M' + p1x + ' ' + p1y + 'M' + p1x + ' ' + p1y +
'C' + p1x + ' ' + p1y + 'C' + p1x + ' ' + p1y +
',' + b1x + ' ' + b1y + ',' + b1x + ' ' + b1y +
',' + p2x + ' ' + p2y + ',' + p2x + ' ' + p2y +
'S' + b2x + ' ' + b2y + 'S' + b2x + ' ' + b2y +
',' + p3x + ' ' + p3y ',' + p3x + ' ' + p3y
)) ))
.attrs(attrs), .attrs(attrs),
p1: {x: p1x, y: p1y}, p1: {x: p1x, y: p1y},
p2: {x: p3x, y: p3y}, p2: {x: p3x, y: p3y},
}; };
} }
renderFlatConnectWave(attrs, {x1, y1, x2, y2}) {
const x1v = x1 + this.vary(0.3);
const x2v = x2 + this.vary(0.3);
const y1v = y1 + this.vary(1);
const y2v = y2 + this.vary(1);
return {
shape: this.svg.el('path')
.attr('d', this.svg.patternedLine(this.wave)
.move(x1v, y1v)
.line(x2v, y2v)
.cap()
.asPath())
.attrs(attrs),
p1: {x: x1v, y: y1v},
p2: {x: x2v, y: y2v},
};
}
renderRevConnectWave(attrs, {x1, y1, x2, y2, xR}) {
const x1v = x1 + this.vary(0.3);
const x2v = x2 + this.vary(0.3);
const y1v = y1 + this.vary(1);
const y2v = y2 + this.vary(1);
return {
shape: this.svg.el('path')
.attr('d', this.svg.patternedLine(this.wave)
.move(x1v, y1v)
.line(xR, y1)
.arc(xR, (y1 + y2) / 2, Math.PI)
.line(x2v, y2v)
.cap()
.asPath())
.attrs(attrs),
p1: {x: x1v, y: y1v},
p2: {x: x2v, y: y2v},
};
} }
renderArrowHead(attrs, {x, y, width, height, dir}) { renderArrowHead(attrs, {x, y, width, height, dir}) {

View File

@ -75,11 +75,11 @@ describe('SequenceDiagram', () => {
'<g>' + '<g>' +
'<text' + '<text' +
' x="0"' + ' x="0"' +
' class="title"' +
' font-family="Helvetica,Arial,Liberation Sans,sans-serif"' + ' font-family="Helvetica,Arial,Liberation Sans,sans-serif"' +
' font-size="20"' + ' font-size="20"' +
' line-height="1.3"' + ' line-height="1.3"' +
' text-anchor="middle"' + ' text-anchor="middle"' +
' class="title"' +
' y="-10">My title here</text>' + ' y="-10">My title here</text>' +
'</g>' + '</g>' +
'<g mask="url(#FullMask)">' + '<g mask="url(#FullMask)">' +

View File

@ -631,7 +631,9 @@ export default class Renderer extends EventObject {
this.theme.addDefs(this.addThemeDef); this.theme.addDefs(this.addThemeDef);
this.title.set({ this.title.set({
attrs: this.theme.titleAttrs, attrs: Object.assign({
'class': 'title',
}, this.theme.getTitleAttrs()),
formatted: sequence.meta.title, formatted: sequence.meta.title,
}); });
this.svg.textSizer.expectMeasure(this.title); this.svg.textSizer.expectMeasure(this.title);

View File

@ -50,13 +50,13 @@ class Arrowhead {
return this.getConfig(theme).height; return this.getConfig(theme).height;
} }
lineGap(theme, lineAttrs) { lineGap(theme, line) {
const arrow = this.getConfig(theme); const arrow = this.getConfig(theme);
const short = this.short(theme); const short = this.short(theme);
if(arrow.attrs.fill === 'none') { if(arrow.attrs.fill === 'none') {
const h = arrow.height / 2; const h = arrow.height / 2;
const w = arrow.width; const w = arrow.width;
const safe = short + (lineAttrs['stroke-width'] / 2) * (w / h); const safe = short + (line.attrs['stroke-width'] / 2) * (w / h);
return (short + safe) / 2; return (short + safe) / 2;
} else { } else {
return short + arrow.width / 2; return short + arrow.width / 2;
@ -174,15 +174,14 @@ export class Connect extends BaseComponent {
} }
renderRevArrowLine({x1, y1, x2, y2, xR}, options, env, clickable) { renderRevArrowLine({x1, y1, x2, y2, xR}, options, env, clickable) {
const config = env.theme.connect; const line = env.theme.getConnectLine(options.line);
const line = config.line[options.line];
const lArrow = ARROWHEADS[options.left]; const lArrow = ARROWHEADS[options.left];
const rArrow = ARROWHEADS[options.right]; const rArrow = ARROWHEADS[options.right];
const dx1 = lArrow.lineGap(env.theme, line.attrs); const dx1 = lArrow.lineGap(env.theme, line);
const dx2 = rArrow.lineGap(env.theme, line.attrs); const dx2 = rArrow.lineGap(env.theme, line);
const rendered = line.renderRev(line.attrs, { const rendered = line.renderRev({
rad: config.loopbackRadius, rad: env.theme.connect.loopbackRadius,
x1: x1 + dx1, x1: x1 + dx1,
x2: x2 + dx2, x2: x2 + dx2,
xR, xR,
@ -273,8 +272,7 @@ export class Connect extends BaseComponent {
} }
renderArrowLine({x1, y1, x2, y2}, options, env, clickable) { renderArrowLine({x1, y1, x2, y2}, options, env, clickable) {
const config = env.theme.connect; const line = env.theme.getConnectLine(options.line);
const line = config.line[options.line];
const lArrow = ARROWHEADS[options.left]; const lArrow = ARROWHEADS[options.left];
const rArrow = ARROWHEADS[options.right]; const rArrow = ARROWHEADS[options.right];
@ -282,12 +280,12 @@ export class Connect extends BaseComponent {
(x2 - x1) * (x2 - x1) + (x2 - x1) * (x2 - x1) +
(y2 - y1) * (y2 - y1) (y2 - y1) * (y2 - y1)
); );
const d1 = lArrow.lineGap(env.theme, line.attrs); const d1 = lArrow.lineGap(env.theme, line);
const d2 = rArrow.lineGap(env.theme, line.attrs); const d2 = rArrow.lineGap(env.theme, line);
const dx = (x2 - x1) / len; const dx = (x2 - x1) / len;
const dy = (y2 - y1) / len; const dy = (y2 - y1) / len;
const rendered = line.renderFlat(line.attrs, { const rendered = line.renderFlat({
x1: x1 + d1 * dx, x1: x1 + d1 * dx,
x2: x2 - d2 * dx, x2: x2 - d2 * dx,
y1: y1 + d1 * dy, y1: y1 + d1 * dy,

View File

@ -31,8 +31,24 @@ export class WavePattern {
} }
export default class BaseTheme { export default class BaseTheme {
constructor(svg) { constructor(svg, baseFontAttrs) {
this.svg = svg; this.svg = svg;
this.baseFontAttrs = baseFontAttrs;
this.fontSize = this.baseFontAttrs['font-size'];
this.connectLines = new Map();
}
addConnectLine(type, {
attrs = {},
pattern = null,
} = {}) {
const base = this.connectLines.get('solid') || {attrs: {}};
const fullAttrs = Object.assign({'fill': 'none'}, base.attrs, attrs);
this.connectLines.set(type, {
attrs: fullAttrs,
renderFlat: this.renderFlatConnect.bind(this, pattern, fullAttrs),
renderRev: this.renderRevConnect.bind(this, pattern, fullAttrs),
});
} }
// PUBLIC API // PUBLIC API
@ -45,6 +61,18 @@ export default class BaseTheme {
// No-op // No-op
} }
getTitleAttrs() {
return Object.assign({}, this.baseFontAttrs, {
'font-size': this.fontSize * 2.5,
'text-anchor': 'middle',
});
}
getConnectLine(type) {
const lines = this.connectLines;
return lines.get(type) || lines.get('solid');
}
getBlock(type) { getBlock(type) {
return this.blocks[type] || this.blocks['']; return this.blocks[type] || this.blocks[''];
} }

View File

@ -5,8 +5,6 @@ import BaseTheme, {WavePattern} from './BaseTheme.mjs';
const FONT = 'Helvetica,Arial,Liberation Sans,sans-serif'; const FONT = 'Helvetica,Arial,Liberation Sans,sans-serif';
const LINE_HEIGHT = 1.3; const LINE_HEIGHT = 1.3;
const WAVE = new WavePattern(6, 0.5);
const NOTE_ATTRS = { const NOTE_ATTRS = {
'font-family': FONT, 'font-family': FONT,
'font-size': 8, 'font-size': 8,
@ -22,7 +20,11 @@ const DIVIDER_LABEL_ATTRS = {
export default class BasicTheme extends BaseTheme { export default class BasicTheme extends BaseTheme {
constructor(svg) { constructor(svg) {
super(svg); super(svg, {
'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
});
const sharedBlockSection = { const sharedBlockSection = {
padding: { padding: {
@ -146,38 +148,6 @@ export default class BasicTheme extends BaseTheme {
connect: { connect: {
loopbackRadius: 6, loopbackRadius: 6,
line: {
'solid': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'dash': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
'stroke-dasharray': '4, 2',
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'wave': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
},
renderFlat: this.renderFlatConnect.bind(this, WAVE),
renderRev: this.renderRevConnect.bind(this, WAVE),
},
},
arrow: { arrow: {
'single': { 'single': {
width: 5, width: 5,
@ -243,14 +213,6 @@ export default class BasicTheme extends BaseTheme {
}, },
}, },
titleAttrs: {
'font-family': FONT,
'font-size': 20,
'line-height': LINE_HEIGHT,
'text-anchor': 'middle',
'class': 'title',
},
agentLineAttrs: { agentLineAttrs: {
'': { '': {
'fill': 'none', 'fill': 'none',
@ -388,6 +350,21 @@ export default class BasicTheme extends BaseTheme {
}, },
}, },
}); });
this.addConnectLine('solid', {attrs: {
'stroke': '#000000',
'stroke-width': 1,
}});
this.addConnectLine('dash', {attrs: {
'stroke-dasharray': '4, 2',
}});
this.addConnectLine('wave', {
attrs: {
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
},
pattern: new WavePattern(6, 0.5),
});
} }
} }

View File

@ -5,8 +5,6 @@ import BaseTheme, {WavePattern} from './BaseTheme.mjs';
const FONT = 'Helvetica,Arial,Liberation Sans,sans-serif'; const FONT = 'Helvetica,Arial,Liberation Sans,sans-serif';
const LINE_HEIGHT = 1.3; const LINE_HEIGHT = 1.3;
const WAVE = new WavePattern(10, 1);
const NOTE_ATTRS = { const NOTE_ATTRS = {
'font-family': FONT, 'font-family': FONT,
'font-size': 8, 'font-size': 8,
@ -22,7 +20,11 @@ const DIVIDER_LABEL_ATTRS = {
export default class ChunkyTheme extends BaseTheme { export default class ChunkyTheme extends BaseTheme {
constructor(svg) { constructor(svg) {
super(svg); super(svg, {
'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
});
const sharedBlockSection = { const sharedBlockSection = {
padding: { padding: {
@ -153,38 +155,6 @@ export default class ChunkyTheme extends BaseTheme {
connect: { connect: {
loopbackRadius: 8, loopbackRadius: 8,
line: {
'solid': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 3,
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'dash': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 3,
'stroke-dasharray': '10, 4',
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'wave': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 3,
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
},
renderFlat: this.renderFlatConnect.bind(this, WAVE),
renderRev: this.renderRevConnect.bind(this, WAVE),
},
},
arrow: { arrow: {
'single': { 'single': {
width: 10, width: 10,
@ -254,15 +224,6 @@ export default class ChunkyTheme extends BaseTheme {
}, },
}, },
titleAttrs: {
'font-family': FONT,
'font-weight': 'bolder',
'font-size': 20,
'line-height': LINE_HEIGHT,
'text-anchor': 'middle',
'class': 'title',
},
agentLineAttrs: { agentLineAttrs: {
'': { '': {
'fill': 'none', 'fill': 'none',
@ -405,6 +366,27 @@ export default class ChunkyTheme extends BaseTheme {
}, },
}, },
}); });
this.addConnectLine('solid', {attrs: {
'stroke': '#000000',
'stroke-width': 3,
}});
this.addConnectLine('dash', {attrs: {
'stroke-dasharray': '10, 4',
}});
this.addConnectLine('wave', {
attrs: {
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
},
pattern: new WavePattern(10, 1),
});
}
getTitleAttrs() {
return Object.assign(super.getTitleAttrs(), {
'font-weight': 'bolder',
});
} }
} }

View File

@ -5,17 +5,6 @@ import BaseTheme, {WavePattern} from './BaseTheme.mjs';
const FONT = 'Courier New,Liberation Mono,monospace'; const FONT = 'Courier New,Liberation Mono,monospace';
const LINE_HEIGHT = 1.3; const LINE_HEIGHT = 1.3;
const WAVE = new WavePattern(6, [
+0,
-0.25,
-0.5,
-0.25,
+0,
+0.25,
+0.5,
+0.25,
]);
const NOTE_ATTRS = { const NOTE_ATTRS = {
'font-family': FONT, 'font-family': FONT,
'font-size': 8, 'font-size': 8,
@ -31,7 +20,11 @@ const DIVIDER_LABEL_ATTRS = {
export default class MonospaceTheme extends BaseTheme { export default class MonospaceTheme extends BaseTheme {
constructor(svg) { constructor(svg) {
super(svg); super(svg, {
'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
});
const sharedBlockSection = { const sharedBlockSection = {
padding: { padding: {
@ -155,36 +148,6 @@ export default class MonospaceTheme extends BaseTheme {
connect: { connect: {
loopbackRadius: 4, loopbackRadius: 4,
line: {
'solid': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'dash': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
'stroke-dasharray': '4, 4',
},
renderFlat: this.renderFlatConnect.bind(this, null),
renderRev: this.renderRevConnect.bind(this, null),
},
'wave': {
attrs: {
'fill': 'none',
'stroke': '#000000',
'stroke-width': 1,
},
renderFlat: this.renderFlatConnect.bind(this, WAVE),
renderRev: this.renderRevConnect.bind(this, WAVE),
},
},
arrow: { arrow: {
'single': { 'single': {
width: 4, width: 4,
@ -250,14 +213,6 @@ export default class MonospaceTheme extends BaseTheme {
}, },
}, },
titleAttrs: {
'font-family': FONT,
'font-size': 20,
'line-height': LINE_HEIGHT,
'text-anchor': 'middle',
'class': 'title',
},
agentLineAttrs: { agentLineAttrs: {
'': { '': {
'fill': 'none', 'fill': 'none',
@ -389,6 +344,26 @@ export default class MonospaceTheme extends BaseTheme {
}, },
}, },
}); });
this.addConnectLine('solid', {attrs: {
'stroke': '#000000',
'stroke-width': 1,
}});
this.addConnectLine('dash', {attrs: {
'stroke-dasharray': '4, 4',
}});
this.addConnectLine('wave', {
pattern: new WavePattern(6, [
+0,
-0.25,
-0.5,
-0.25,
+0,
+0.25,
+0.5,
+0.25,
]),
});
} }
} }

View File

@ -72,11 +72,15 @@ function clamp(v, low, high) {
export default class SketchTheme extends BaseTheme { export default class SketchTheme extends BaseTheme {
constructor(svg, handedness = RIGHT) { constructor(svg, handedness = RIGHT) {
super(svg); super(svg, {
'font-family': FONT,
'font-size': 8,
'line-height': LINE_HEIGHT,
});
this.handedness = (handedness === RIGHT) ? 1 : -1; this.handedness = (handedness === RIGHT) ? 1 : -1;
this.random = new Random(); this.random = new Random();
this.wave = new SketchWavePattern(4, handedness); const wave = new SketchWavePattern(4, handedness);
const sharedBlockSection = { const sharedBlockSection = {
padding: { padding: {
@ -180,32 +184,6 @@ export default class SketchTheme extends BaseTheme {
connect: { connect: {
loopbackRadius: 6, loopbackRadius: 6,
line: {
'solid': {
attrs: Object.assign({
'fill': 'none',
}, PENCIL.normal),
renderFlat: this.renderFlatConnect.bind(this),
renderRev: this.renderRevConnect.bind(this),
},
'dash': {
attrs: Object.assign({
'fill': 'none',
'stroke-dasharray': '4, 2',
}, PENCIL.normal),
renderFlat: this.renderFlatConnect.bind(this),
renderRev: this.renderRevConnect.bind(this),
},
'wave': {
attrs: Object.assign({
'fill': 'none',
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
}, PENCIL.normal),
renderFlat: this.renderFlatConnectWave.bind(this),
renderRev: this.renderRevConnectWave.bind(this),
},
},
arrow: { arrow: {
'single': { 'single': {
width: 5, width: 5,
@ -262,14 +240,6 @@ export default class SketchTheme extends BaseTheme {
}, },
}, },
titleAttrs: {
'font-family': FONT_FAMILY,
'font-size': 20,
'line-height': LINE_HEIGHT,
'text-anchor': 'middle',
'class': 'title',
},
agentLineAttrs: { agentLineAttrs: {
'': Object.assign({ '': Object.assign({
'fill': 'none', 'fill': 'none',
@ -356,12 +326,24 @@ export default class SketchTheme extends BaseTheme {
render: this.renderTearDivider.bind(this, { render: this.renderTearDivider.bind(this, {
fadeBegin: 5, fadeBegin: 5,
fadeSize: 10, fadeSize: 10,
pattern: this.wave, pattern: wave,
lineAttrs: PENCIL.normal, lineAttrs: PENCIL.normal,
}), }),
}, },
}, },
}); });
this.addConnectLine('solid', {attrs: PENCIL.normal});
this.addConnectLine('dash', {attrs: {
'stroke-dasharray': '4, 2',
}});
this.addConnectLine('wave', {
attrs: {
'stroke-linejoin': 'round',
'stroke-linecap': 'round',
},
pattern: wave,
});
} }
reset() { reset() {
@ -566,85 +548,85 @@ export default class SketchTheme extends BaseTheme {
return {shape}; return {shape};
} }
renderFlatConnect(attrs, {x1, y1, x2, y2}) { renderFlatConnect(pattern, attrs, {x1, y1, x2, y2}) {
const ln = this.lineNodes( if(pattern) {
{x: x1, y: y1}, const x1v = x1 + this.vary(0.3);
{x: x2, y: y2}, const x2v = x2 + this.vary(0.3);
{varX: 0.3} const y1v = y1 + this.vary(1);
); const y2v = y2 + this.vary(1);
return { return {
shape: this.svg.el('path').attr('d', ln.nodes).attrs(attrs), shape: this.svg.el('path')
p1: ln.p1, .attr('d', this.svg.patternedLine(pattern)
p2: ln.p2, .move(x1v, y1v)
}; .line(x2v, y2v)
.cap()
.asPath())
.attrs(attrs),
p1: {x: x1v, y: y1v},
p2: {x: x2v, y: y2v},
};
} else {
const ln = this.lineNodes(
{x: x1, y: y1},
{x: x2, y: y2},
{varX: 0.3}
);
return {
shape: this.svg.el('path').attr('d', ln.nodes).attrs(attrs),
p1: ln.p1,
p2: ln.p2,
};
}
} }
renderRevConnect(attrs, {x1, y1, x2, y2, xR}) { renderRevConnect(pattern, attrs, {x1, y1, x2, y2, xR}) {
const variance = Math.min((xR - x1) * 0.06, 3); if(pattern) {
const overshoot = Math.min((xR - x1) * 0.5, 6); const x1v = x1 + this.vary(0.3);
const p1x = x1 + this.vary(variance, -1); const x2v = x2 + this.vary(0.3);
const p1y = y1 + this.vary(variance, -1); const y1v = y1 + this.vary(1);
const b1x = xR - overshoot * this.vary(0.2, 1); const y2v = y2 + this.vary(1);
const b1y = y1 - this.vary(1, 2); return {
const p2x = xR; shape: this.svg.el('path')
const p2y = y1 + this.vary(1, 1); .attr('d', this.svg.patternedLine(pattern)
const b2x = xR; .move(x1v, y1v)
const b2y = y2 + this.vary(2); .line(xR, y1)
const p3x = x2 + this.vary(variance, -1); .arc(xR, (y1 + y2) / 2, Math.PI)
const p3y = y2 + this.vary(variance, -1); .line(x2v, y2v)
.cap()
.asPath())
.attrs(attrs),
p1: {x: x1v, y: y1v},
p2: {x: x2v, y: y2v},
};
} else {
const variance = Math.min((xR - x1) * 0.06, 3);
const overshoot = Math.min((xR - x1) * 0.5, 6);
const p1x = x1 + this.vary(variance, -1);
const p1y = y1 + this.vary(variance, -1);
const b1x = xR - overshoot * this.vary(0.2, 1);
const b1y = y1 - this.vary(1, 2);
const p2x = xR;
const p2y = y1 + this.vary(1, 1);
const b2x = xR;
const b2y = y2 + this.vary(2);
const p3x = x2 + this.vary(variance, -1);
const p3y = y2 + this.vary(variance, -1);
return { return {
shape: this.svg.el('path') shape: this.svg.el('path')
.attr('d', ( .attr('d', (
'M' + p1x + ' ' + p1y + 'M' + p1x + ' ' + p1y +
'C' + p1x + ' ' + p1y + 'C' + p1x + ' ' + p1y +
',' + b1x + ' ' + b1y + ',' + b1x + ' ' + b1y +
',' + p2x + ' ' + p2y + ',' + p2x + ' ' + p2y +
'S' + b2x + ' ' + b2y + 'S' + b2x + ' ' + b2y +
',' + p3x + ' ' + p3y ',' + p3x + ' ' + p3y
)) ))
.attrs(attrs), .attrs(attrs),
p1: {x: p1x, y: p1y}, p1: {x: p1x, y: p1y},
p2: {x: p3x, y: p3y}, p2: {x: p3x, y: p3y},
}; };
} }
renderFlatConnectWave(attrs, {x1, y1, x2, y2}) {
const x1v = x1 + this.vary(0.3);
const x2v = x2 + this.vary(0.3);
const y1v = y1 + this.vary(1);
const y2v = y2 + this.vary(1);
return {
shape: this.svg.el('path')
.attr('d', this.svg.patternedLine(this.wave)
.move(x1v, y1v)
.line(x2v, y2v)
.cap()
.asPath())
.attrs(attrs),
p1: {x: x1v, y: y1v},
p2: {x: x2v, y: y2v},
};
}
renderRevConnectWave(attrs, {x1, y1, x2, y2, xR}) {
const x1v = x1 + this.vary(0.3);
const x2v = x2 + this.vary(0.3);
const y1v = y1 + this.vary(1);
const y2v = y2 + this.vary(1);
return {
shape: this.svg.el('path')
.attr('d', this.svg.patternedLine(this.wave)
.move(x1v, y1v)
.line(xR, y1)
.arc(xR, (y1 + y2) / 2, Math.PI)
.line(x2v, y2v)
.cap()
.asPath())
.attrs(attrs),
p1: {x: x1v, y: y1v},
p2: {x: x2v, y: y2v},
};
} }
renderArrowHead(attrs, {x, y, width, height, dir}) { renderArrowHead(attrs, {x, y, width, height, dir}) {