SequenceDiagram/scripts/sequence/themes/BaseTheme.js

358 lines
7.4 KiB
JavaScript

define([
'svg/SVGUtilities',
'svg/SVGShapes',
], (
svg,
SVGShapes
) => {
'use strict';
function deepCopy(o) {
if(typeof o !== 'object' || !o) {
return o;
}
const r = {};
for(const k in o) {
if(o.hasOwnProperty(k)) {
r[k] = deepCopy(o[k]);
}
}
return r;
}
class BaseTheme {
constructor({name, settings, blocks, notes, dividers}) {
this.name = name;
this.blocks = deepCopy(blocks);
this.notes = deepCopy(notes);
this.dividers = deepCopy(dividers);
Object.assign(this, deepCopy(settings));
}
reset() {
}
addDefs() {
}
getBlock(type) {
return this.blocks[type] || this.blocks[''];
}
getNote(type) {
return this.notes[type] || this.notes[''];
}
getDivider(type) {
return this.dividers[type] || this.dividers[''];
}
renderAgentLine({x, y0, y1, width, className}) {
if(width > 0) {
return svg.make('rect', Object.assign({
'x': x - width / 2,
'y': y0,
'width': width,
'height': y1 - y0,
'class': className,
}, this.agentLineAttrs));
} else {
return svg.make('line', Object.assign({
'x1': x,
'y1': y0,
'x2': x,
'y2': y1,
'class': className,
}, this.agentLineAttrs));
}
}
}
BaseTheme.renderArrowHead = (attrs, {x, y, width, height, dir}) => {
const wx = width * dir.dx;
const wy = width * dir.dy;
const hy = height * 0.5 * dir.dx;
const hx = -height * 0.5 * dir.dy;
return svg.make(
attrs.fill === 'none' ? 'polyline' : 'polygon',
Object.assign({
'points': (
(x + wx - hx) + ' ' + (y + wy - hy) + ' ' +
x + ' ' + y + ' ' +
(x + wx + hx) + ' ' + (y + wy + hy)
),
}, attrs)
);
};
BaseTheme.renderTag = (attrs, {x, y, width, height}) => {
const {rx, ry} = attrs;
const x2 = x + width;
const y2 = y + height;
const line = (
'M' + x2 + ' ' + y +
'L' + x2 + ' ' + (y2 - ry) +
'L' + (x2 - rx) + ' ' + y2 +
'L' + x + ' ' + y2
);
const g = svg.make('g');
if(attrs.fill !== 'none') {
g.appendChild(svg.make('path', Object.assign({
'd': line + 'L' + x + ' ' + y,
}, attrs, {'stroke': 'none'})));
}
if(attrs.stroke !== 'none') {
g.appendChild(svg.make('path', Object.assign({
'd': line,
}, attrs, {'fill': 'none'})));
}
return g;
};
BaseTheme.renderCross = (attrs, {x, y, radius}) => {
return svg.make('path', Object.assign({
'd': (
'M' + (x - radius) + ' ' + (y - radius) +
'l' + (radius * 2) + ' ' + (radius * 2) +
'm0 ' + (-radius * 2) +
'l' + (-radius * 2) + ' ' + (radius * 2)
),
}, attrs));
};
BaseTheme.renderRef = (options, position) => {
return {
shape: SVGShapes.renderBox(Object.assign({}, options, {
'fill': 'none',
}), position),
mask: SVGShapes.renderBox(Object.assign({}, options, {
'fill': '#000000',
'stroke': 'none',
}), position),
fill: SVGShapes.renderBox(Object.assign({}, options, {
'stroke': 'none',
}), position),
};
};
BaseTheme.WavePattern = class WavePattern {
constructor(width, height) {
if(Array.isArray(height)) {
this.deltas = height;
} else {
this.deltas = [
0,
-height * 2 / 3,
-height,
-height * 2 / 3,
0,
height * 2 / 3,
height,
height * 2 / 3,
];
}
this.partWidth = width / this.deltas.length;
}
getDelta(p) {
return this.deltas[p % this.deltas.length];
}
};
BaseTheme.renderFlatConnector = (
pattern,
attrs,
{x1, y1, x2, y2}
) => {
return {
shape: svg.make('path', Object.assign({
d: new SVGShapes.PatternedLine(pattern)
.move(x1, y1)
.line(x2, y2)
.cap()
.asPath(),
}, attrs)),
p1: {x: x1, y: y1},
p2: {x: x2, y: y2},
};
};
BaseTheme.renderRevConnector = (
pattern,
attrs,
{x1, y1, x2, y2, xR, rad}
) => {
const maxRad = (y2 - y1) / 2;
const line = new SVGShapes.PatternedLine(pattern)
.move(x1, y1)
.line(xR, y1);
if(rad < maxRad) {
line
.arc(xR, y1 + rad, Math.PI / 2)
.line(xR + rad, y2 - rad)
.arc(xR, y2 - rad, Math.PI / 2);
} else {
line.arc(xR, (y1 + y2) / 2, Math.PI);
}
return {
shape: svg.make('path', Object.assign({
d: line
.line(x2, y2)
.cap()
.asPath(),
}, attrs)),
p1: {x: x1, y: y1},
p2: {x: x2, y: y2},
};
};
BaseTheme.renderLineDivider = (
{lineAttrs},
{x, y, labelWidth, width, height}
) => {
let shape = null;
const yPos = y + height / 2;
if(labelWidth > 0) {
shape = svg.make('g', {}, [
svg.make('line', Object.assign({
'x1': x,
'y1': yPos,
'x2': x + (width - labelWidth) / 2,
'y2': yPos,
'fill': 'none',
}, lineAttrs)),
svg.make('line', Object.assign({
'x1': x + (width + labelWidth) / 2,
'y1': yPos,
'x2': x + width,
'y2': yPos,
'fill': 'none',
}, lineAttrs)),
]);
} else {
shape = svg.make('line', Object.assign({
'x1': x,
'y1': yPos,
'x2': x + width,
'y2': yPos,
'fill': 'none',
}, lineAttrs));
}
return {shape};
};
BaseTheme.renderDelayDivider = (
{dotSize, gapSize},
{x, y, width, height}
) => {
const mask = svg.make('g');
for(let i = 0; i + gapSize <= height; i += dotSize + gapSize) {
mask.appendChild(svg.make('rect', {
'x': x,
'y': y + i,
'width': width,
'height': gapSize,
'fill': '#000000',
}));
}
return {mask};
};
BaseTheme.renderTearDivider = (
{fadeBegin, fadeSize, pattern, zigWidth, zigHeight, lineAttrs},
{x, y, labelWidth, labelHeight, width, height, env}
) => {
const maskGradID = env.addDef('tear-grad', () => {
const px = 100 / width;
return svg.make('linearGradient', {}, [
svg.make('stop', {
'offset': (fadeBegin * px) + '%',
'stop-color': '#000000',
}),
svg.make('stop', {
'offset': ((fadeBegin + fadeSize) * px) + '%',
'stop-color': '#FFFFFF',
}),
svg.make('stop', {
'offset': (100 - (fadeBegin + fadeSize) * px) + '%',
'stop-color': '#FFFFFF',
}),
svg.make('stop', {
'offset': (100 - fadeBegin * px) + '%',
'stop-color': '#000000',
}),
]);
});
const shapeMask = svg.make('mask', {
'maskUnits': 'userSpaceOnUse',
}, [
svg.make('rect', {
'x': x,
'y': y - 5,
'width': width,
'height': height + 10,
'fill': 'url(#' + maskGradID + ')',
}),
]);
const shapeMaskID = env.addDef(shapeMask);
if(labelWidth > 0) {
shapeMask.appendChild(svg.make('rect', {
'x': x + (width - labelWidth) / 2,
'y': y + (height - labelHeight) / 2 - 1,
'width': labelWidth,
'height': labelHeight + 2,
'rx': 2,
'ry': 2,
'fill': '#000000',
}));
}
if(!pattern) {
pattern = new BaseTheme.WavePattern(
zigWidth,
[zigHeight, -zigHeight]
);
}
let mask = null;
const pathTop = new SVGShapes.PatternedLine(pattern)
.move(x, y)
.line(x + width, y);
const shape = svg.make('g', {
'mask': 'url(#' + shapeMaskID + ')',
}, [
svg.make('path', Object.assign({
'd': pathTop.asPath(),
'fill': 'none',
}, lineAttrs)),
]);
if(height > 0) {
const pathBase = new SVGShapes.PatternedLine(pattern)
.move(x, y + height)
.line(x + width, y + height);
shape.appendChild(svg.make('path', Object.assign({
'd': pathBase.asPath(),
'fill': 'none',
}, lineAttrs)));
pathTop
.line(pathBase.x, pathBase.y, {patterned: false})
.cap();
pathTop.points.push(...pathBase.points.reverse());
mask = svg.make('path', {
'd': pathTop.asPath(),
'fill': '#000000',
});
}
return {shape, mask};
};
return BaseTheme;
});