Add found messages and fix minor rendering issue with groups containing arrows to sides [#37]
This commit is contained in:
parent
b8491e25dc
commit
394dcb0e42
|
@ -645,8 +645,13 @@ define('sequence/CodeMirrorMode',['core/ArrayUtilities'], (array) => {
|
||||||
)};
|
)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const colonTextToEnd = {
|
||||||
|
type: 'operator',
|
||||||
|
suggest: true,
|
||||||
|
then: {'': textToEnd, '\n': hiddenEnd},
|
||||||
|
};
|
||||||
const agentListToText = agentListTo({
|
const agentListToText = agentListTo({
|
||||||
':': {type: 'operator', suggest: true, then: {'': textToEnd}},
|
':': colonTextToEnd,
|
||||||
});
|
});
|
||||||
const agentList2ToText = {type: 'variable', suggest: 'Agent', then: {
|
const agentList2ToText = {type: 'variable', suggest: 'Agent', then: {
|
||||||
'': 0,
|
'': 0,
|
||||||
|
@ -656,7 +661,7 @@ define('sequence/CodeMirrorMode',['core/ArrayUtilities'], (array) => {
|
||||||
const singleAgentToText = {type: 'variable', suggest: 'Agent', then: {
|
const singleAgentToText = {type: 'variable', suggest: 'Agent', then: {
|
||||||
'': 0,
|
'': 0,
|
||||||
',': CM_ERROR,
|
',': CM_ERROR,
|
||||||
':': {type: 'operator', suggest: true, then: {'': textToEnd}},
|
':': colonTextToEnd,
|
||||||
}};
|
}};
|
||||||
const agentToOptText = {type: 'variable', suggest: 'Agent', then: {
|
const agentToOptText = {type: 'variable', suggest: 'Agent', then: {
|
||||||
'': 0,
|
'': 0,
|
||||||
|
@ -700,7 +705,7 @@ define('sequence/CodeMirrorMode',['core/ArrayUtilities'], (array) => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeOpBlock(exit) {
|
function makeOpBlock(exit, sourceExit) {
|
||||||
const op = {type: 'operator', suggest: true, then: {
|
const op = {type: 'operator', suggest: true, then: {
|
||||||
'+': CM_ERROR,
|
'+': CM_ERROR,
|
||||||
'-': CM_ERROR,
|
'-': CM_ERROR,
|
||||||
|
@ -729,13 +734,13 @@ define('sequence/CodeMirrorMode',['core/ArrayUtilities'], (array) => {
|
||||||
}},
|
}},
|
||||||
'': exit,
|
'': exit,
|
||||||
}},
|
}},
|
||||||
'*': {type: 'operator', suggest: true, then: {
|
'*': {type: 'operator', suggest: true, then: Object.assign({
|
||||||
'+': op,
|
'+': op,
|
||||||
'-': op,
|
'-': op,
|
||||||
'*': CM_ERROR,
|
'*': CM_ERROR,
|
||||||
'!': CM_ERROR,
|
'!': CM_ERROR,
|
||||||
'': exit,
|
'': exit,
|
||||||
}},
|
}, sourceExit)},
|
||||||
'!': op,
|
'!': op,
|
||||||
'': exit,
|
'': exit,
|
||||||
};
|
};
|
||||||
|
@ -745,7 +750,10 @@ define('sequence/CodeMirrorMode',['core/ArrayUtilities'], (array) => {
|
||||||
const connect = {
|
const connect = {
|
||||||
type: 'keyword',
|
type: 'keyword',
|
||||||
suggest: true,
|
suggest: true,
|
||||||
then: makeOpBlock(agentToOptText),
|
then: makeOpBlock(agentToOptText, {
|
||||||
|
':': colonTextToEnd,
|
||||||
|
'\n': hiddenEnd,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const then = {'': 0};
|
const then = {'': 0};
|
||||||
|
@ -756,7 +764,10 @@ define('sequence/CodeMirrorMode',['core/ArrayUtilities'], (array) => {
|
||||||
override: 'Label',
|
override: 'Label',
|
||||||
then: {},
|
then: {},
|
||||||
};
|
};
|
||||||
return makeOpBlock({type: 'variable', suggest: 'Agent', then});
|
return makeOpBlock(
|
||||||
|
{type: 'variable', suggest: 'Agent', then},
|
||||||
|
then
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const BASE_THEN = {
|
const BASE_THEN = {
|
||||||
|
@ -1597,10 +1608,10 @@ define('sequence/Parser',[
|
||||||
})());
|
})());
|
||||||
|
|
||||||
const CONNECT_AGENT_FLAGS = {
|
const CONNECT_AGENT_FLAGS = {
|
||||||
'*': 'begin',
|
'*': {flag: 'begin', allowBlankName: true, blankNameFlag: 'source'},
|
||||||
'+': 'start',
|
'+': {flag: 'start'},
|
||||||
'-': 'stop',
|
'-': {flag: 'stop'},
|
||||||
'!': 'end',
|
'!': {flag: 'end'},
|
||||||
};
|
};
|
||||||
|
|
||||||
const TERMINATOR_TYPES = [
|
const TERMINATOR_TYPES = [
|
||||||
|
@ -1712,7 +1723,7 @@ define('sequence/Parser',[
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readAgentAlias(line, start, end, enableAlias) {
|
function readAgentAlias(line, start, end, {enableAlias, allowBlankName}) {
|
||||||
let aliasSep = -1;
|
let aliasSep = -1;
|
||||||
if(enableAlias) {
|
if(enableAlias) {
|
||||||
aliasSep = findToken(line, 'as', start);
|
aliasSep = findToken(line, 'as', start);
|
||||||
|
@ -1720,7 +1731,7 @@ define('sequence/Parser',[
|
||||||
if(aliasSep === -1 || aliasSep >= end) {
|
if(aliasSep === -1 || aliasSep >= end) {
|
||||||
aliasSep = end;
|
aliasSep = end;
|
||||||
}
|
}
|
||||||
if(start >= aliasSep) {
|
if(start >= aliasSep && !allowBlankName) {
|
||||||
throw makeError('Missing agent name', errToken(line, start));
|
throw makeError('Missing agent name', errToken(line, start));
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
@ -1734,25 +1745,31 @@ define('sequence/Parser',[
|
||||||
aliases = false,
|
aliases = false,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
const flags = [];
|
const flags = [];
|
||||||
|
const blankNameFlags = [];
|
||||||
let p = start;
|
let p = start;
|
||||||
|
let allowBlankName = false;
|
||||||
for(; p < end; ++ p) {
|
for(; p < end; ++ p) {
|
||||||
const token = line[p];
|
const token = line[p];
|
||||||
const rawFlag = tokenKeyword(token);
|
const rawFlag = tokenKeyword(token);
|
||||||
const flag = flagTypes[rawFlag];
|
const flag = flagTypes[rawFlag];
|
||||||
if(flag) {
|
if(!flag) {
|
||||||
if(flags.includes(flag)) {
|
|
||||||
throw makeError('Duplicate agent flag: ' + rawFlag, token);
|
|
||||||
}
|
|
||||||
flags.push(flag);
|
|
||||||
} else {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if(flags.includes(flag.flag)) {
|
||||||
|
throw makeError('Duplicate agent flag: ' + rawFlag, token);
|
||||||
|
}
|
||||||
|
allowBlankName = allowBlankName || Boolean(flag.allowBlankName);
|
||||||
|
flags.push(flag.flag);
|
||||||
|
blankNameFlags.push(flag.blankNameFlag);
|
||||||
}
|
}
|
||||||
const {name, alias} = readAgentAlias(line, p, end, aliases);
|
const {name, alias} = readAgentAlias(line, p, end, {
|
||||||
|
enableAlias: aliases,
|
||||||
|
allowBlankName,
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
alias,
|
alias,
|
||||||
flags,
|
flags: name ? flags : blankNameFlags,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2091,8 +2108,8 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => {
|
||||||
equals: (a, b) => {
|
equals: (a, b) => {
|
||||||
return a.id === b.id;
|
return a.id === b.id;
|
||||||
},
|
},
|
||||||
make: (id, {anchorRight = false} = {}) => {
|
make: (id, {anchorRight = false, isVirtualSource = false} = {}) => {
|
||||||
return {id, anchorRight};
|
return {id, anchorRight, isVirtualSource};
|
||||||
},
|
},
|
||||||
indexOf: (list, gAgent) => {
|
indexOf: (list, gAgent) => {
|
||||||
return array.indexOf(list, gAgent, GAgent.equals);
|
return array.indexOf(list, gAgent, GAgent.equals);
|
||||||
|
@ -2100,6 +2117,14 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => {
|
||||||
hasIntersection: (a, b) => {
|
hasIntersection: (a, b) => {
|
||||||
return array.hasIntersection(a, b, GAgent.equals);
|
return array.hasIntersection(a, b, GAgent.equals);
|
||||||
},
|
},
|
||||||
|
addNearby: (target, reference, item, offset) => {
|
||||||
|
const p = array.indexOf(target, reference, GAgent.equals);
|
||||||
|
if(p === -1) {
|
||||||
|
target.push(item);
|
||||||
|
} else {
|
||||||
|
target.splice(p + offset, 0, item);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const NOTE_DEFAULT_G_AGENTS = {
|
const NOTE_DEFAULT_G_AGENTS = {
|
||||||
|
@ -2108,6 +2133,8 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => {
|
||||||
'note right': [GAgent.make(']')],
|
'note right': [GAgent.make(']')],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SPECIAL_AGENT_IDS = ['[', ']'];
|
||||||
|
|
||||||
const MERGABLE = {
|
const MERGABLE = {
|
||||||
'agent begin': {
|
'agent begin': {
|
||||||
check: ['mode'],
|
check: ['mode'],
|
||||||
|
@ -2282,7 +2309,7 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => {
|
||||||
this.activeGroups = new Map();
|
this.activeGroups = new Map();
|
||||||
this.gAgents = [];
|
this.gAgents = [];
|
||||||
this.labelPattern = null;
|
this.labelPattern = null;
|
||||||
this.blockCount = 0;
|
this.nextID = 0;
|
||||||
this.nesting = [];
|
this.nesting = [];
|
||||||
this.markers = new Set();
|
this.markers = new Set();
|
||||||
this.currentSection = null;
|
this.currentSection = null;
|
||||||
|
@ -2311,7 +2338,7 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => {
|
||||||
this.endGroup = this.endGroup.bind(this);
|
this.endGroup = this.endGroup.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toGAgent({alias, name}) {
|
toGAgent({name, alias, flags}) {
|
||||||
if(alias) {
|
if(alias) {
|
||||||
if(this.agentAliases.has(name)) {
|
if(this.agentAliases.has(name)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -2330,7 +2357,9 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => {
|
||||||
}
|
}
|
||||||
this.agentAliases.set(alias, name);
|
this.agentAliases.set(alias, name);
|
||||||
}
|
}
|
||||||
return GAgent.make(this.agentAliases.get(name) || name);
|
return GAgent.make(this.agentAliases.get(name) || name, {
|
||||||
|
isVirtualSource: flags.includes('source'),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addStage(stage, isVisible = true) {
|
addStage(stage, isVisible = true) {
|
||||||
|
@ -2366,7 +2395,12 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
defineGAgents(gAgents) {
|
defineGAgents(gAgents) {
|
||||||
array.mergeSets(this.currentNest.gAgents, gAgents, GAgent.equals);
|
array.mergeSets(
|
||||||
|
this.currentNest.gAgents,
|
||||||
|
gAgents.filter((gAgent) =>
|
||||||
|
!SPECIAL_AGENT_IDS.includes(gAgent.id)),
|
||||||
|
GAgent.equals
|
||||||
|
);
|
||||||
array.mergeSets(this.gAgents, gAgents, GAgent.equals);
|
array.mergeSets(this.gAgents, gAgents, GAgent.equals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2390,7 +2424,9 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => {
|
||||||
validateGAgents(gAgents, {
|
validateGAgents(gAgents, {
|
||||||
allowGrouped = false,
|
allowGrouped = false,
|
||||||
rejectGrouped = false,
|
rejectGrouped = false,
|
||||||
|
allowVirtual = false,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
|
/* jshint -W074 */ // agent validity checking requires several steps
|
||||||
gAgents.forEach((gAgent) => {
|
gAgents.forEach((gAgent) => {
|
||||||
const state = this.getGAgentState(gAgent);
|
const state = this.getGAgentState(gAgent);
|
||||||
if(state.covered) {
|
if(state.covered) {
|
||||||
|
@ -2404,6 +2440,9 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => {
|
||||||
if(state.blocked && (!allowGrouped || state.group === null)) {
|
if(state.blocked && (!allowGrouped || state.group === null)) {
|
||||||
throw new Error('Duplicate agent name: ' + gAgent.id);
|
throw new Error('Duplicate agent name: ' + gAgent.id);
|
||||||
}
|
}
|
||||||
|
if(!allowVirtual && gAgent.isVirtualSource) {
|
||||||
|
throw new Error('cannot use message source here');
|
||||||
|
}
|
||||||
if(gAgent.id.startsWith('__')) {
|
if(gAgent.id.startsWith('__')) {
|
||||||
throw new Error(gAgent.id + ' is a reserved name');
|
throw new Error(gAgent.id + ' is a reserved name');
|
||||||
}
|
}
|
||||||
|
@ -2497,12 +2536,18 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => {
|
||||||
this.replaceGAgentState(rightGAgent, AgentState.LOCKED);
|
this.replaceGAgentState(rightGAgent, AgentState.LOCKED);
|
||||||
this.nesting.push(this.currentNest);
|
this.nesting.push(this.currentNest);
|
||||||
|
|
||||||
return {gAgents, stages};
|
return {stages};
|
||||||
}
|
}
|
||||||
|
|
||||||
nextBlockName() {
|
nextBlockName() {
|
||||||
const name = '__BLOCK' + this.blockCount;
|
const name = '__BLOCK' + this.nextID;
|
||||||
++ this.blockCount;
|
++ this.nextID;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextVirtualAgentName() {
|
||||||
|
const name = '__' + this.nextID;
|
||||||
|
++ this.nextID;
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2706,9 +2751,13 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => {
|
||||||
let ind1 = GAgent.indexOf(this.gAgents, gAgents1[0]);
|
let ind1 = GAgent.indexOf(this.gAgents, gAgents1[0]);
|
||||||
let ind2 = GAgent.indexOf(this.gAgents, gAgents2[0]);
|
let ind2 = GAgent.indexOf(this.gAgents, gAgents2[0]);
|
||||||
if(ind1 === -1) {
|
if(ind1 === -1) {
|
||||||
ind1 = this.gAgents.length;
|
// Virtual sources written as '* -> Ref' will spawn to the left,
|
||||||
|
// not the right (as non-virtual agents would)
|
||||||
|
ind1 = gAgents1[0].isVirtualSource ? -1 : this.gAgents.length;
|
||||||
}
|
}
|
||||||
if(ind2 === -1) {
|
if(ind2 === -1) {
|
||||||
|
// Virtual and non-virtual agents written as 'Ref -> *' will
|
||||||
|
// spawn to the right
|
||||||
ind2 = this.gAgents.length;
|
ind2 = this.gAgents.length;
|
||||||
}
|
}
|
||||||
if(ind1 === ind2) {
|
if(ind1 === ind2) {
|
||||||
|
@ -2755,21 +2804,79 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => {
|
||||||
return {beginGAgents, endGAgents, startGAgents, stopGAgents};
|
return {beginGAgents, endGAgents, startGAgents, stopGAgents};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
makeVirtualAgent(anchorRight) {
|
||||||
|
const virtualGAgent = GAgent.make(this.nextVirtualAgentName(), {
|
||||||
|
anchorRight,
|
||||||
|
isVirtualSource: true,
|
||||||
|
});
|
||||||
|
this.replaceGAgentState(virtualGAgent, AgentState.LOCKED);
|
||||||
|
return virtualGAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
addNearbyAgent(gAgentReference, gAgent, offset) {
|
||||||
|
GAgent.addNearby(
|
||||||
|
this.currentNest.gAgents,
|
||||||
|
gAgentReference,
|
||||||
|
gAgent,
|
||||||
|
offset
|
||||||
|
);
|
||||||
|
GAgent.addNearby(
|
||||||
|
this.gAgents,
|
||||||
|
gAgentReference,
|
||||||
|
gAgent,
|
||||||
|
offset
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
expandVirtualSourceAgents(gAgents) {
|
||||||
|
if(gAgents[0].isVirtualSource) {
|
||||||
|
if(gAgents[1].isVirtualSource) {
|
||||||
|
throw new Error('Cannot connect found messages');
|
||||||
|
}
|
||||||
|
if(SPECIAL_AGENT_IDS.includes(gAgents[1].id)) {
|
||||||
|
throw new Error(
|
||||||
|
'Cannot connect found messages to special agents'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const virtualGAgent = this.makeVirtualAgent(true);
|
||||||
|
this.addNearbyAgent(gAgents[1], virtualGAgent, 0);
|
||||||
|
return [virtualGAgent, gAgents[1]];
|
||||||
|
}
|
||||||
|
if(gAgents[1].isVirtualSource) {
|
||||||
|
if(SPECIAL_AGENT_IDS.includes(gAgents[0].id)) {
|
||||||
|
throw new Error(
|
||||||
|
'Cannot connect found messages to special agents'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const virtualGAgent = this.makeVirtualAgent(false);
|
||||||
|
this.addNearbyAgent(gAgents[0], virtualGAgent, 1);
|
||||||
|
return [gAgents[0], virtualGAgent];
|
||||||
|
}
|
||||||
|
return gAgents;
|
||||||
|
}
|
||||||
|
|
||||||
handleConnect({agents, label, options}) {
|
handleConnect({agents, label, options}) {
|
||||||
const flags = this.filterConnectFlags(agents);
|
const flags = this.filterConnectFlags(agents);
|
||||||
|
|
||||||
let gAgents = agents.map(this.toGAgent);
|
let gAgents = agents.map(this.toGAgent);
|
||||||
this.validateGAgents(gAgents, {allowGrouped: true});
|
this.validateGAgents(gAgents, {
|
||||||
|
allowGrouped: true,
|
||||||
|
allowVirtual: true,
|
||||||
|
});
|
||||||
|
|
||||||
const allGAgents = array.flatMap(gAgents, this.expandGroupedGAgent);
|
const allGAgents = array.flatMap(gAgents, this.expandGroupedGAgent);
|
||||||
this.defineGAgents(allGAgents);
|
this.defineGAgents(allGAgents
|
||||||
|
.filter((gAgent) => !gAgent.isVirtualSource)
|
||||||
|
);
|
||||||
|
|
||||||
gAgents = this.expandGroupedGAgentConnection(gAgents);
|
gAgents = this.expandGroupedGAgentConnection(gAgents);
|
||||||
|
gAgents = this.expandVirtualSourceAgents(gAgents);
|
||||||
const agentIDs = gAgents.map((gAgent) => gAgent.id);
|
const agentIDs = gAgents.map((gAgent) => gAgent.id);
|
||||||
|
|
||||||
const implicitBeginGAgents = (agents
|
const implicitBeginGAgents = (agents
|
||||||
.filter(PAgent.hasFlag('begin', false))
|
.filter(PAgent.hasFlag('begin', false))
|
||||||
.map(this.toGAgent)
|
.map(this.toGAgent)
|
||||||
|
.filter((gAgent) => !gAgent.isVirtualSource)
|
||||||
);
|
);
|
||||||
this.addStage(this.setGAgentVis(implicitBeginGAgents, true, 'box'));
|
this.addStage(this.setGAgentVis(implicitBeginGAgents, true, 'box'));
|
||||||
|
|
||||||
|
@ -2866,7 +2973,7 @@ define('sequence/Generator',['core/ArrayUtilities'], (array) => {
|
||||||
this.agentAliases.clear();
|
this.agentAliases.clear();
|
||||||
this.activeGroups.clear();
|
this.activeGroups.clear();
|
||||||
this.gAgents.length = 0;
|
this.gAgents.length = 0;
|
||||||
this.blockCount = 0;
|
this.nextID = 0;
|
||||||
this.nesting.length = 0;
|
this.nesting.length = 0;
|
||||||
this.labelPattern = [{token: 'label'}];
|
this.labelPattern = [{token: 'label'}];
|
||||||
}
|
}
|
||||||
|
@ -3457,6 +3564,7 @@ define('sequence/components/BaseComponent',[],() => {
|
||||||
theme,
|
theme,
|
||||||
agentInfos,
|
agentInfos,
|
||||||
visibleAgentIDs,
|
visibleAgentIDs,
|
||||||
|
momentaryAgentIDs,
|
||||||
textSizer,
|
textSizer,
|
||||||
addSpacing,
|
addSpacing,
|
||||||
addSeparation,
|
addSeparation,
|
||||||
|
@ -3469,6 +3577,7 @@ define('sequence/components/BaseComponent',[],() => {
|
||||||
theme,
|
theme,
|
||||||
agentInfos,
|
agentInfos,
|
||||||
visibleAgentIDs,
|
visibleAgentIDs,
|
||||||
|
momentaryAgentIDs,
|
||||||
textSizer,
|
textSizer,
|
||||||
addSpacing,
|
addSpacing,
|
||||||
addSeparation,
|
addSeparation,
|
||||||
|
@ -4211,10 +4320,12 @@ define('sequence/components/AgentHighlight',['./BaseComponent'], (BaseComponent)
|
||||||
});
|
});
|
||||||
|
|
||||||
define('sequence/components/Connect',[
|
define('sequence/components/Connect',[
|
||||||
|
'core/ArrayUtilities',
|
||||||
'./BaseComponent',
|
'./BaseComponent',
|
||||||
'svg/SVGUtilities',
|
'svg/SVGUtilities',
|
||||||
'svg/SVGShapes',
|
'svg/SVGShapes',
|
||||||
], (
|
], (
|
||||||
|
array,
|
||||||
BaseComponent,
|
BaseComponent,
|
||||||
svg,
|
svg,
|
||||||
SVGShapes
|
SVGShapes
|
||||||
|
@ -4318,6 +4429,18 @@ define('sequence/components/Connect',[
|
||||||
];
|
];
|
||||||
|
|
||||||
class Connect extends BaseComponent {
|
class Connect extends BaseComponent {
|
||||||
|
separationPre({agentIDs}, env) {
|
||||||
|
const r = env.theme.connect.source.radius;
|
||||||
|
agentIDs.forEach((id) => {
|
||||||
|
const agentInfo = env.agentInfos.get(id);
|
||||||
|
if(!agentInfo.isVirtualSource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
agentInfo.currentRad = r;
|
||||||
|
agentInfo.currentMaxRad = Math.max(agentInfo.currentMaxRad, r);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
separation({label, agentIDs, options}, env) {
|
separation({label, agentIDs, options}, env) {
|
||||||
const config = env.theme.connect;
|
const config = env.theme.connect;
|
||||||
|
|
||||||
|
@ -4359,6 +4482,8 @@ define('sequence/components/Connect',[
|
||||||
) * 2
|
) * 2
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
array.mergeSets(env.momentaryAgentIDs, agentIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSelfConnect({label, agentIDs, options}, env) {
|
renderSelfConnect({label, agentIDs, options}, env) {
|
||||||
|
@ -4436,14 +4561,59 @@ define('sequence/components/Connect',[
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderSimpleLine(x0, x1, options, env) {
|
||||||
|
const dir = (x0 < x1) ? 1 : -1;
|
||||||
|
|
||||||
|
const config = env.theme.connect;
|
||||||
|
const line = config.line[options.line];
|
||||||
|
const lArrow = ARROWHEADS[options.left];
|
||||||
|
const rArrow = ARROWHEADS[options.right];
|
||||||
|
|
||||||
|
const rendered = line.renderFlat(line.attrs, {
|
||||||
|
x1: x0,
|
||||||
|
dx1: lArrow.lineGap(env.theme, line.attrs) * dir,
|
||||||
|
x2: x1,
|
||||||
|
dx2: -rArrow.lineGap(env.theme, line.attrs) * dir,
|
||||||
|
y: env.primaryY,
|
||||||
|
});
|
||||||
|
env.shapeLayer.appendChild(rendered.shape);
|
||||||
|
return rendered;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSimpleArrowheads(options, renderedLine, env, dir) {
|
||||||
|
const lArrow = ARROWHEADS[options.left];
|
||||||
|
const rArrow = ARROWHEADS[options.right];
|
||||||
|
|
||||||
|
lArrow.render(env.shapeLayer, env.theme, renderedLine.p1, dir);
|
||||||
|
rArrow.render(env.shapeLayer, env.theme, renderedLine.p2, -dir);
|
||||||
|
|
||||||
|
return {lArrow, rArrow};
|
||||||
|
}
|
||||||
|
|
||||||
|
renderVirtualSources(from, to, renderedLine, env) {
|
||||||
|
const config = env.theme.connect.source;
|
||||||
|
|
||||||
|
if(from.isVirtualSource) {
|
||||||
|
env.shapeLayer.appendChild(config.render({
|
||||||
|
x: renderedLine.p1.x - config.radius,
|
||||||
|
y: renderedLine.p1.y,
|
||||||
|
radius: config.radius,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if(to.isVirtualSource) {
|
||||||
|
env.shapeLayer.appendChild(config.render({
|
||||||
|
x: renderedLine.p2.x + config.radius,
|
||||||
|
y: renderedLine.p2.y,
|
||||||
|
radius: config.radius,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
renderSimpleConnect({label, agentIDs, options}, env) {
|
renderSimpleConnect({label, agentIDs, options}, env) {
|
||||||
const config = env.theme.connect;
|
const config = env.theme.connect;
|
||||||
const from = env.agentInfos.get(agentIDs[0]);
|
const from = env.agentInfos.get(agentIDs[0]);
|
||||||
const to = env.agentInfos.get(agentIDs[1]);
|
const to = env.agentInfos.get(agentIDs[1]);
|
||||||
|
|
||||||
const lArrow = ARROWHEADS[options.left];
|
|
||||||
const rArrow = ARROWHEADS[options.right];
|
|
||||||
|
|
||||||
const dir = (from.x < to.x) ? 1 : -1;
|
const dir = (from.x < to.x) ? 1 : -1;
|
||||||
|
|
||||||
const height = (
|
const height = (
|
||||||
|
@ -4469,18 +4639,12 @@ define('sequence/components/Connect',[
|
||||||
SVGTextBlockClass: env.SVGTextBlockClass,
|
SVGTextBlockClass: env.SVGTextBlockClass,
|
||||||
});
|
});
|
||||||
|
|
||||||
const line = config.line[options.line];
|
const rendered = this.renderSimpleLine(x0, x1, options, env);
|
||||||
const rendered = line.renderFlat(line.attrs, {
|
const {
|
||||||
x1: x0,
|
lArrow,
|
||||||
dx1: lArrow.lineGap(env.theme, line.attrs) * dir,
|
rArrow
|
||||||
x2: x1,
|
} = this.renderSimpleArrowheads(options, rendered, env, dir);
|
||||||
dx2: -rArrow.lineGap(env.theme, line.attrs) * dir,
|
this.renderVirtualSources(from, to, rendered, env);
|
||||||
y,
|
|
||||||
});
|
|
||||||
env.shapeLayer.appendChild(rendered.shape);
|
|
||||||
|
|
||||||
lArrow.render(env.shapeLayer, env.theme, rendered.p1, dir);
|
|
||||||
rArrow.render(env.shapeLayer, env.theme, rendered.p2, -dir);
|
|
||||||
|
|
||||||
const arrowSpread = Math.max(
|
const arrowSpread = Math.max(
|
||||||
lArrow.height(env.theme),
|
lArrow.height(env.theme),
|
||||||
|
@ -4996,6 +5160,7 @@ define('sequence/Renderer',[
|
||||||
theme: this.theme,
|
theme: this.theme,
|
||||||
agentInfos: this.agentInfos,
|
agentInfos: this.agentInfos,
|
||||||
visibleAgentIDs: this.visibleAgentIDs,
|
visibleAgentIDs: this.visibleAgentIDs,
|
||||||
|
momentaryAgentIDs: agentIDs,
|
||||||
textSizer: this.sizer,
|
textSizer: this.sizer,
|
||||||
addSpacing,
|
addSpacing,
|
||||||
addSeparation: this.addSeparation,
|
addSeparation: this.addSeparation,
|
||||||
|
@ -5200,6 +5365,7 @@ define('sequence/Renderer',[
|
||||||
id: agent.id,
|
id: agent.id,
|
||||||
formattedLabel: agent.formattedLabel,
|
formattedLabel: agent.formattedLabel,
|
||||||
anchorRight: agent.anchorRight,
|
anchorRight: agent.anchorRight,
|
||||||
|
isVirtualSource: agent.isVirtualSource,
|
||||||
index,
|
index,
|
||||||
x: null,
|
x: null,
|
||||||
latestYStart: null,
|
latestYStart: null,
|
||||||
|
@ -5906,6 +6072,19 @@ define('sequence/themes/Basic',[
|
||||||
'line-height': LINE_HEIGHT,
|
'line-height': LINE_HEIGHT,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
source: {
|
||||||
|
radius: 2,
|
||||||
|
render: ({x, y, radius}) => {
|
||||||
|
return svg.make('circle', {
|
||||||
|
'cx': x,
|
||||||
|
'cy': y,
|
||||||
|
'r': radius,
|
||||||
|
'fill': '#000000',
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 1,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
mask: {
|
mask: {
|
||||||
padding: {
|
padding: {
|
||||||
top: 0,
|
top: 0,
|
||||||
|
@ -6227,6 +6406,19 @@ define('sequence/themes/Monospace',[
|
||||||
'line-height': LINE_HEIGHT,
|
'line-height': LINE_HEIGHT,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
source: {
|
||||||
|
radius: 2,
|
||||||
|
render: ({x, y, radius}) => {
|
||||||
|
return svg.make('circle', {
|
||||||
|
'cx': x,
|
||||||
|
'cy': y,
|
||||||
|
'r': radius,
|
||||||
|
'fill': '#000000',
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 1,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
mask: {
|
mask: {
|
||||||
padding: {
|
padding: {
|
||||||
top: 0,
|
top: 0,
|
||||||
|
@ -6547,6 +6739,19 @@ define('sequence/themes/Chunky',[
|
||||||
'line-height': LINE_HEIGHT,
|
'line-height': LINE_HEIGHT,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
source: {
|
||||||
|
radius: 5,
|
||||||
|
render: ({x, y, radius}) => {
|
||||||
|
return svg.make('circle', {
|
||||||
|
'cx': x,
|
||||||
|
'cy': y,
|
||||||
|
'r': radius,
|
||||||
|
'fill': '#000000',
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 3,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
mask: {
|
mask: {
|
||||||
padding: {
|
padding: {
|
||||||
top: 1,
|
top: 1,
|
||||||
|
@ -7287,6 +7492,19 @@ define('sequence/themes/Sketch',[
|
||||||
'line-height': LINE_HEIGHT,
|
'line-height': LINE_HEIGHT,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
source: {
|
||||||
|
radius: 1,
|
||||||
|
render: ({x, y, radius}) => {
|
||||||
|
return svg.make('circle', {
|
||||||
|
'cx': x,
|
||||||
|
'cy': y,
|
||||||
|
'r': radius,
|
||||||
|
'fill': '#000000',
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 1,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
mask: {
|
mask: {
|
||||||
padding: {
|
padding: {
|
||||||
top: 0,
|
top: 0,
|
||||||
|
@ -7722,7 +7940,7 @@ define('sequence/themes/Sketch',[
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
shape: svg.make('path', Object.assign({'d': ln.nodes}, attrs)),
|
shape: svg.make('path', Object.assign({'d': ln.nodes}, attrs)),
|
||||||
p1: {x: ln.p1.x - dx1, y: ln.p2.y},
|
p1: {x: ln.p1.x - dx1, y: ln.p1.y},
|
||||||
p2: {x: ln.p2.x - dx2, y: ln.p2.y},
|
p2: {x: ln.p2.x - dx2, y: ln.p2.y},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -56,6 +56,10 @@
|
||||||
title: 'Self-connection',
|
title: 'Self-connection',
|
||||||
code: '{Agent1} -> {Agent1}: {Message}',
|
code: '{Agent1} -> {Agent1}: {Message}',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Found message',
|
||||||
|
code: '* -> {Agent1}: {Message}',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Request/response pair',
|
title: 'Request/response pair',
|
||||||
code: (
|
code: (
|
||||||
|
|
|
@ -42,8 +42,13 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
)};
|
)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const colonTextToEnd = {
|
||||||
|
type: 'operator',
|
||||||
|
suggest: true,
|
||||||
|
then: {'': textToEnd, '\n': hiddenEnd},
|
||||||
|
};
|
||||||
const agentListToText = agentListTo({
|
const agentListToText = agentListTo({
|
||||||
':': {type: 'operator', suggest: true, then: {'': textToEnd}},
|
':': colonTextToEnd,
|
||||||
});
|
});
|
||||||
const agentList2ToText = {type: 'variable', suggest: 'Agent', then: {
|
const agentList2ToText = {type: 'variable', suggest: 'Agent', then: {
|
||||||
'': 0,
|
'': 0,
|
||||||
|
@ -53,7 +58,7 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
const singleAgentToText = {type: 'variable', suggest: 'Agent', then: {
|
const singleAgentToText = {type: 'variable', suggest: 'Agent', then: {
|
||||||
'': 0,
|
'': 0,
|
||||||
',': CM_ERROR,
|
',': CM_ERROR,
|
||||||
':': {type: 'operator', suggest: true, then: {'': textToEnd}},
|
':': colonTextToEnd,
|
||||||
}};
|
}};
|
||||||
const agentToOptText = {type: 'variable', suggest: 'Agent', then: {
|
const agentToOptText = {type: 'variable', suggest: 'Agent', then: {
|
||||||
'': 0,
|
'': 0,
|
||||||
|
@ -97,7 +102,7 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeOpBlock(exit) {
|
function makeOpBlock(exit, sourceExit) {
|
||||||
const op = {type: 'operator', suggest: true, then: {
|
const op = {type: 'operator', suggest: true, then: {
|
||||||
'+': CM_ERROR,
|
'+': CM_ERROR,
|
||||||
'-': CM_ERROR,
|
'-': CM_ERROR,
|
||||||
|
@ -126,13 +131,13 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
}},
|
}},
|
||||||
'': exit,
|
'': exit,
|
||||||
}},
|
}},
|
||||||
'*': {type: 'operator', suggest: true, then: {
|
'*': {type: 'operator', suggest: true, then: Object.assign({
|
||||||
'+': op,
|
'+': op,
|
||||||
'-': op,
|
'-': op,
|
||||||
'*': CM_ERROR,
|
'*': CM_ERROR,
|
||||||
'!': CM_ERROR,
|
'!': CM_ERROR,
|
||||||
'': exit,
|
'': exit,
|
||||||
}},
|
}, sourceExit)},
|
||||||
'!': op,
|
'!': op,
|
||||||
'': exit,
|
'': exit,
|
||||||
};
|
};
|
||||||
|
@ -142,7 +147,10 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
const connect = {
|
const connect = {
|
||||||
type: 'keyword',
|
type: 'keyword',
|
||||||
suggest: true,
|
suggest: true,
|
||||||
then: makeOpBlock(agentToOptText),
|
then: makeOpBlock(agentToOptText, {
|
||||||
|
':': colonTextToEnd,
|
||||||
|
'\n': hiddenEnd,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const then = {'': 0};
|
const then = {'': 0};
|
||||||
|
@ -153,7 +161,10 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
override: 'Label',
|
override: 'Label',
|
||||||
then: {},
|
then: {},
|
||||||
};
|
};
|
||||||
return makeOpBlock({type: 'variable', suggest: 'Agent', then});
|
return makeOpBlock(
|
||||||
|
{type: 'variable', suggest: 'Agent', then},
|
||||||
|
then
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const BASE_THEN = {
|
const BASE_THEN = {
|
||||||
|
|
|
@ -36,8 +36,8 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
equals: (a, b) => {
|
equals: (a, b) => {
|
||||||
return a.id === b.id;
|
return a.id === b.id;
|
||||||
},
|
},
|
||||||
make: (id, {anchorRight = false} = {}) => {
|
make: (id, {anchorRight = false, isVirtualSource = false} = {}) => {
|
||||||
return {id, anchorRight};
|
return {id, anchorRight, isVirtualSource};
|
||||||
},
|
},
|
||||||
indexOf: (list, gAgent) => {
|
indexOf: (list, gAgent) => {
|
||||||
return array.indexOf(list, gAgent, GAgent.equals);
|
return array.indexOf(list, gAgent, GAgent.equals);
|
||||||
|
@ -45,6 +45,14 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
hasIntersection: (a, b) => {
|
hasIntersection: (a, b) => {
|
||||||
return array.hasIntersection(a, b, GAgent.equals);
|
return array.hasIntersection(a, b, GAgent.equals);
|
||||||
},
|
},
|
||||||
|
addNearby: (target, reference, item, offset) => {
|
||||||
|
const p = array.indexOf(target, reference, GAgent.equals);
|
||||||
|
if(p === -1) {
|
||||||
|
target.push(item);
|
||||||
|
} else {
|
||||||
|
target.splice(p + offset, 0, item);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const NOTE_DEFAULT_G_AGENTS = {
|
const NOTE_DEFAULT_G_AGENTS = {
|
||||||
|
@ -53,6 +61,8 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
'note right': [GAgent.make(']')],
|
'note right': [GAgent.make(']')],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SPECIAL_AGENT_IDS = ['[', ']'];
|
||||||
|
|
||||||
const MERGABLE = {
|
const MERGABLE = {
|
||||||
'agent begin': {
|
'agent begin': {
|
||||||
check: ['mode'],
|
check: ['mode'],
|
||||||
|
@ -227,7 +237,7 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
this.activeGroups = new Map();
|
this.activeGroups = new Map();
|
||||||
this.gAgents = [];
|
this.gAgents = [];
|
||||||
this.labelPattern = null;
|
this.labelPattern = null;
|
||||||
this.blockCount = 0;
|
this.nextID = 0;
|
||||||
this.nesting = [];
|
this.nesting = [];
|
||||||
this.markers = new Set();
|
this.markers = new Set();
|
||||||
this.currentSection = null;
|
this.currentSection = null;
|
||||||
|
@ -256,7 +266,7 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
this.endGroup = this.endGroup.bind(this);
|
this.endGroup = this.endGroup.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toGAgent({alias, name}) {
|
toGAgent({name, alias, flags}) {
|
||||||
if(alias) {
|
if(alias) {
|
||||||
if(this.agentAliases.has(name)) {
|
if(this.agentAliases.has(name)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -275,7 +285,9 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
}
|
}
|
||||||
this.agentAliases.set(alias, name);
|
this.agentAliases.set(alias, name);
|
||||||
}
|
}
|
||||||
return GAgent.make(this.agentAliases.get(name) || name);
|
return GAgent.make(this.agentAliases.get(name) || name, {
|
||||||
|
isVirtualSource: flags.includes('source'),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addStage(stage, isVisible = true) {
|
addStage(stage, isVisible = true) {
|
||||||
|
@ -311,7 +323,12 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
defineGAgents(gAgents) {
|
defineGAgents(gAgents) {
|
||||||
array.mergeSets(this.currentNest.gAgents, gAgents, GAgent.equals);
|
array.mergeSets(
|
||||||
|
this.currentNest.gAgents,
|
||||||
|
gAgents.filter((gAgent) =>
|
||||||
|
!SPECIAL_AGENT_IDS.includes(gAgent.id)),
|
||||||
|
GAgent.equals
|
||||||
|
);
|
||||||
array.mergeSets(this.gAgents, gAgents, GAgent.equals);
|
array.mergeSets(this.gAgents, gAgents, GAgent.equals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,7 +352,9 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
validateGAgents(gAgents, {
|
validateGAgents(gAgents, {
|
||||||
allowGrouped = false,
|
allowGrouped = false,
|
||||||
rejectGrouped = false,
|
rejectGrouped = false,
|
||||||
|
allowVirtual = false,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
|
/* jshint -W074 */ // agent validity checking requires several steps
|
||||||
gAgents.forEach((gAgent) => {
|
gAgents.forEach((gAgent) => {
|
||||||
const state = this.getGAgentState(gAgent);
|
const state = this.getGAgentState(gAgent);
|
||||||
if(state.covered) {
|
if(state.covered) {
|
||||||
|
@ -349,6 +368,9 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
if(state.blocked && (!allowGrouped || state.group === null)) {
|
if(state.blocked && (!allowGrouped || state.group === null)) {
|
||||||
throw new Error('Duplicate agent name: ' + gAgent.id);
|
throw new Error('Duplicate agent name: ' + gAgent.id);
|
||||||
}
|
}
|
||||||
|
if(!allowVirtual && gAgent.isVirtualSource) {
|
||||||
|
throw new Error('cannot use message source here');
|
||||||
|
}
|
||||||
if(gAgent.id.startsWith('__')) {
|
if(gAgent.id.startsWith('__')) {
|
||||||
throw new Error(gAgent.id + ' is a reserved name');
|
throw new Error(gAgent.id + ' is a reserved name');
|
||||||
}
|
}
|
||||||
|
@ -442,12 +464,18 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
this.replaceGAgentState(rightGAgent, AgentState.LOCKED);
|
this.replaceGAgentState(rightGAgent, AgentState.LOCKED);
|
||||||
this.nesting.push(this.currentNest);
|
this.nesting.push(this.currentNest);
|
||||||
|
|
||||||
return {gAgents, stages};
|
return {stages};
|
||||||
}
|
}
|
||||||
|
|
||||||
nextBlockName() {
|
nextBlockName() {
|
||||||
const name = '__BLOCK' + this.blockCount;
|
const name = '__BLOCK' + this.nextID;
|
||||||
++ this.blockCount;
|
++ this.nextID;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextVirtualAgentName() {
|
||||||
|
const name = '__' + this.nextID;
|
||||||
|
++ this.nextID;
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -651,9 +679,13 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
let ind1 = GAgent.indexOf(this.gAgents, gAgents1[0]);
|
let ind1 = GAgent.indexOf(this.gAgents, gAgents1[0]);
|
||||||
let ind2 = GAgent.indexOf(this.gAgents, gAgents2[0]);
|
let ind2 = GAgent.indexOf(this.gAgents, gAgents2[0]);
|
||||||
if(ind1 === -1) {
|
if(ind1 === -1) {
|
||||||
ind1 = this.gAgents.length;
|
// Virtual sources written as '* -> Ref' will spawn to the left,
|
||||||
|
// not the right (as non-virtual agents would)
|
||||||
|
ind1 = gAgents1[0].isVirtualSource ? -1 : this.gAgents.length;
|
||||||
}
|
}
|
||||||
if(ind2 === -1) {
|
if(ind2 === -1) {
|
||||||
|
// Virtual and non-virtual agents written as 'Ref -> *' will
|
||||||
|
// spawn to the right
|
||||||
ind2 = this.gAgents.length;
|
ind2 = this.gAgents.length;
|
||||||
}
|
}
|
||||||
if(ind1 === ind2) {
|
if(ind1 === ind2) {
|
||||||
|
@ -700,21 +732,79 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
return {beginGAgents, endGAgents, startGAgents, stopGAgents};
|
return {beginGAgents, endGAgents, startGAgents, stopGAgents};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
makeVirtualAgent(anchorRight) {
|
||||||
|
const virtualGAgent = GAgent.make(this.nextVirtualAgentName(), {
|
||||||
|
anchorRight,
|
||||||
|
isVirtualSource: true,
|
||||||
|
});
|
||||||
|
this.replaceGAgentState(virtualGAgent, AgentState.LOCKED);
|
||||||
|
return virtualGAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
addNearbyAgent(gAgentReference, gAgent, offset) {
|
||||||
|
GAgent.addNearby(
|
||||||
|
this.currentNest.gAgents,
|
||||||
|
gAgentReference,
|
||||||
|
gAgent,
|
||||||
|
offset
|
||||||
|
);
|
||||||
|
GAgent.addNearby(
|
||||||
|
this.gAgents,
|
||||||
|
gAgentReference,
|
||||||
|
gAgent,
|
||||||
|
offset
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
expandVirtualSourceAgents(gAgents) {
|
||||||
|
if(gAgents[0].isVirtualSource) {
|
||||||
|
if(gAgents[1].isVirtualSource) {
|
||||||
|
throw new Error('Cannot connect found messages');
|
||||||
|
}
|
||||||
|
if(SPECIAL_AGENT_IDS.includes(gAgents[1].id)) {
|
||||||
|
throw new Error(
|
||||||
|
'Cannot connect found messages to special agents'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const virtualGAgent = this.makeVirtualAgent(true);
|
||||||
|
this.addNearbyAgent(gAgents[1], virtualGAgent, 0);
|
||||||
|
return [virtualGAgent, gAgents[1]];
|
||||||
|
}
|
||||||
|
if(gAgents[1].isVirtualSource) {
|
||||||
|
if(SPECIAL_AGENT_IDS.includes(gAgents[0].id)) {
|
||||||
|
throw new Error(
|
||||||
|
'Cannot connect found messages to special agents'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const virtualGAgent = this.makeVirtualAgent(false);
|
||||||
|
this.addNearbyAgent(gAgents[0], virtualGAgent, 1);
|
||||||
|
return [gAgents[0], virtualGAgent];
|
||||||
|
}
|
||||||
|
return gAgents;
|
||||||
|
}
|
||||||
|
|
||||||
handleConnect({agents, label, options}) {
|
handleConnect({agents, label, options}) {
|
||||||
const flags = this.filterConnectFlags(agents);
|
const flags = this.filterConnectFlags(agents);
|
||||||
|
|
||||||
let gAgents = agents.map(this.toGAgent);
|
let gAgents = agents.map(this.toGAgent);
|
||||||
this.validateGAgents(gAgents, {allowGrouped: true});
|
this.validateGAgents(gAgents, {
|
||||||
|
allowGrouped: true,
|
||||||
|
allowVirtual: true,
|
||||||
|
});
|
||||||
|
|
||||||
const allGAgents = array.flatMap(gAgents, this.expandGroupedGAgent);
|
const allGAgents = array.flatMap(gAgents, this.expandGroupedGAgent);
|
||||||
this.defineGAgents(allGAgents);
|
this.defineGAgents(allGAgents
|
||||||
|
.filter((gAgent) => !gAgent.isVirtualSource)
|
||||||
|
);
|
||||||
|
|
||||||
gAgents = this.expandGroupedGAgentConnection(gAgents);
|
gAgents = this.expandGroupedGAgentConnection(gAgents);
|
||||||
|
gAgents = this.expandVirtualSourceAgents(gAgents);
|
||||||
const agentIDs = gAgents.map((gAgent) => gAgent.id);
|
const agentIDs = gAgents.map((gAgent) => gAgent.id);
|
||||||
|
|
||||||
const implicitBeginGAgents = (agents
|
const implicitBeginGAgents = (agents
|
||||||
.filter(PAgent.hasFlag('begin', false))
|
.filter(PAgent.hasFlag('begin', false))
|
||||||
.map(this.toGAgent)
|
.map(this.toGAgent)
|
||||||
|
.filter((gAgent) => !gAgent.isVirtualSource)
|
||||||
);
|
);
|
||||||
this.addStage(this.setGAgentVis(implicitBeginGAgents, true, 'box'));
|
this.addStage(this.setGAgentVis(implicitBeginGAgents, true, 'box'));
|
||||||
|
|
||||||
|
@ -811,7 +901,7 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
this.agentAliases.clear();
|
this.agentAliases.clear();
|
||||||
this.activeGroups.clear();
|
this.activeGroups.clear();
|
||||||
this.gAgents.length = 0;
|
this.gAgents.length = 0;
|
||||||
this.blockCount = 0;
|
this.nextID = 0;
|
||||||
this.nesting.length = 0;
|
this.nesting.length = 0;
|
||||||
this.labelPattern = [{token: 'label'}];
|
this.labelPattern = [{token: 'label'}];
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,6 +110,14 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const GENERATED = {
|
const GENERATED = {
|
||||||
|
agent: (id, {
|
||||||
|
formattedLabel = any(),
|
||||||
|
anchorRight = any(),
|
||||||
|
isVirtualSource = any(),
|
||||||
|
} = {}) => {
|
||||||
|
return {id, formattedLabel, anchorRight, isVirtualSource};
|
||||||
|
},
|
||||||
|
|
||||||
beginAgents: (agentIDs, {
|
beginAgents: (agentIDs, {
|
||||||
mode = any(),
|
mode = any(),
|
||||||
ln = any(),
|
ln = any(),
|
||||||
|
@ -264,8 +272,8 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
it('includes implicit hidden left/right agents', () => {
|
it('includes implicit hidden left/right agents', () => {
|
||||||
const sequence = invoke([]);
|
const sequence = invoke([]);
|
||||||
expect(sequence.agents).toEqual([
|
expect(sequence.agents).toEqual([
|
||||||
{id: '[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('[', {anchorRight: true}),
|
||||||
{id: ']', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent(']', {anchorRight: false}),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -296,13 +304,13 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
PARSED.beginAgents(['E']),
|
PARSED.beginAgents(['E']),
|
||||||
]);
|
]);
|
||||||
expect(sequence.agents).toEqual([
|
expect(sequence.agents).toEqual([
|
||||||
{id: '[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('['),
|
||||||
{id: 'A', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('A'),
|
||||||
{id: 'B', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('B'),
|
||||||
{id: 'C', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('C'),
|
||||||
{id: 'D', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('D'),
|
||||||
{id: 'E', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('E'),
|
||||||
{id: ']', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent(']'),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -311,10 +319,10 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
PARSED.connect(['A', 'B']),
|
PARSED.connect(['A', 'B']),
|
||||||
]);
|
]);
|
||||||
expect(sequence.agents).toEqual([
|
expect(sequence.agents).toEqual([
|
||||||
{id: '[', formattedLabel: any(), anchorRight: any()},
|
GENERATED.agent('['),
|
||||||
{id: 'A', formattedLabel: 'A!', anchorRight: any()},
|
GENERATED.agent('A', {formattedLabel: 'A!'}),
|
||||||
{id: 'B', formattedLabel: 'B!', anchorRight: any()},
|
GENERATED.agent('B', {formattedLabel: 'B!'}),
|
||||||
{id: ']', formattedLabel: any(), anchorRight: any()},
|
GENERATED.agent(']'),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -323,9 +331,9 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
PARSED.connect([']', 'B']),
|
PARSED.connect([']', 'B']),
|
||||||
]);
|
]);
|
||||||
expect(sequence.agents).toEqual([
|
expect(sequence.agents).toEqual([
|
||||||
{id: '[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('['),
|
||||||
{id: 'B', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('B'),
|
||||||
{id: ']', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent(']'),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -335,10 +343,10 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
PARSED.connect(['A', 'B']),
|
PARSED.connect(['A', 'B']),
|
||||||
]);
|
]);
|
||||||
expect(sequence.agents).toEqual([
|
expect(sequence.agents).toEqual([
|
||||||
{id: '[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('['),
|
||||||
{id: 'B', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('B'),
|
||||||
{id: 'A', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('A'),
|
||||||
{id: ']', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent(']'),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -348,10 +356,10 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
PARSED.connect(['A', 'B']),
|
PARSED.connect(['A', 'B']),
|
||||||
]);
|
]);
|
||||||
expect(sequence.agents).toEqual([
|
expect(sequence.agents).toEqual([
|
||||||
{id: '[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('['),
|
||||||
{id: 'Baz', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('Baz'),
|
||||||
{id: 'A', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('A'),
|
||||||
{id: ']', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent(']'),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -419,6 +427,119 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('converts source agents into virtual agents', () => {
|
||||||
|
const sequence = invoke([
|
||||||
|
PARSED.connect([
|
||||||
|
'A',
|
||||||
|
{name: '', alias: '', flags: ['source']},
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
expect(sequence.agents).toEqual([
|
||||||
|
GENERATED.agent('['),
|
||||||
|
GENERATED.agent('A'),
|
||||||
|
GENERATED.agent('__0', {
|
||||||
|
anchorRight: false,
|
||||||
|
isVirtualSource: true,
|
||||||
|
}),
|
||||||
|
GENERATED.agent(']'),
|
||||||
|
]);
|
||||||
|
expect(sequence.stages).toEqual([
|
||||||
|
GENERATED.beginAgents(['A']),
|
||||||
|
GENERATED.connect(['A', '__0']),
|
||||||
|
GENERATED.endAgents(['A']),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('converts sources into distinct virtual agents', () => {
|
||||||
|
const sequence = invoke([
|
||||||
|
PARSED.connect([
|
||||||
|
'A',
|
||||||
|
{name: '', alias: '', flags: ['source']},
|
||||||
|
]),
|
||||||
|
PARSED.connect([
|
||||||
|
'A',
|
||||||
|
{name: '', alias: '', flags: ['source']},
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
expect(sequence.agents).toEqual([
|
||||||
|
GENERATED.agent('['),
|
||||||
|
GENERATED.agent('A'),
|
||||||
|
GENERATED.agent('__1'),
|
||||||
|
GENERATED.agent('__0'),
|
||||||
|
GENERATED.agent(']'),
|
||||||
|
]);
|
||||||
|
expect(sequence.stages).toEqual([
|
||||||
|
GENERATED.beginAgents(['A']),
|
||||||
|
GENERATED.connect(['A', '__0']),
|
||||||
|
GENERATED.connect(['A', '__1']),
|
||||||
|
GENERATED.endAgents(['A']),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('places source agents near the connected agent', () => {
|
||||||
|
const sequence = invoke([
|
||||||
|
PARSED.beginAgents(['A', 'B', 'C']),
|
||||||
|
PARSED.connect([
|
||||||
|
'B',
|
||||||
|
{name: '', alias: '', flags: ['source']},
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
expect(sequence.agents).toEqual([
|
||||||
|
GENERATED.agent('['),
|
||||||
|
GENERATED.agent('A'),
|
||||||
|
GENERATED.agent('B'),
|
||||||
|
GENERATED.agent('__0', {
|
||||||
|
anchorRight: false,
|
||||||
|
isVirtualSource: true,
|
||||||
|
}),
|
||||||
|
GENERATED.agent('C'),
|
||||||
|
GENERATED.agent(']'),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('places source agents left when connections are reversed', () => {
|
||||||
|
const sequence = invoke([
|
||||||
|
PARSED.beginAgents(['A', 'B', 'C']),
|
||||||
|
PARSED.connect([
|
||||||
|
{name: '', alias: '', flags: ['source']},
|
||||||
|
'B',
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
expect(sequence.agents).toEqual([
|
||||||
|
GENERATED.agent('['),
|
||||||
|
GENERATED.agent('A'),
|
||||||
|
GENERATED.agent('__0', {
|
||||||
|
anchorRight: true,
|
||||||
|
isVirtualSource: true,
|
||||||
|
}),
|
||||||
|
GENERATED.agent('B'),
|
||||||
|
GENERATED.agent('C'),
|
||||||
|
GENERATED.agent(']'),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects connections between virtual agents', () => {
|
||||||
|
expect(() => invoke([
|
||||||
|
PARSED.connect([
|
||||||
|
{name: '', alias: '', flags: ['source']},
|
||||||
|
{name: '', alias: '', flags: ['source']},
|
||||||
|
]),
|
||||||
|
])).toThrow(new Error(
|
||||||
|
'Cannot connect found messages at line 1'
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects connections between virtual agents and sides', () => {
|
||||||
|
expect(() => invoke([
|
||||||
|
PARSED.connect([
|
||||||
|
{name: '', alias: '', flags: ['source']},
|
||||||
|
']',
|
||||||
|
]),
|
||||||
|
])).toThrow(new Error(
|
||||||
|
'Cannot connect found messages to special agents at line 1'
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
it('uses label patterns for connections', () => {
|
it('uses label patterns for connections', () => {
|
||||||
const sequence = invoke([
|
const sequence = invoke([
|
||||||
PARSED.labelPattern(['foo ', {token: 'label'}, ' bar']),
|
PARSED.labelPattern(['foo ', {token: 'label'}, ' bar']),
|
||||||
|
@ -819,12 +940,12 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(sequence.agents).toEqual([
|
expect(sequence.agents).toEqual([
|
||||||
{id: '[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('['),
|
||||||
{id: '__BLOCK0[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('__BLOCK0[', {anchorRight: true}),
|
||||||
{id: 'A', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('A'),
|
||||||
{id: 'B', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('B'),
|
||||||
{id: '__BLOCK0]', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('__BLOCK0]', {anchorRight: false}),
|
||||||
{id: ']', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent(']'),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -841,20 +962,40 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(sequence.agents).toEqual([
|
expect(sequence.agents).toEqual([
|
||||||
{id: '[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('['),
|
||||||
{id: 'A', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('A'),
|
||||||
{id: 'B', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('B'),
|
||||||
{id: '__BLOCK0[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('__BLOCK0['),
|
||||||
{id: 'C', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('C'),
|
||||||
{id: 'D', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('D'),
|
||||||
{id: '__BLOCK1[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('__BLOCK1['),
|
||||||
{id: 'E', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('E'),
|
||||||
{id: 'F', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('F'),
|
||||||
{id: '__BLOCK1]', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('__BLOCK1]'),
|
||||||
{id: '__BLOCK0]', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('__BLOCK0]'),
|
||||||
{id: 'G', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('G'),
|
||||||
{id: 'H', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('H'),
|
||||||
{id: ']', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent(']'),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignores side agents when calculating block bounds', () => {
|
||||||
|
const sequence = invoke([
|
||||||
|
PARSED.beginAgents(['A', 'B', 'C']),
|
||||||
|
PARSED.blockBegin('if', 'abc'),
|
||||||
|
PARSED.connect(['[', 'B']),
|
||||||
|
PARSED.connect(['B', ']']),
|
||||||
|
PARSED.blockEnd(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(sequence.agents).toEqual([
|
||||||
|
GENERATED.agent('['),
|
||||||
|
GENERATED.agent('A'),
|
||||||
|
GENERATED.agent('__BLOCK0['),
|
||||||
|
GENERATED.agent('B'),
|
||||||
|
GENERATED.agent('__BLOCK0]'),
|
||||||
|
GENERATED.agent('C'),
|
||||||
|
GENERATED.agent(']'),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -916,15 +1057,15 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(sequence.agents).toEqual([
|
expect(sequence.agents).toEqual([
|
||||||
{id: '[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('['),
|
||||||
{id: '__BLOCK0[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('__BLOCK0['),
|
||||||
{id: '__BLOCK1[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('__BLOCK1['),
|
||||||
{id: 'A', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('A'),
|
||||||
{id: 'B', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('B'),
|
||||||
{id: 'C', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('C'),
|
||||||
{id: '__BLOCK1]', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('__BLOCK1]'),
|
||||||
{id: '__BLOCK0]', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('__BLOCK0]'),
|
||||||
{id: ']', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent(']'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const bounds0 = {
|
const bounds0 = {
|
||||||
|
@ -955,14 +1096,14 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(sequence.agents).toEqual([
|
expect(sequence.agents).toEqual([
|
||||||
{id: '[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('['),
|
||||||
{id: '__BLOCK0[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('__BLOCK0['),
|
||||||
{id: '__BLOCK1[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('__BLOCK1['),
|
||||||
{id: 'A', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('A'),
|
||||||
{id: 'B', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('B'),
|
||||||
{id: '__BLOCK1]', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('__BLOCK1]'),
|
||||||
{id: '__BLOCK0]', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('__BLOCK0]'),
|
||||||
{id: ']', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent(']'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const bounds0 = {
|
const bounds0 = {
|
||||||
|
@ -1051,12 +1192,12 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(sequence.agents).toEqual([
|
expect(sequence.agents).toEqual([
|
||||||
{id: '[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('['),
|
||||||
{id: '__BLOCK0[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('__BLOCK0['),
|
||||||
{id: 'A', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('A'),
|
||||||
{id: 'B', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('B'),
|
||||||
{id: '__BLOCK0]', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('__BLOCK0]'),
|
||||||
{id: ']', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent(']'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(sequence.stages).toEqual([
|
expect(sequence.stages).toEqual([
|
||||||
|
@ -1116,14 +1257,14 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(sequence.agents).toEqual([
|
expect(sequence.agents).toEqual([
|
||||||
{id: '[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('['),
|
||||||
{id: 'A', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('A'),
|
||||||
{id: '__BLOCK0[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('__BLOCK0['),
|
||||||
{id: 'B', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('B'),
|
||||||
{id: 'C', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('C'),
|
||||||
{id: '__BLOCK0]', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('__BLOCK0]'),
|
||||||
{id: 'D', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('D'),
|
||||||
{id: ']', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent(']'),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1135,15 +1276,15 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(sequence.agents).toEqual([
|
expect(sequence.agents).toEqual([
|
||||||
{id: '[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('['),
|
||||||
{id: 'A', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('A'),
|
||||||
{id: '__BLOCK0[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('__BLOCK0['),
|
||||||
{id: 'B', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('B'),
|
||||||
{id: 'C', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('C'),
|
||||||
{id: 'D', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('D'),
|
||||||
{id: '__BLOCK0]', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('__BLOCK0]'),
|
||||||
{id: 'E', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('E'),
|
||||||
{id: ']', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent(']'),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1342,16 +1483,16 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(sequenceR.agents).toEqual([
|
expect(sequenceR.agents).toEqual([
|
||||||
{id: '[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('['),
|
||||||
{id: 'A', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('A'),
|
||||||
{id: '__BLOCK1[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('__BLOCK1['),
|
||||||
{id: '__BLOCK0[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('__BLOCK0[', {anchorRight: true}),
|
||||||
{id: 'B', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('B'),
|
||||||
{id: 'C', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('C'),
|
||||||
{id: '__BLOCK0]', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('__BLOCK0]', {anchorRight: false}),
|
||||||
{id: 'D', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('D'),
|
||||||
{id: '__BLOCK1]', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('__BLOCK1]'),
|
||||||
{id: ']', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent(']'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const sequenceL = invoke([
|
const sequenceL = invoke([
|
||||||
|
@ -1364,16 +1505,51 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(sequenceL.agents).toEqual([
|
expect(sequenceL.agents).toEqual([
|
||||||
{id: '[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('['),
|
||||||
{id: '__BLOCK1[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('__BLOCK1['),
|
||||||
{id: 'A', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('A'),
|
||||||
{id: '__BLOCK0[', formattedLabel: any(), anchorRight: true},
|
GENERATED.agent('__BLOCK0['),
|
||||||
{id: 'B', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('B'),
|
||||||
{id: 'C', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('C'),
|
||||||
{id: '__BLOCK0]', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('__BLOCK0]'),
|
||||||
{id: '__BLOCK1]', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('__BLOCK1]'),
|
||||||
{id: 'D', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent('D'),
|
||||||
{id: ']', formattedLabel: any(), anchorRight: false},
|
GENERATED.agent(']'),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows connections between sources and references', () => {
|
||||||
|
const sequence = invoke([
|
||||||
|
PARSED.beginAgents(['A', 'B', 'C', 'D']),
|
||||||
|
PARSED.groupBegin('Bar', ['B', 'C'], {label: 'Foo'}),
|
||||||
|
PARSED.connect([
|
||||||
|
{name: '', alias: '', flags: ['source']},
|
||||||
|
'Bar',
|
||||||
|
]),
|
||||||
|
PARSED.connect([
|
||||||
|
'Bar',
|
||||||
|
{name: '', alias: '', flags: ['source']},
|
||||||
|
]),
|
||||||
|
PARSED.endAgents(['Bar']),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(sequence.agents).toEqual([
|
||||||
|
GENERATED.agent('['),
|
||||||
|
GENERATED.agent('A'),
|
||||||
|
GENERATED.agent('__1', {
|
||||||
|
anchorRight: true,
|
||||||
|
isVirtualSource: true,
|
||||||
|
}),
|
||||||
|
GENERATED.agent('__BLOCK0['),
|
||||||
|
GENERATED.agent('B'),
|
||||||
|
GENERATED.agent('C'),
|
||||||
|
GENERATED.agent('__BLOCK0]'),
|
||||||
|
GENERATED.agent('__2', {
|
||||||
|
anchorRight: false,
|
||||||
|
isVirtualSource: true,
|
||||||
|
}),
|
||||||
|
GENERATED.agent('D'),
|
||||||
|
GENERATED.agent(']'),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -67,10 +67,10 @@ define([
|
||||||
})());
|
})());
|
||||||
|
|
||||||
const CONNECT_AGENT_FLAGS = {
|
const CONNECT_AGENT_FLAGS = {
|
||||||
'*': 'begin',
|
'*': {flag: 'begin', allowBlankName: true, blankNameFlag: 'source'},
|
||||||
'+': 'start',
|
'+': {flag: 'start'},
|
||||||
'-': 'stop',
|
'-': {flag: 'stop'},
|
||||||
'!': 'end',
|
'!': {flag: 'end'},
|
||||||
};
|
};
|
||||||
|
|
||||||
const TERMINATOR_TYPES = [
|
const TERMINATOR_TYPES = [
|
||||||
|
@ -182,7 +182,7 @@ define([
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readAgentAlias(line, start, end, enableAlias) {
|
function readAgentAlias(line, start, end, {enableAlias, allowBlankName}) {
|
||||||
let aliasSep = -1;
|
let aliasSep = -1;
|
||||||
if(enableAlias) {
|
if(enableAlias) {
|
||||||
aliasSep = findToken(line, 'as', start);
|
aliasSep = findToken(line, 'as', start);
|
||||||
|
@ -190,7 +190,7 @@ define([
|
||||||
if(aliasSep === -1 || aliasSep >= end) {
|
if(aliasSep === -1 || aliasSep >= end) {
|
||||||
aliasSep = end;
|
aliasSep = end;
|
||||||
}
|
}
|
||||||
if(start >= aliasSep) {
|
if(start >= aliasSep && !allowBlankName) {
|
||||||
throw makeError('Missing agent name', errToken(line, start));
|
throw makeError('Missing agent name', errToken(line, start));
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
@ -204,25 +204,31 @@ define([
|
||||||
aliases = false,
|
aliases = false,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
const flags = [];
|
const flags = [];
|
||||||
|
const blankNameFlags = [];
|
||||||
let p = start;
|
let p = start;
|
||||||
|
let allowBlankName = false;
|
||||||
for(; p < end; ++ p) {
|
for(; p < end; ++ p) {
|
||||||
const token = line[p];
|
const token = line[p];
|
||||||
const rawFlag = tokenKeyword(token);
|
const rawFlag = tokenKeyword(token);
|
||||||
const flag = flagTypes[rawFlag];
|
const flag = flagTypes[rawFlag];
|
||||||
if(flag) {
|
if(!flag) {
|
||||||
if(flags.includes(flag)) {
|
|
||||||
throw makeError('Duplicate agent flag: ' + rawFlag, token);
|
|
||||||
}
|
|
||||||
flags.push(flag);
|
|
||||||
} else {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if(flags.includes(flag.flag)) {
|
||||||
|
throw makeError('Duplicate agent flag: ' + rawFlag, token);
|
||||||
|
}
|
||||||
|
allowBlankName = allowBlankName || Boolean(flag.allowBlankName);
|
||||||
|
flags.push(flag.flag);
|
||||||
|
blankNameFlags.push(flag.blankNameFlag);
|
||||||
}
|
}
|
||||||
const {name, alias} = readAgentAlias(line, p, end, aliases);
|
const {name, alias} = readAgentAlias(line, p, end, {
|
||||||
|
enableAlias: aliases,
|
||||||
|
allowBlankName,
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
alias,
|
alias,
|
||||||
flags,
|
flags: name ? flags : blankNameFlags,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -185,6 +185,38 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('parses source agents', () => {
|
||||||
|
const parsed = parser.parse('A -> *');
|
||||||
|
expect(parsed.stages).toEqual([
|
||||||
|
{
|
||||||
|
type: 'connect',
|
||||||
|
ln: jasmine.anything(),
|
||||||
|
agents: [
|
||||||
|
{name: 'A', alias: '', flags: []},
|
||||||
|
{name: '', alias: '', flags: ['source']},
|
||||||
|
],
|
||||||
|
label: jasmine.anything(),
|
||||||
|
options: jasmine.anything(),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses source agents with labels', () => {
|
||||||
|
const parsed = parser.parse('A -> *: foo');
|
||||||
|
expect(parsed.stages).toEqual([
|
||||||
|
{
|
||||||
|
type: 'connect',
|
||||||
|
ln: jasmine.anything(),
|
||||||
|
agents: [
|
||||||
|
{name: 'A', alias: '', flags: []},
|
||||||
|
{name: '', alias: '', flags: ['source']},
|
||||||
|
],
|
||||||
|
label: 'foo',
|
||||||
|
options: jasmine.anything(),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('converts multiple entries', () => {
|
it('converts multiple entries', () => {
|
||||||
const parsed = parser.parse('A -> B\nB -> A');
|
const parsed = parser.parse('A -> B\nB -> A');
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
|
|
|
@ -188,6 +188,7 @@ define([
|
||||||
theme: this.theme,
|
theme: this.theme,
|
||||||
agentInfos: this.agentInfos,
|
agentInfos: this.agentInfos,
|
||||||
visibleAgentIDs: this.visibleAgentIDs,
|
visibleAgentIDs: this.visibleAgentIDs,
|
||||||
|
momentaryAgentIDs: agentIDs,
|
||||||
textSizer: this.sizer,
|
textSizer: this.sizer,
|
||||||
addSpacing,
|
addSpacing,
|
||||||
addSeparation: this.addSeparation,
|
addSeparation: this.addSeparation,
|
||||||
|
@ -392,6 +393,7 @@ define([
|
||||||
id: agent.id,
|
id: agent.id,
|
||||||
formattedLabel: agent.formattedLabel,
|
formattedLabel: agent.formattedLabel,
|
||||||
anchorRight: agent.anchorRight,
|
anchorRight: agent.anchorRight,
|
||||||
|
isVirtualSource: agent.isVirtualSource,
|
||||||
index,
|
index,
|
||||||
x: null,
|
x: null,
|
||||||
latestYStart: null,
|
latestYStart: null,
|
||||||
|
|
|
@ -13,6 +13,7 @@ define(() => {
|
||||||
theme,
|
theme,
|
||||||
agentInfos,
|
agentInfos,
|
||||||
visibleAgentIDs,
|
visibleAgentIDs,
|
||||||
|
momentaryAgentIDs,
|
||||||
textSizer,
|
textSizer,
|
||||||
addSpacing,
|
addSpacing,
|
||||||
addSeparation,
|
addSeparation,
|
||||||
|
@ -25,6 +26,7 @@ define(() => {
|
||||||
theme,
|
theme,
|
||||||
agentInfos,
|
agentInfos,
|
||||||
visibleAgentIDs,
|
visibleAgentIDs,
|
||||||
|
momentaryAgentIDs,
|
||||||
textSizer,
|
textSizer,
|
||||||
addSpacing,
|
addSpacing,
|
||||||
addSeparation,
|
addSeparation,
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
define([
|
define([
|
||||||
|
'core/ArrayUtilities',
|
||||||
'./BaseComponent',
|
'./BaseComponent',
|
||||||
'svg/SVGUtilities',
|
'svg/SVGUtilities',
|
||||||
'svg/SVGShapes',
|
'svg/SVGShapes',
|
||||||
], (
|
], (
|
||||||
|
array,
|
||||||
BaseComponent,
|
BaseComponent,
|
||||||
svg,
|
svg,
|
||||||
SVGShapes
|
SVGShapes
|
||||||
|
@ -106,6 +108,18 @@ define([
|
||||||
];
|
];
|
||||||
|
|
||||||
class Connect extends BaseComponent {
|
class Connect extends BaseComponent {
|
||||||
|
separationPre({agentIDs}, env) {
|
||||||
|
const r = env.theme.connect.source.radius;
|
||||||
|
agentIDs.forEach((id) => {
|
||||||
|
const agentInfo = env.agentInfos.get(id);
|
||||||
|
if(!agentInfo.isVirtualSource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
agentInfo.currentRad = r;
|
||||||
|
agentInfo.currentMaxRad = Math.max(agentInfo.currentMaxRad, r);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
separation({label, agentIDs, options}, env) {
|
separation({label, agentIDs, options}, env) {
|
||||||
const config = env.theme.connect;
|
const config = env.theme.connect;
|
||||||
|
|
||||||
|
@ -147,6 +161,8 @@ define([
|
||||||
) * 2
|
) * 2
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
array.mergeSets(env.momentaryAgentIDs, agentIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSelfConnect({label, agentIDs, options}, env) {
|
renderSelfConnect({label, agentIDs, options}, env) {
|
||||||
|
@ -224,14 +240,59 @@ define([
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderSimpleLine(x0, x1, options, env) {
|
||||||
|
const dir = (x0 < x1) ? 1 : -1;
|
||||||
|
|
||||||
|
const config = env.theme.connect;
|
||||||
|
const line = config.line[options.line];
|
||||||
|
const lArrow = ARROWHEADS[options.left];
|
||||||
|
const rArrow = ARROWHEADS[options.right];
|
||||||
|
|
||||||
|
const rendered = line.renderFlat(line.attrs, {
|
||||||
|
x1: x0,
|
||||||
|
dx1: lArrow.lineGap(env.theme, line.attrs) * dir,
|
||||||
|
x2: x1,
|
||||||
|
dx2: -rArrow.lineGap(env.theme, line.attrs) * dir,
|
||||||
|
y: env.primaryY,
|
||||||
|
});
|
||||||
|
env.shapeLayer.appendChild(rendered.shape);
|
||||||
|
return rendered;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSimpleArrowheads(options, renderedLine, env, dir) {
|
||||||
|
const lArrow = ARROWHEADS[options.left];
|
||||||
|
const rArrow = ARROWHEADS[options.right];
|
||||||
|
|
||||||
|
lArrow.render(env.shapeLayer, env.theme, renderedLine.p1, dir);
|
||||||
|
rArrow.render(env.shapeLayer, env.theme, renderedLine.p2, -dir);
|
||||||
|
|
||||||
|
return {lArrow, rArrow};
|
||||||
|
}
|
||||||
|
|
||||||
|
renderVirtualSources(from, to, renderedLine, env) {
|
||||||
|
const config = env.theme.connect.source;
|
||||||
|
|
||||||
|
if(from.isVirtualSource) {
|
||||||
|
env.shapeLayer.appendChild(config.render({
|
||||||
|
x: renderedLine.p1.x - config.radius,
|
||||||
|
y: renderedLine.p1.y,
|
||||||
|
radius: config.radius,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if(to.isVirtualSource) {
|
||||||
|
env.shapeLayer.appendChild(config.render({
|
||||||
|
x: renderedLine.p2.x + config.radius,
|
||||||
|
y: renderedLine.p2.y,
|
||||||
|
radius: config.radius,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
renderSimpleConnect({label, agentIDs, options}, env) {
|
renderSimpleConnect({label, agentIDs, options}, env) {
|
||||||
const config = env.theme.connect;
|
const config = env.theme.connect;
|
||||||
const from = env.agentInfos.get(agentIDs[0]);
|
const from = env.agentInfos.get(agentIDs[0]);
|
||||||
const to = env.agentInfos.get(agentIDs[1]);
|
const to = env.agentInfos.get(agentIDs[1]);
|
||||||
|
|
||||||
const lArrow = ARROWHEADS[options.left];
|
|
||||||
const rArrow = ARROWHEADS[options.right];
|
|
||||||
|
|
||||||
const dir = (from.x < to.x) ? 1 : -1;
|
const dir = (from.x < to.x) ? 1 : -1;
|
||||||
|
|
||||||
const height = (
|
const height = (
|
||||||
|
@ -257,18 +318,12 @@ define([
|
||||||
SVGTextBlockClass: env.SVGTextBlockClass,
|
SVGTextBlockClass: env.SVGTextBlockClass,
|
||||||
});
|
});
|
||||||
|
|
||||||
const line = config.line[options.line];
|
const rendered = this.renderSimpleLine(x0, x1, options, env);
|
||||||
const rendered = line.renderFlat(line.attrs, {
|
const {
|
||||||
x1: x0,
|
lArrow,
|
||||||
dx1: lArrow.lineGap(env.theme, line.attrs) * dir,
|
rArrow
|
||||||
x2: x1,
|
} = this.renderSimpleArrowheads(options, rendered, env, dir);
|
||||||
dx2: -rArrow.lineGap(env.theme, line.attrs) * dir,
|
this.renderVirtualSources(from, to, rendered, env);
|
||||||
y,
|
|
||||||
});
|
|
||||||
env.shapeLayer.appendChild(rendered.shape);
|
|
||||||
|
|
||||||
lArrow.render(env.shapeLayer, env.theme, rendered.p1, dir);
|
|
||||||
rArrow.render(env.shapeLayer, env.theme, rendered.p2, -dir);
|
|
||||||
|
|
||||||
const arrowSpread = Math.max(
|
const arrowSpread = Math.max(
|
||||||
lArrow.height(env.theme),
|
lArrow.height(env.theme),
|
||||||
|
|
|
@ -150,6 +150,19 @@ define([
|
||||||
'line-height': LINE_HEIGHT,
|
'line-height': LINE_HEIGHT,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
source: {
|
||||||
|
radius: 2,
|
||||||
|
render: ({x, y, radius}) => {
|
||||||
|
return svg.make('circle', {
|
||||||
|
'cx': x,
|
||||||
|
'cy': y,
|
||||||
|
'r': radius,
|
||||||
|
'fill': '#000000',
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 1,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
mask: {
|
mask: {
|
||||||
padding: {
|
padding: {
|
||||||
top: 0,
|
top: 0,
|
||||||
|
|
|
@ -160,6 +160,19 @@ define([
|
||||||
'line-height': LINE_HEIGHT,
|
'line-height': LINE_HEIGHT,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
source: {
|
||||||
|
radius: 5,
|
||||||
|
render: ({x, y, radius}) => {
|
||||||
|
return svg.make('circle', {
|
||||||
|
'cx': x,
|
||||||
|
'cy': y,
|
||||||
|
'r': radius,
|
||||||
|
'fill': '#000000',
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 3,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
mask: {
|
mask: {
|
||||||
padding: {
|
padding: {
|
||||||
top: 1,
|
top: 1,
|
||||||
|
|
|
@ -157,6 +157,19 @@ define([
|
||||||
'line-height': LINE_HEIGHT,
|
'line-height': LINE_HEIGHT,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
source: {
|
||||||
|
radius: 2,
|
||||||
|
render: ({x, y, radius}) => {
|
||||||
|
return svg.make('circle', {
|
||||||
|
'cx': x,
|
||||||
|
'cy': y,
|
||||||
|
'r': radius,
|
||||||
|
'fill': '#000000',
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 1,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
mask: {
|
mask: {
|
||||||
padding: {
|
padding: {
|
||||||
top: 0,
|
top: 0,
|
||||||
|
|
|
@ -139,6 +139,19 @@ define([
|
||||||
'line-height': LINE_HEIGHT,
|
'line-height': LINE_HEIGHT,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
source: {
|
||||||
|
radius: 1,
|
||||||
|
render: ({x, y, radius}) => {
|
||||||
|
return svg.make('circle', {
|
||||||
|
'cx': x,
|
||||||
|
'cy': y,
|
||||||
|
'r': radius,
|
||||||
|
'fill': '#000000',
|
||||||
|
'stroke': '#000000',
|
||||||
|
'stroke-width': 1,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
mask: {
|
mask: {
|
||||||
padding: {
|
padding: {
|
||||||
top: 0,
|
top: 0,
|
||||||
|
@ -574,7 +587,7 @@ define([
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
shape: svg.make('path', Object.assign({'d': ln.nodes}, attrs)),
|
shape: svg.make('path', Object.assign({'d': ln.nodes}, attrs)),
|
||||||
p1: {x: ln.p1.x - dx1, y: ln.p2.y},
|
p1: {x: ln.p1.x - dx1, y: ln.p1.y},
|
||||||
p2: {x: ln.p2.x - dx2, y: ln.p2.y},
|
p2: {x: ln.p2.x - dx2, y: ln.p2.y},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue