241 lines
5.6 KiB
JavaScript
241 lines
5.6 KiB
JavaScript
define([
|
|
'./BaseComponent',
|
|
'svg/SVGUtilities',
|
|
'svg/SVGShapes',
|
|
], (
|
|
BaseComponent,
|
|
svg,
|
|
SVGShapes
|
|
) => {
|
|
'use strict';
|
|
|
|
function drawHorizontalArrowHead(container, {x, y, dx, dy, attrs}) {
|
|
container.appendChild(svg.make(
|
|
attrs.fill === 'none' ? 'polyline' : 'polygon',
|
|
Object.assign({
|
|
'points': (
|
|
(x + dx) + ' ' + (y - dy) + ' ' +
|
|
x + ' ' + y + ' ' +
|
|
(x + dx) + ' ' + (y + dy)
|
|
),
|
|
}, attrs)
|
|
));
|
|
}
|
|
|
|
function getArrowShort(theme) {
|
|
const arrow = theme.connect.arrow;
|
|
const h = arrow.height / 2;
|
|
const w = arrow.width;
|
|
const t = arrow.attrs['stroke-width'] * 0.5;
|
|
const lineStroke = theme.agentLineAttrs['stroke-width'] * 0.5;
|
|
const arrowDistance = t * Math.sqrt((w * w) / (h * h) + 1);
|
|
return lineStroke + arrowDistance;
|
|
}
|
|
|
|
class Connect extends BaseComponent {
|
|
separation({agentNames, label}, env) {
|
|
const config = env.theme.connect;
|
|
|
|
const labelWidth = (
|
|
env.textSizer.measure(config.label.attrs, label).width +
|
|
config.label.padding * 2
|
|
);
|
|
|
|
const short = getArrowShort(env.theme);
|
|
|
|
const info1 = env.agentInfos.get(agentNames[0]);
|
|
if(agentNames[0] === agentNames[1]) {
|
|
env.addSpacing(agentNames[0], {
|
|
left: 0,
|
|
right: (
|
|
info1.currentMaxRad +
|
|
labelWidth +
|
|
config.arrow.width +
|
|
short +
|
|
config.loopbackRadius
|
|
),
|
|
});
|
|
} else {
|
|
const info2 = env.agentInfos.get(agentNames[1]);
|
|
env.addSeparation(
|
|
agentNames[0],
|
|
agentNames[1],
|
|
|
|
info1.currentMaxRad +
|
|
info2.currentMaxRad +
|
|
labelWidth +
|
|
config.arrow.width * 2 +
|
|
short * 2
|
|
);
|
|
}
|
|
}
|
|
|
|
renderSelfConnect({label, agentNames, options}, env) {
|
|
const config = env.theme.connect;
|
|
const from = env.agentInfos.get(agentNames[0]);
|
|
|
|
const dx = config.arrow.width;
|
|
const dy = config.arrow.height / 2;
|
|
const short = getArrowShort(env.theme);
|
|
|
|
const height = (
|
|
env.textSizer.measureHeight(config.label.attrs, label) +
|
|
config.label.margin.top +
|
|
config.label.margin.bottom
|
|
);
|
|
|
|
const lineX = from.x + from.currentMaxRad;
|
|
const y0 = env.primaryY;
|
|
const x0 = (
|
|
lineX +
|
|
short +
|
|
dx +
|
|
config.label.padding
|
|
);
|
|
|
|
const renderedText = SVGShapes.renderBoxedText(label, {
|
|
x: x0 - config.mask.padding.left,
|
|
y: y0 - height + config.label.margin.top,
|
|
padding: config.mask.padding,
|
|
boxAttrs: config.mask.maskAttrs,
|
|
labelAttrs: config.label.loopbackAttrs,
|
|
boxLayer: env.maskLayer,
|
|
labelLayer: env.labelLayer,
|
|
SVGTextBlockClass: env.SVGTextBlockClass,
|
|
});
|
|
const r = config.loopbackRadius;
|
|
const x1 = (
|
|
x0 +
|
|
renderedText.width +
|
|
config.label.padding -
|
|
config.mask.padding.left -
|
|
config.mask.padding.right
|
|
);
|
|
const y1 = y0 + r * 2;
|
|
|
|
const space = short + dx / 2;
|
|
|
|
env.shapeLayer.appendChild(svg.make('path', Object.assign({
|
|
'd': (
|
|
'M ' + (lineX + (options.left ? space : 0)) + ' ' + y0 +
|
|
' L ' + x1 + ' ' + y0 +
|
|
' A ' + r + ' ' + r + ' 0 0 1 ' + x1 + ' ' + y1 +
|
|
' L ' + (lineX + (options.right ? space : 0)) + ' ' + y1
|
|
),
|
|
}, config.lineAttrs[options.line])));
|
|
|
|
if(options.left) {
|
|
drawHorizontalArrowHead(env.shapeLayer, {
|
|
x: lineX + short,
|
|
y: y0,
|
|
dx,
|
|
dy,
|
|
attrs: config.arrow.attrs,
|
|
});
|
|
}
|
|
|
|
if(options.right) {
|
|
drawHorizontalArrowHead(env.shapeLayer, {
|
|
x: lineX + short,
|
|
y: y1,
|
|
dx,
|
|
dy,
|
|
attrs: config.arrow.attrs,
|
|
});
|
|
}
|
|
|
|
return y1 + dy + env.theme.actionMargin;
|
|
}
|
|
|
|
renderSimpleConnect({label, agentNames, options}, env) {
|
|
const config = env.theme.connect;
|
|
const from = env.agentInfos.get(agentNames[0]);
|
|
const to = env.agentInfos.get(agentNames[1]);
|
|
|
|
const dx = config.arrow.width;
|
|
const dy = config.arrow.height / 2;
|
|
const dir = (from.x < to.x) ? 1 : -1;
|
|
const short = getArrowShort(env.theme);
|
|
|
|
const height = (
|
|
env.textSizer.measureHeight(config.label.attrs, label) +
|
|
config.label.margin.top +
|
|
config.label.margin.bottom
|
|
);
|
|
|
|
const x0 = from.x + from.currentMaxRad * dir;
|
|
const x1 = to.x - to.currentMaxRad * dir;
|
|
let y = env.primaryY;
|
|
|
|
SVGShapes.renderBoxedText(label, {
|
|
x: (x0 + x1) / 2,
|
|
y: y - height + config.label.margin.top,
|
|
padding: config.mask.padding,
|
|
boxAttrs: config.mask.maskAttrs,
|
|
labelAttrs: config.label.attrs,
|
|
boxLayer: env.maskLayer,
|
|
labelLayer: env.labelLayer,
|
|
SVGTextBlockClass: env.SVGTextBlockClass,
|
|
});
|
|
|
|
const space = short + dx / 2;
|
|
|
|
env.shapeLayer.appendChild(svg.make('line', Object.assign({
|
|
'x1': x0 + (options.left ? space : 0) * dir,
|
|
'y1': y,
|
|
'x2': x1 - (options.right ? space : 0) * dir,
|
|
'y2': y,
|
|
}, config.lineAttrs[options.line])));
|
|
|
|
if(options.left) {
|
|
drawHorizontalArrowHead(env.shapeLayer, {
|
|
x: x0 + short * dir,
|
|
y,
|
|
dx: dx * dir,
|
|
dy,
|
|
attrs: config.arrow.attrs,
|
|
});
|
|
}
|
|
|
|
if(options.right) {
|
|
drawHorizontalArrowHead(env.shapeLayer, {
|
|
x: x1 - short * dir,
|
|
y,
|
|
dx: -dx * dir,
|
|
dy,
|
|
attrs: config.arrow.attrs,
|
|
});
|
|
}
|
|
|
|
return y + dy + env.theme.actionMargin;
|
|
}
|
|
|
|
renderPre({label, agentNames}, env) {
|
|
const config = env.theme.connect;
|
|
|
|
const height = (
|
|
env.textSizer.measureHeight(config.label.attrs, label) +
|
|
config.label.margin.top +
|
|
config.label.margin.bottom
|
|
);
|
|
|
|
return {
|
|
agentNames,
|
|
topShift: Math.max(config.arrow.height / 2, height),
|
|
};
|
|
}
|
|
|
|
render(stage, env) {
|
|
if(stage.agentNames[0] === stage.agentNames[1]) {
|
|
return this.renderSelfConnect(stage, env);
|
|
} else {
|
|
return this.renderSimpleConnect(stage, env);
|
|
}
|
|
}
|
|
}
|
|
|
|
BaseComponent.register('connect', new Connect());
|
|
|
|
return Connect;
|
|
});
|