358 lines
7.4 KiB
JavaScript
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;
|
|
});
|