Support rendering conditional boxes [#1]
This commit is contained in:
parent
5ecce9a6ab
commit
1929e3ffb1
|
@ -36,6 +36,7 @@ define(() => {
|
|||
return class Generator {
|
||||
constructor() {
|
||||
this.agentStates = new Map();
|
||||
this.agents = [];
|
||||
this.blockCount = 0;
|
||||
this.nesting = [];
|
||||
this.currentSection = null;
|
||||
|
@ -52,18 +53,13 @@ define(() => {
|
|||
this.handleStage = this.handleStage.bind(this);
|
||||
}
|
||||
|
||||
addStage(stage) {
|
||||
this.currentSection.stages.push(stage);
|
||||
mergeSets(this.currentNest.agents, stage.agents);
|
||||
}
|
||||
|
||||
addColumnBounds(target, agentL, agentR, involvedAgents = []) {
|
||||
addBounds(target, agentL, agentR, involvedAgents = null) {
|
||||
removeElement(target, agentL);
|
||||
removeElement(target, agentR);
|
||||
|
||||
let indexL = 0;
|
||||
let indexR = target.length;
|
||||
if(involvedAgents.length > 0) {
|
||||
if(involvedAgents) {
|
||||
const found = (involvedAgents
|
||||
.map((agent) => target.indexOf(agent))
|
||||
.filter((p) => (p !== -1))
|
||||
|
@ -103,18 +99,21 @@ define(() => {
|
|||
const existing = lastElement(this.currentSection.stages) || {};
|
||||
if(existing.type === type && existing.mode === mode) {
|
||||
mergeSets(existing.agents, filteredAgents);
|
||||
mergeSets(this.currentNest.agents, filteredAgents);
|
||||
} else {
|
||||
this.addStage({
|
||||
this.currentSection.stages.push({
|
||||
type,
|
||||
agents: filteredAgents,
|
||||
mode,
|
||||
});
|
||||
}
|
||||
mergeSets(this.currentNest.agents, filteredAgents);
|
||||
mergeSets(this.agents, filteredAgents);
|
||||
}
|
||||
|
||||
beginNested(mode, label, name) {
|
||||
const agents = [];
|
||||
const nameL = name + '[';
|
||||
const nameR = name + ']';
|
||||
const agents = [nameL, nameR];
|
||||
const stages = [];
|
||||
this.currentSection = {
|
||||
mode,
|
||||
|
@ -122,20 +121,24 @@ define(() => {
|
|||
stages,
|
||||
};
|
||||
this.currentNest = {
|
||||
type: 'block',
|
||||
agents,
|
||||
sections: [this.currentSection],
|
||||
leftColumn: name + '[',
|
||||
rightColumn: name + ']',
|
||||
stage: {
|
||||
type: 'block',
|
||||
sections: [this.currentSection],
|
||||
left: nameL,
|
||||
right: nameR,
|
||||
},
|
||||
};
|
||||
this.agentStates.set(name + '[', LOCKED_AGENT);
|
||||
this.agentStates.set(name + ']', LOCKED_AGENT);
|
||||
this.agentStates.set(nameL, LOCKED_AGENT);
|
||||
this.agentStates.set(nameR, LOCKED_AGENT);
|
||||
this.nesting.push(this.currentNest);
|
||||
|
||||
return {agents, stages};
|
||||
}
|
||||
|
||||
handleAgentDefine() {
|
||||
handleAgentDefine({agents}) {
|
||||
mergeSets(this.currentNest.agents, agents);
|
||||
mergeSets(this.agents, agents);
|
||||
}
|
||||
|
||||
handleAgentBegin({agents, mode}) {
|
||||
|
@ -153,7 +156,7 @@ define(() => {
|
|||
}
|
||||
|
||||
handleBlockSplit({mode, label}) {
|
||||
if(this.currentNest.sections[0].mode !== 'if') {
|
||||
if(this.currentNest.stage.sections[0].mode !== 'if') {
|
||||
throw new Error('Invalid block nesting');
|
||||
}
|
||||
this.currentSection = {
|
||||
|
@ -161,35 +164,34 @@ define(() => {
|
|||
label,
|
||||
stages: [],
|
||||
};
|
||||
this.currentNest.sections.push(this.currentSection);
|
||||
this.currentNest.stage.sections.push(this.currentSection);
|
||||
}
|
||||
|
||||
handleBlockEnd() {
|
||||
if(this.nesting.length <= 1) {
|
||||
throw new Error('Invalid block nesting');
|
||||
}
|
||||
const subNest = this.nesting.pop();
|
||||
const {stage, agents} = this.nesting.pop();
|
||||
this.currentNest = lastElement(this.nesting);
|
||||
this.currentSection = lastElement(this.currentNest.sections);
|
||||
if(subNest.agents.length > 0) {
|
||||
this.addStage(subNest);
|
||||
this.addColumnBounds(
|
||||
this.currentNest.agents,
|
||||
subNest.leftColumn,
|
||||
subNest.rightColumn,
|
||||
subNest.agents
|
||||
);
|
||||
this.addColumnBounds(
|
||||
subNest.agents,
|
||||
subNest.leftColumn,
|
||||
subNest.rightColumn
|
||||
this.currentSection = lastElement(this.currentNest.stage.sections);
|
||||
if(stage.sections.some((section) => section.stages.length > 0)) {
|
||||
mergeSets(this.currentNest.agents, agents);
|
||||
mergeSets(this.agents, agents);
|
||||
this.addBounds(
|
||||
this.agents,
|
||||
stage.left,
|
||||
stage.right,
|
||||
agents
|
||||
);
|
||||
this.currentSection.stages.push(stage);
|
||||
}
|
||||
}
|
||||
|
||||
handleUnknownStage(stage) {
|
||||
this.setAgentVis(stage.agents, true, 'box');
|
||||
this.addStage(stage);
|
||||
this.currentSection.stages.push(stage);
|
||||
mergeSets(this.currentNest.agents, stage.agents);
|
||||
mergeSets(this.agents, stage.agents);
|
||||
}
|
||||
|
||||
handleStage(stage) {
|
||||
|
@ -203,6 +205,7 @@ define(() => {
|
|||
|
||||
generate({stages, meta = {}}) {
|
||||
this.agentStates.clear();
|
||||
this.agents.length = 0;
|
||||
this.blockCount = 0;
|
||||
this.nesting.length = 0;
|
||||
const globals = this.beginNested('global', '', '');
|
||||
|
@ -213,19 +216,19 @@ define(() => {
|
|||
throw new Error('Invalid block nesting');
|
||||
}
|
||||
|
||||
this.setAgentVis(globals.agents, false, meta.terminators || 'none');
|
||||
this.setAgentVis(this.agents, false, meta.terminators || 'none');
|
||||
|
||||
this.addColumnBounds(
|
||||
globals.agents,
|
||||
this.currentNest.leftColumn,
|
||||
this.currentNest.rightColumn
|
||||
this.addBounds(
|
||||
this.agents,
|
||||
this.currentNest.stage.left,
|
||||
this.currentNest.stage.right
|
||||
);
|
||||
|
||||
return {
|
||||
meta: {
|
||||
title: meta.title,
|
||||
},
|
||||
agents: globals.agents,
|
||||
agents: this.agents,
|
||||
stages: globals.stages,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -51,6 +51,14 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
|||
expect(sequence.agents).toEqual(['[', 'B', ']']);
|
||||
});
|
||||
|
||||
it('accounts for define calls when ordering agents', () => {
|
||||
const sequence = generator.generate({stages: [
|
||||
{type: AGENT_DEFINE, agents: ['B']},
|
||||
{type: '->', agents: ['A', 'B']},
|
||||
]});
|
||||
expect(sequence.agents).toEqual(['[', 'B', 'A', ']']);
|
||||
});
|
||||
|
||||
it('creates implicit begin stages for agents when used', () => {
|
||||
const sequence = generator.generate({stages: [
|
||||
{type: '->', agents: ['A', 'B']},
|
||||
|
@ -216,7 +224,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
|||
]);
|
||||
});
|
||||
|
||||
it('records virtual block column names in blocks', () => {
|
||||
it('records virtual block agent names in blocks', () => {
|
||||
const sequence = generator.generate({stages: [
|
||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||
{type: '->', agents: ['A', 'B']},
|
||||
|
@ -225,23 +233,8 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
|||
|
||||
const block0 = sequence.stages[0];
|
||||
expect(block0.type).toEqual('block');
|
||||
expect(block0.leftColumn).toEqual('__BLOCK0[');
|
||||
expect(block0.rightColumn).toEqual('__BLOCK0]');
|
||||
});
|
||||
|
||||
it('records all involved agents in blocks', () => {
|
||||
const sequence = generator.generate({stages: [
|
||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||
{type: '->', agents: ['A', 'B']},
|
||||
{type: BLOCK_SPLIT, mode: 'else', label: 'xyz'},
|
||||
{type: '->', agents: ['A', 'C']},
|
||||
{type: BLOCK_END},
|
||||
]});
|
||||
|
||||
const block0 = sequence.stages[0];
|
||||
expect(block0.agents).toEqual(
|
||||
['__BLOCK0[', 'A', 'B', 'C', '__BLOCK0]']
|
||||
);
|
||||
expect(block0.left).toEqual('__BLOCK0[');
|
||||
expect(block0.right).toEqual('__BLOCK0]');
|
||||
});
|
||||
|
||||
it('records all sections within blocks', () => {
|
||||
|
@ -277,9 +270,8 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
|||
{type: BLOCK_END},
|
||||
]});
|
||||
|
||||
const block0 = sequence.stages[0];
|
||||
expect(block0.type).toEqual('block');
|
||||
expect(block0.agents).toEqual([
|
||||
expect(sequence.agents).toEqual([
|
||||
'[',
|
||||
'__BLOCK0[',
|
||||
'__BLOCK1[',
|
||||
'A',
|
||||
|
@ -287,20 +279,48 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
|||
'C',
|
||||
'__BLOCK1]',
|
||||
'__BLOCK0]',
|
||||
']',
|
||||
]);
|
||||
expect(block0.leftColumn).toEqual('__BLOCK0[');
|
||||
expect(block0.rightColumn).toEqual('__BLOCK0]');
|
||||
const block0 = sequence.stages[0];
|
||||
expect(block0.type).toEqual('block');
|
||||
expect(block0.left).toEqual('__BLOCK0[');
|
||||
expect(block0.right).toEqual('__BLOCK0]');
|
||||
|
||||
const block1 = block0.sections[1].stages[0];
|
||||
expect(block1.type).toEqual('block');
|
||||
expect(block1.agents).toEqual([
|
||||
expect(block1.left).toEqual('__BLOCK1[');
|
||||
expect(block1.right).toEqual('__BLOCK1]');
|
||||
});
|
||||
|
||||
it('preserves block boundaries when agents exist outside', () => {
|
||||
const sequence = generator.generate({stages: [
|
||||
{type: '->', agents: ['A', 'B']},
|
||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||
{type: BLOCK_BEGIN, mode: 'if', label: 'def'},
|
||||
{type: '->', agents: ['A', 'B']},
|
||||
{type: BLOCK_END},
|
||||
{type: BLOCK_END},
|
||||
]});
|
||||
|
||||
expect(sequence.agents).toEqual([
|
||||
'[',
|
||||
'__BLOCK0[',
|
||||
'__BLOCK1[',
|
||||
'C',
|
||||
'A',
|
||||
'B',
|
||||
'__BLOCK1]',
|
||||
'__BLOCK0]',
|
||||
']',
|
||||
]);
|
||||
expect(block1.leftColumn).toEqual('__BLOCK1[');
|
||||
expect(block1.rightColumn).toEqual('__BLOCK1]');
|
||||
const block0 = sequence.stages[2];
|
||||
expect(block0.type).toEqual('block');
|
||||
expect(block0.left).toEqual('__BLOCK0[');
|
||||
expect(block0.right).toEqual('__BLOCK0]');
|
||||
|
||||
const block1 = block0.sections[0].stages[0];
|
||||
expect(block1.type).toEqual('block');
|
||||
expect(block1.left).toEqual('__BLOCK1[');
|
||||
expect(block1.right).toEqual('__BLOCK1]');
|
||||
});
|
||||
|
||||
it('allows empty block parts after split', () => {
|
||||
|
@ -351,6 +371,16 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
|||
expect(sequence.stages).toEqual([]);
|
||||
});
|
||||
|
||||
it('removes blocks which only contain define statements', () => {
|
||||
const sequence = generator.generate({stages: [
|
||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||
{type: AGENT_DEFINE, agents: ['A']},
|
||||
{type: BLOCK_END},
|
||||
]});
|
||||
|
||||
expect(sequence.stages).toEqual([]);
|
||||
});
|
||||
|
||||
it('does not create virtual agents for empty blocks', () => {
|
||||
const sequence = generator.generate({stages: [
|
||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||
|
|
|
@ -48,8 +48,32 @@ define(() => {
|
|||
const CONNECT_POINT = 4;
|
||||
const CONNECT_LABEL_PADDING = 6;
|
||||
const CONNECT_LABEL_MASK_PADDING = 3;
|
||||
const CONNECT_LABEL_MARGIN_TOP = 2;
|
||||
const CONNECT_LABEL_MARGIN_BOTTOM = 1;
|
||||
const CONNECT_LABEL_MARGIN = {
|
||||
top: 2,
|
||||
bottom: 1,
|
||||
};
|
||||
const BLOCK_MARGIN = {
|
||||
top: 5,
|
||||
bottom: 5,
|
||||
};
|
||||
const BLOCK_SECTION_PADDING = {
|
||||
top: 1,
|
||||
bottom: 5,
|
||||
};
|
||||
const BLOCK_MODE_PADDING = {
|
||||
top: 1,
|
||||
left: 3,
|
||||
right: 3,
|
||||
bottom: 0,
|
||||
};
|
||||
const BLOCK_LABEL_PADDING = {
|
||||
left: 5,
|
||||
right: 5,
|
||||
};
|
||||
const BLOCK_LABEL_MASK_PADDING = {
|
||||
left: 3,
|
||||
right: 3,
|
||||
};
|
||||
|
||||
const ATTRS = {
|
||||
TITLE: {
|
||||
|
@ -85,6 +109,40 @@ define(() => {
|
|||
'height': 5,
|
||||
},
|
||||
|
||||
BLOCK_BOX: {
|
||||
'fill': 'none',
|
||||
'stroke': '#000000',
|
||||
'stroke-width': 1.5,
|
||||
'rx': 2,
|
||||
'ry': 2,
|
||||
},
|
||||
BLOCK_SEPARATOR: {
|
||||
'stroke': '#000000',
|
||||
'stroke-width': 1.5,
|
||||
'stroke-dasharray': '4, 2',
|
||||
},
|
||||
BLOCK_MODE: {
|
||||
'fill': '#FFFFFF',
|
||||
'stroke': '#000000',
|
||||
'stroke-width': 1,
|
||||
'rx': 2,
|
||||
'ry': 2,
|
||||
},
|
||||
BLOCK_MODE_LABEL: {
|
||||
'font-family': 'sans-serif',
|
||||
'font-weight': 'bold',
|
||||
'font-size': 9,
|
||||
'text-anchor': 'left',
|
||||
},
|
||||
BLOCK_LABEL: {
|
||||
'font-family': 'sans-serif',
|
||||
'font-size': 8,
|
||||
'text-anchor': 'left',
|
||||
},
|
||||
BLOCK_LABEL_MASK: {
|
||||
'fill': '#FFFFFF',
|
||||
},
|
||||
|
||||
CONNECT_LINE_SOLID: {
|
||||
'fill': 'none',
|
||||
'stroke': '#000000',
|
||||
|
@ -94,7 +152,7 @@ define(() => {
|
|||
'fill': 'none',
|
||||
'stroke': '#000000',
|
||||
'stroke-width': 1,
|
||||
'stroke-dasharray': '2, 2',
|
||||
'stroke-dasharray': '4, 2',
|
||||
},
|
||||
CONNECT_LABEL: {
|
||||
'font-family': 'sans-serif',
|
||||
|
@ -126,13 +184,27 @@ define(() => {
|
|||
return o;
|
||||
}
|
||||
|
||||
function traverse(stages, fn) {
|
||||
function traverse(stages, callbacks) {
|
||||
stages.forEach((stage) => {
|
||||
fn(stage);
|
||||
if(stage.type === 'block') {
|
||||
const scope = {};
|
||||
if(callbacks.blockBeginFn) {
|
||||
callbacks.blockBeginFn(scope, stage);
|
||||
}
|
||||
stage.sections.forEach((section) => {
|
||||
traverse(section.stages, fn);
|
||||
if(callbacks.sectionBeginFn) {
|
||||
callbacks.sectionBeginFn(scope, stage, section);
|
||||
}
|
||||
traverse(section.stages, callbacks);
|
||||
if(callbacks.sectionEndFn) {
|
||||
callbacks.sectionEndFn(scope, stage, section);
|
||||
}
|
||||
});
|
||||
if(callbacks.blockEndFn) {
|
||||
callbacks.blockEndFn(scope, stage);
|
||||
}
|
||||
} else if(callbacks.stageFn) {
|
||||
callbacks.stageFn(stage);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -155,9 +227,13 @@ define(() => {
|
|||
|
||||
this.diagram = makeSVGNode('g');
|
||||
this.agentLines = makeSVGNode('g');
|
||||
this.blocks = makeSVGNode('g');
|
||||
this.sections = makeSVGNode('g');
|
||||
this.agentDecor = makeSVGNode('g');
|
||||
this.actions = makeSVGNode('g');
|
||||
this.diagram.appendChild(this.agentLines);
|
||||
this.diagram.appendChild(this.blocks);
|
||||
this.diagram.appendChild(this.sections);
|
||||
this.diagram.appendChild(this.agentDecor);
|
||||
this.diagram.appendChild(this.actions);
|
||||
this.base.appendChild(this.diagram);
|
||||
|
@ -173,7 +249,6 @@ define(() => {
|
|||
'agent begin': this.separationAgent.bind(this),
|
||||
'agent end': this.separationAgent.bind(this),
|
||||
'connection': this.separationConnection.bind(this),
|
||||
'block': this.separationBlock.bind(this),
|
||||
'note over': this.separationNoteOver.bind(this),
|
||||
'note left': this.separationNoteLeft.bind(this),
|
||||
'note right': this.separationNoteRight.bind(this),
|
||||
|
@ -191,13 +266,27 @@ define(() => {
|
|||
'agent begin': this.renderAgentBegin.bind(this),
|
||||
'agent end': this.renderAgentEnd.bind(this),
|
||||
'connection': this.renderConnection.bind(this),
|
||||
'block': this.renderBlock.bind(this),
|
||||
'note over': this.renderNoteOver.bind(this),
|
||||
'note left': this.renderNoteLeft.bind(this),
|
||||
'note right': this.renderNoteRight.bind(this),
|
||||
'note between': this.renderNoteBetween.bind(this),
|
||||
};
|
||||
|
||||
this.separationTraversalFns = {
|
||||
stageFn: this.checkSeparation.bind(this),
|
||||
blockBeginFn: this.separationBlockBegin.bind(this),
|
||||
sectionBeginFn: this.separationSectionBegin.bind(this),
|
||||
blockEndFn: this.separationBlockEnd.bind(this),
|
||||
};
|
||||
|
||||
this.renderTraversalFns = {
|
||||
stageFn: this.addAction.bind(this),
|
||||
blockBeginFn: this.renderBlockBegin.bind(this),
|
||||
sectionBeginFn: this.renderSectionBegin.bind(this),
|
||||
sectionEndFn: this.renderSectionEnd.bind(this),
|
||||
blockEndFn: this.renderBlockEnd.bind(this),
|
||||
};
|
||||
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
}
|
||||
|
@ -280,17 +369,13 @@ define(() => {
|
|||
agents[0],
|
||||
agents[1],
|
||||
|
||||
this.testTextWidth(this.testConnectWidth, label) +
|
||||
this.testTextWidth(this.testConnect, label) +
|
||||
CONNECT_POINT * 2 +
|
||||
CONNECT_LABEL_PADDING * 2 +
|
||||
ATTRS.AGENT_LINE['stroke-width']
|
||||
);
|
||||
}
|
||||
|
||||
separationBlock(/*stage*/) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
separationNoteOver(/*stage*/) {
|
||||
// TODO
|
||||
}
|
||||
|
@ -307,6 +392,25 @@ define(() => {
|
|||
// TODO
|
||||
}
|
||||
|
||||
separationBlockBegin(scope, {left, right}) {
|
||||
mergeSets(this.visibleAgents, [left, right]);
|
||||
this.addSeparations(this.visibleAgents, new Map());
|
||||
}
|
||||
|
||||
separationSectionBegin(scope, {left, right}, {mode, label}) {
|
||||
const width = (
|
||||
this.testTextWidth(this.testBlockMode, mode) +
|
||||
BLOCK_MODE_PADDING.left + BLOCK_MODE_PADDING.right +
|
||||
this.testTextWidth(this.testBlockLabel, label) +
|
||||
BLOCK_LABEL_PADDING.left + BLOCK_LABEL_PADDING.right
|
||||
);
|
||||
this.addSeparation(left, right, width);
|
||||
}
|
||||
|
||||
separationBlockEnd(scope, {left, right}) {
|
||||
removeAll(this.visibleAgents, [left, right]);
|
||||
}
|
||||
|
||||
checkSeparation(stage) {
|
||||
this.separationAction[stage.type](stage);
|
||||
}
|
||||
|
@ -430,16 +534,16 @@ define(() => {
|
|||
this.actions.appendChild(labelNode);
|
||||
y += Math.max(
|
||||
dy,
|
||||
CONNECT_LABEL_MARGIN_TOP +
|
||||
CONNECT_LABEL_MARGIN.top +
|
||||
sz * LINE_HEIGHT +
|
||||
CONNECT_LABEL_MARGIN_BOTTOM
|
||||
CONNECT_LABEL_MARGIN.bottom
|
||||
);
|
||||
const w = labelNode.getComputedTextLength();
|
||||
const x = (from.x + to.x) / 2;
|
||||
const yBase = (
|
||||
y -
|
||||
sz * (LINE_HEIGHT - 1) -
|
||||
CONNECT_LABEL_MARGIN_BOTTOM
|
||||
CONNECT_LABEL_MARGIN.bottom
|
||||
);
|
||||
labelNode.setAttribute('x', x);
|
||||
labelNode.setAttribute('y', yBase);
|
||||
|
@ -483,10 +587,6 @@ define(() => {
|
|||
this.currentY = y + dy + ACTION_MARGIN;
|
||||
}
|
||||
|
||||
renderBlock(/*stage*/) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
renderNoteOver(/*stage*/) {
|
||||
// TODO
|
||||
}
|
||||
|
@ -503,6 +603,106 @@ define(() => {
|
|||
// TODO
|
||||
}
|
||||
|
||||
renderBlockBegin(scope) {
|
||||
this.currentY += BLOCK_MARGIN.top;
|
||||
|
||||
scope.y = this.currentY;
|
||||
scope.first = true;
|
||||
}
|
||||
|
||||
renderSectionBegin(scope, {left, right}, {mode, label}) {
|
||||
const agentInfoL = this.agentInfos.get(left);
|
||||
const agentInfoR = this.agentInfos.get(right);
|
||||
|
||||
if(scope.first) {
|
||||
scope.first = false;
|
||||
} else {
|
||||
this.currentY += BLOCK_SECTION_PADDING.bottom;
|
||||
this.sections.appendChild(makeSVGNode('path', Object.assign({
|
||||
'd': (
|
||||
'M' + agentInfoL.x + ' ' + this.currentY +
|
||||
' L' + agentInfoR.x + ' ' + this.currentY
|
||||
),
|
||||
}, ATTRS.BLOCK_SEPARATOR)));
|
||||
}
|
||||
|
||||
let x = agentInfoL.x;
|
||||
if(mode) {
|
||||
const sz = ATTRS.BLOCK_MODE_LABEL['font-size'];
|
||||
const modeBox = makeSVGNode('rect', Object.assign({
|
||||
'x': x,
|
||||
'y': this.currentY,
|
||||
'height': (
|
||||
sz * LINE_HEIGHT +
|
||||
BLOCK_MODE_PADDING.top +
|
||||
BLOCK_MODE_PADDING.bottom
|
||||
),
|
||||
}, ATTRS.BLOCK_MODE));
|
||||
const modeLabel = makeSVGNode('text', Object.assign({
|
||||
'x': x + BLOCK_MODE_PADDING.left,
|
||||
'y': (
|
||||
this.currentY + sz +
|
||||
BLOCK_MODE_PADDING.top
|
||||
),
|
||||
}, ATTRS.BLOCK_MODE_LABEL));
|
||||
modeLabel.appendChild(makeText(mode));
|
||||
this.blocks.appendChild(modeBox);
|
||||
this.actions.appendChild(modeLabel);
|
||||
const w = (
|
||||
modeLabel.getComputedTextLength() +
|
||||
BLOCK_MODE_PADDING.left +
|
||||
BLOCK_MODE_PADDING.right
|
||||
);
|
||||
modeBox.setAttribute('width', w);
|
||||
x += w;
|
||||
|
||||
this.currentY += sz * LINE_HEIGHT;
|
||||
}
|
||||
|
||||
if(label) {
|
||||
x += BLOCK_LABEL_PADDING.left;
|
||||
const sz = ATTRS.BLOCK_LABEL['font-size'];
|
||||
const mask = makeSVGNode('rect', Object.assign({
|
||||
'x': x - BLOCK_LABEL_MASK_PADDING.left,
|
||||
'y': this.currentY - sz * LINE_HEIGHT,
|
||||
'height': sz * LINE_HEIGHT,
|
||||
}, ATTRS.BLOCK_LABEL_MASK));
|
||||
const labelLabel = makeSVGNode('text', Object.assign({
|
||||
'x': x,
|
||||
'y': this.currentY - sz * (LINE_HEIGHT - 1),
|
||||
}, ATTRS.BLOCK_LABEL));
|
||||
labelLabel.appendChild(makeText(label));
|
||||
this.actions.appendChild(mask);
|
||||
this.actions.appendChild(labelLabel);
|
||||
const w = (
|
||||
labelLabel.getComputedTextLength() +
|
||||
BLOCK_LABEL_MASK_PADDING.left +
|
||||
BLOCK_LABEL_MASK_PADDING.right
|
||||
);
|
||||
mask.setAttribute('width', w);
|
||||
}
|
||||
|
||||
this.currentY += BLOCK_SECTION_PADDING.top;
|
||||
}
|
||||
|
||||
renderSectionEnd(/*scope, block, section*/) {
|
||||
}
|
||||
|
||||
renderBlockEnd(scope, {left, right}) {
|
||||
this.currentY += BLOCK_SECTION_PADDING.bottom;
|
||||
|
||||
const agentInfoL = this.agentInfos.get(left);
|
||||
const agentInfoR = this.agentInfos.get(right);
|
||||
this.blocks.appendChild(makeSVGNode('rect', Object.assign({
|
||||
'x': agentInfoL.x,
|
||||
'y': scope.y,
|
||||
'width': agentInfoR.x - agentInfoL.x,
|
||||
'height': this.currentY - scope.y,
|
||||
}, ATTRS.BLOCK_BOX)));
|
||||
|
||||
this.currentY += BLOCK_MARGIN.bottom;
|
||||
}
|
||||
|
||||
addAction(stage) {
|
||||
this.renderAction[stage.type](stage);
|
||||
}
|
||||
|
@ -525,14 +725,14 @@ define(() => {
|
|||
}
|
||||
|
||||
buildAgentInfos(agents, stages) {
|
||||
const testNameWidth = this.makeTextTester(ATTRS.AGENT_BOX_LABEL);
|
||||
const testName = this.makeTextTester(ATTRS.AGENT_BOX_LABEL);
|
||||
|
||||
this.agentInfos = new Map();
|
||||
agents.forEach((agent, index) => {
|
||||
this.agentInfos.set(agent, {
|
||||
label: agent,
|
||||
labelWidth: (
|
||||
this.testTextWidth(testNameWidth, agent) +
|
||||
this.testTextWidth(testName, agent) +
|
||||
BOX_PADDING * 2
|
||||
),
|
||||
index,
|
||||
|
@ -544,12 +744,16 @@ define(() => {
|
|||
this.agentInfos.get('[').labelWidth = 0;
|
||||
this.agentInfos.get(']').labelWidth = 0;
|
||||
|
||||
this.removeTextTester(testNameWidth);
|
||||
this.removeTextTester(testName);
|
||||
|
||||
this.testConnectWidth = this.makeTextTester(ATTRS.CONNECT_LABEL);
|
||||
this.testConnect = this.makeTextTester(ATTRS.CONNECT_LABEL);
|
||||
this.testBlockMode = this.makeTextTester(ATTRS.BLOCK_MODE_LABEL);
|
||||
this.testBlockLabel = this.makeTextTester(ATTRS.BLOCK_LABEL);
|
||||
this.visibleAgents = ['[', ']'];
|
||||
traverse(stages, this.checkSeparation.bind(this));
|
||||
this.removeTextTester(this.testConnectWidth);
|
||||
traverse(stages, this.separationTraversalFns);
|
||||
this.removeTextTester(this.testConnect);
|
||||
this.removeTextTester(this.testBlockMode);
|
||||
this.removeTextTester(this.testBlockLabel);
|
||||
|
||||
agents.forEach((agent) => {
|
||||
const agentInfo = this.agentInfos.get(agent);
|
||||
|
@ -597,6 +801,8 @@ define(() => {
|
|||
|
||||
render({meta, agents, stages}) {
|
||||
empty(this.agentLines);
|
||||
empty(this.blocks);
|
||||
empty(this.sections);
|
||||
empty(this.agentDecor);
|
||||
empty(this.actions);
|
||||
|
||||
|
@ -607,12 +813,12 @@ define(() => {
|
|||
this.buildAgentInfos(agents, stages);
|
||||
|
||||
this.currentY = 0;
|
||||
traverse(stages, this.addAction.bind(this));
|
||||
traverse(stages, this.renderTraversalFns);
|
||||
|
||||
this.updateBounds(Math.max(this.currentY - ACTION_MARGIN, 0));
|
||||
}
|
||||
|
||||
getColumnX(name) {
|
||||
getAgentX(name) {
|
||||
return this.agentInfos.get(name).x;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ defineDescribe('Sequence Renderer', ['./Renderer'], (Renderer) => {
|
|||
expect(title.innerHTML).toEqual('Title');
|
||||
});
|
||||
|
||||
it('positions column lines', () => {
|
||||
it('positions agent lines', () => {
|
||||
/*
|
||||
A -> B
|
||||
*/
|
||||
|
@ -61,10 +61,10 @@ defineDescribe('Sequence Renderer', ['./Renderer'], (Renderer) => {
|
|||
const line = element.getElementsByClassName('agent-1-line')[0];
|
||||
const drawnX = Number(line.getAttribute('d').split(' ')[1]);
|
||||
|
||||
expect(drawnX).toEqual(renderer.getColumnX('A'));
|
||||
expect(drawnX).toEqual(renderer.getAgentX('A'));
|
||||
});
|
||||
|
||||
it('arranges columns left-to-right', () => {
|
||||
it('arranges agents left-to-right', () => {
|
||||
/*
|
||||
[ -> A
|
||||
A -> B
|
||||
|
@ -85,11 +85,11 @@ defineDescribe('Sequence Renderer', ['./Renderer'], (Renderer) => {
|
|||
],
|
||||
});
|
||||
|
||||
const xL = renderer.getColumnX('[');
|
||||
const xA = renderer.getColumnX('A');
|
||||
const xB = renderer.getColumnX('B');
|
||||
const xC = renderer.getColumnX('C');
|
||||
const xR = renderer.getColumnX(']');
|
||||
const xL = renderer.getAgentX('[');
|
||||
const xA = renderer.getAgentX('A');
|
||||
const xB = renderer.getAgentX('B');
|
||||
const xC = renderer.getAgentX('C');
|
||||
const xR = renderer.getAgentX(']');
|
||||
|
||||
expect(xA).toBeGreaterThan(xL);
|
||||
expect(xB).toBeGreaterThan(xA);
|
||||
|
@ -97,7 +97,7 @@ defineDescribe('Sequence Renderer', ['./Renderer'], (Renderer) => {
|
|||
expect(xR).toBeGreaterThan(xC);
|
||||
});
|
||||
|
||||
it('allows column reordering for mutually-exclusive columns', () => {
|
||||
it('allows agent reordering for mutually-exclusive agents', () => {
|
||||
/*
|
||||
A -> B: short
|
||||
end B
|
||||
|
@ -123,10 +123,10 @@ defineDescribe('Sequence Renderer', ['./Renderer'], (Renderer) => {
|
|||
],
|
||||
});
|
||||
|
||||
const xA = renderer.getColumnX('A');
|
||||
const xB = renderer.getColumnX('B');
|
||||
const xC = renderer.getColumnX('C');
|
||||
const xD = renderer.getColumnX('D');
|
||||
const xA = renderer.getAgentX('A');
|
||||
const xB = renderer.getAgentX('B');
|
||||
const xC = renderer.getAgentX('C');
|
||||
const xD = renderer.getAgentX('D');
|
||||
|
||||
expect(xB).toBeGreaterThan(xA);
|
||||
expect(xC).toBeGreaterThan(xA);
|
||||
|
|
Loading…
Reference in New Issue