mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-14 06:43:25 +08:00
Merge branch '5237-unified-layout-common-renderer' into knsv/new-shapes
This commit is contained in:
commit
82c247bb11
@ -97,7 +97,7 @@ export const openURLAndVerifyRendering = (
|
|||||||
const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
|
const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
|
||||||
|
|
||||||
cy.visit(url);
|
cy.visit(url);
|
||||||
// cy.window().should('have.property', 'rendered', true);
|
cy.window().should('have.property', 'rendered', true);
|
||||||
cy.get('svg').should('be.visible');
|
cy.get('svg').should('be.visible');
|
||||||
|
|
||||||
if (validation) {
|
if (validation) {
|
||||||
|
@ -236,7 +236,7 @@ describe('Block diagram', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('BL16: width alignment - blocks shold be equal in width', () => {
|
it('BL17: width alignment - blocks shold be equal in width', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`block-beta
|
`block-beta
|
||||||
A("This is the text")
|
A("This is the text")
|
||||||
@ -247,7 +247,7 @@ describe('Block diagram', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('BL17: block types 1 - square, rounded and circle', () => {
|
it('BL18: block types 1 - square, rounded and circle', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`block-beta
|
`block-beta
|
||||||
A["square"]
|
A["square"]
|
||||||
@ -258,7 +258,7 @@ describe('Block diagram', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('BL18: block types 2 - odd, diamond and hexagon', () => {
|
it('BL19: block types 2 - odd, diamond and hexagon', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`block-beta
|
`block-beta
|
||||||
A>"rect_left_inv_arrow"]
|
A>"rect_left_inv_arrow"]
|
||||||
@ -269,7 +269,7 @@ describe('Block diagram', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('BL19: block types 3 - stadium', () => {
|
it('BL20: block types 3 - stadium', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`block-beta
|
`block-beta
|
||||||
A(["stadium"])
|
A(["stadium"])
|
||||||
@ -278,7 +278,7 @@ describe('Block diagram', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('BL20: block types 4 - lean right, lean left, trapezoid and inv trapezoid', () => {
|
it('BL21: block types 4 - lean right, lean left, trapezoid and inv trapezoid', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`block-beta
|
`block-beta
|
||||||
A[/"lean right"/]
|
A[/"lean right"/]
|
||||||
@ -290,7 +290,7 @@ describe('Block diagram', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('BL21: block types 1 - square, rounded and circle', () => {
|
it('BL22: block types 1 - square, rounded and circle', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`block-beta
|
`block-beta
|
||||||
A["square"]
|
A["square"]
|
||||||
@ -301,7 +301,7 @@ describe('Block diagram', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('BL22: sizing - it should be possible to make a block wider', () => {
|
it('BL23: sizing - it should be possible to make a block wider', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`block-beta
|
`block-beta
|
||||||
A("rounded"):2
|
A("rounded"):2
|
||||||
@ -312,7 +312,7 @@ describe('Block diagram', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('BL23: sizing - it should be possible to make a composite block wider', () => {
|
it('BL24: sizing - it should be possible to make a composite block wider', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`block-beta
|
`block-beta
|
||||||
block:2
|
block:2
|
||||||
@ -324,7 +324,7 @@ describe('Block diagram', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('BL24: block in the middle with space on each side', () => {
|
it('BL25: block in the middle with space on each side', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`block-beta
|
`block-beta
|
||||||
columns 3
|
columns 3
|
||||||
@ -335,7 +335,7 @@ describe('Block diagram', () => {
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('BL25: space and an edge', () => {
|
it('BL26: space and an edge', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`block-beta
|
`block-beta
|
||||||
columns 5
|
columns 5
|
||||||
@ -345,7 +345,7 @@ describe('Block diagram', () => {
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('BL26: block sizes for regular blocks', () => {
|
it('BL27: block sizes for regular blocks', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`block-beta
|
`block-beta
|
||||||
columns 3
|
columns 3
|
||||||
@ -354,7 +354,7 @@ describe('Block diagram', () => {
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('BL27: composite block with a set width - f should use the available space', () => {
|
it('BL28: composite block with a set width - f should use the available space', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`block-beta
|
`block-beta
|
||||||
columns 3
|
columns 3
|
||||||
@ -363,11 +363,12 @@ describe('Block diagram', () => {
|
|||||||
f
|
f
|
||||||
end
|
end
|
||||||
g
|
g
|
||||||
`,
|
`,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('BL23: composite block with a set width - f and g should split the available space', () => {
|
|
||||||
|
it('BL29: composite block with a set width - f and g should split the available space', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`block-beta
|
`block-beta
|
||||||
columns 3
|
columns 3
|
||||||
@ -379,7 +380,7 @@ describe('Block diagram', () => {
|
|||||||
h
|
h
|
||||||
i
|
i
|
||||||
j
|
j
|
||||||
`,
|
`,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
|
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
|
||||||
|
|
||||||
describe('C4 diagram', () => {
|
describe('C4 diagram', () => {
|
||||||
it('should render a simple C4Context diagram', () => {
|
it('C4.1 should render a simple C4Context diagram', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`
|
`
|
||||||
C4Context
|
C4Context
|
||||||
@ -31,7 +31,7 @@ describe('C4 diagram', () => {
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should render a simple C4Container diagram', () => {
|
it('C4.2 should render a simple C4Container diagram', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`
|
`
|
||||||
C4Container
|
C4Container
|
||||||
@ -50,7 +50,7 @@ describe('C4 diagram', () => {
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should render a simple C4Component diagram', () => {
|
it('C4.3 should render a simple C4Component diagram', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`
|
`
|
||||||
C4Component
|
C4Component
|
||||||
@ -68,7 +68,7 @@ describe('C4 diagram', () => {
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should render a simple C4Dynamic diagram', () => {
|
it('C4.4 should render a simple C4Dynamic diagram', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`
|
`
|
||||||
C4Dynamic
|
C4Dynamic
|
||||||
@ -91,7 +91,7 @@ describe('C4 diagram', () => {
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should render a simple C4Deployment diagram', () => {
|
it('C4.5 should render a simple C4Deployment diagram', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`
|
`
|
||||||
C4Deployment
|
C4Deployment
|
||||||
|
@ -76,7 +76,7 @@ describe('Class diagram V2', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render a simple class diagram with different visibilities', () => {
|
it('2.1 should render a simple class diagram with different visibilities', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`
|
`
|
||||||
classDiagram-v2
|
classDiagram-v2
|
||||||
@ -93,7 +93,7 @@ describe('Class diagram V2', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render multiple class diagrams', () => {
|
it('3: should render multiple class diagrams', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
[
|
[
|
||||||
`
|
`
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import mermaid from './mermaid.esm.mjs';
|
import mermaid from './mermaid.esm.mjs';
|
||||||
// import flowchartELK from './mermaid-flowchart-elk.esm.mjs';
|
|
||||||
import { layouts } from './mermaid-layout-elk.esm.mjs';
|
import { layouts } from './mermaid-layout-elk.esm.mjs';
|
||||||
import externalExample from './mermaid-example-diagram.esm.mjs';
|
import externalExample from './mermaid-example-diagram.esm.mjs';
|
||||||
import zenUml from './mermaid-zenuml.esm.mjs';
|
import zenUml from './mermaid-zenuml.esm.mjs';
|
||||||
@ -48,7 +47,6 @@ const contentLoaded = async function () {
|
|||||||
document.getElementsByTagName('body')[0].appendChild(div);
|
document.getElementsByTagName('body')[0].appendChild(div);
|
||||||
}
|
}
|
||||||
|
|
||||||
// await mermaid.registerExternalDiagrams([externalExample, zenUml, flowchartELK]);
|
|
||||||
await mermaid.registerExternalDiagrams([externalExample, zenUml]);
|
await mermaid.registerExternalDiagrams([externalExample, zenUml]);
|
||||||
|
|
||||||
mermaid.registerLayoutLoaders(layouts);
|
mermaid.registerLayoutLoaders(layouts);
|
||||||
|
@ -925,7 +925,6 @@ export const intersection = (node, outsidePoint, insidePoint) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const outsideNode = (node, point) => {
|
const outsideNode = (node, point) => {
|
||||||
console.log('Checking bounds ', node, point);
|
|
||||||
const x = node.x;
|
const x = node.x;
|
||||||
const y = node.y;
|
const y = node.y;
|
||||||
const dx = Math.abs(point.x - x);
|
const dx = Math.abs(point.x - x);
|
||||||
|
@ -198,9 +198,6 @@ function getClassesFromDbInfo(dbInfoItem) {
|
|||||||
* Get classes from the db for the info item.
|
* Get classes from the db for the info item.
|
||||||
* If there aren't any or if dbInfoItem isn't defined, return an empty string.
|
* If there aren't any or if dbInfoItem isn't defined, return an empty string.
|
||||||
* Else create 1 string from the list of classes found
|
* Else create 1 string from the list of classes found
|
||||||
*
|
|
||||||
* @param {undefined | null | object} dbInfoItem
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
*/
|
||||||
function getStylesFromDbInfo(dbInfoItem) {
|
function getStylesFromDbInfo(dbInfoItem) {
|
||||||
if (dbInfoItem === undefined || dbInfoItem === null) {
|
if (dbInfoItem === undefined || dbInfoItem === null) {
|
||||||
|
@ -121,7 +121,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
|||||||
graph
|
graph
|
||||||
);
|
);
|
||||||
log.info(findNonClusterChild(node.id, graph));
|
log.info(findNonClusterChild(node.id, graph));
|
||||||
clusterDb[node.id] = { id: findNonClusterChild(node.id, graph), node };
|
clusterDb.set(node.id, { id: findNonClusterChild(node.id, graph), node });
|
||||||
// insertCluster(clusters, graph.node(v));
|
// insertCluster(clusters, graph.node(v));
|
||||||
} else {
|
} else {
|
||||||
log.trace('Node - the non recursive path XAX', v, node.id, node);
|
log.trace('Node - the non recursive path XAX', v, node.id, node);
|
||||||
@ -138,7 +138,16 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
|||||||
log.info('Edge ' + e.v + ' -> ' + e.w + ': ', e, ' ', JSON.stringify(graph.edge(e)));
|
log.info('Edge ' + e.v + ' -> ' + e.w + ': ', e, ' ', JSON.stringify(graph.edge(e)));
|
||||||
|
|
||||||
// Check if link is either from or to a cluster
|
// Check if link is either from or to a cluster
|
||||||
log.info('Fix', clusterDb, 'ids:', e.v, e.w, 'Translating: ', clusterDb[e.v], clusterDb[e.w]);
|
log.info(
|
||||||
|
'Fix',
|
||||||
|
clusterDb,
|
||||||
|
'ids:',
|
||||||
|
e.v,
|
||||||
|
e.w,
|
||||||
|
'Translating: ',
|
||||||
|
clusterDb.get(e.v),
|
||||||
|
clusterDb.get(e.w)
|
||||||
|
);
|
||||||
await insertEdgeLabel(edgeLabels, edge);
|
await insertEdgeLabel(edgeLabels, edge);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -147,20 +156,6 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
|||||||
|
|
||||||
await processEdges();
|
await processEdges();
|
||||||
|
|
||||||
// // Insert labels, this will insert them into the dom so that the width can be calculated
|
|
||||||
// // Also figure out which edges point to/from clusters and adjust them accordingly
|
|
||||||
// // Edges from/to clusters really points to the first child in the cluster.
|
|
||||||
// // TODO: pick optimal child in the cluster to us as link anchor
|
|
||||||
// await graph.edges().forEach(async function (e) {
|
|
||||||
// const edge = graph.edge(e.v, e.w, e.name);
|
|
||||||
// log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
|
||||||
// log.info('Edge ' + e.v + ' -> ' + e.w + ': ', e, ' ', JSON.stringify(graph.edge(e)));
|
|
||||||
|
|
||||||
// // Check if link is either from or to a cluster
|
|
||||||
// log.info('Fix', clusterDb, 'ids:', e.v, e.w, 'Translating: ', clusterDb[e.v], clusterDb[e.w]);
|
|
||||||
// await insertEdgeLabel(edgeLabels, edge);
|
|
||||||
// });
|
|
||||||
|
|
||||||
log.info('Graph before layout:', JSON.stringify(graphlibJson.write(graph)));
|
log.info('Graph before layout:', JSON.stringify(graphlibJson.write(graph)));
|
||||||
|
|
||||||
log.info('############################################# XXX');
|
log.info('############################################# XXX');
|
||||||
@ -198,7 +193,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
|||||||
node.y,
|
node.y,
|
||||||
graph.parent(v)
|
graph.parent(v)
|
||||||
);
|
);
|
||||||
clusterDb[node.id].node = node;
|
clusterDb.get(node.id).node = node;
|
||||||
positionNode(node);
|
positionNode(node);
|
||||||
} else {
|
} else {
|
||||||
// A tainted cluster node
|
// A tainted cluster node
|
||||||
@ -222,7 +217,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
|||||||
await insertCluster(clusters, node);
|
await insertCluster(clusters, node);
|
||||||
|
|
||||||
// A cluster in the non-recursive way
|
// A cluster in the non-recursive way
|
||||||
clusterDb[node.id].node = node;
|
clusterDb.get(node.id).node = node;
|
||||||
} else {
|
} else {
|
||||||
// Regular node
|
// Regular node
|
||||||
const parent = graph.node(node.parentId);
|
const parent = graph.node(node.parentId);
|
||||||
@ -255,7 +250,6 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
|||||||
const edge = graph.edge(e);
|
const edge = graph.edge(e);
|
||||||
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge);
|
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge);
|
||||||
|
|
||||||
// OBS HERE
|
|
||||||
edge.points.forEach((point) => (point.y += subGraphTitleTotalMargin / 2));
|
edge.points.forEach((point) => (point.y += subGraphTitleTotalMargin / 2));
|
||||||
const startNode = graph.node(e.v);
|
const startNode = graph.node(e.v);
|
||||||
var endNode = graph.node(e.w);
|
var endNode = graph.node(e.w);
|
||||||
@ -273,17 +267,8 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
|||||||
log.warn('Returning from recursive render XAX', elem, diff);
|
log.warn('Returning from recursive render XAX', elem, diff);
|
||||||
return { elem, diff };
|
return { elem, diff };
|
||||||
};
|
};
|
||||||
/**
|
|
||||||
* ###############################################################
|
|
||||||
* Render the graph
|
|
||||||
* ###############################################################
|
|
||||||
* @param data4Layout
|
|
||||||
* @param svg
|
|
||||||
* @param element
|
|
||||||
*/
|
|
||||||
export const render = async (data4Layout, svg, element) => {
|
export const render = async (data4Layout, svg, element) => {
|
||||||
// Create the input mermaid.graph
|
|
||||||
// console.log('XYZ data4Layout', data4Layout);
|
|
||||||
const graph = new graphlib.Graph({
|
const graph = new graphlib.Graph({
|
||||||
multigraph: true,
|
multigraph: true,
|
||||||
compound: true,
|
compound: true,
|
||||||
@ -305,15 +290,12 @@ export const render = async (data4Layout, svg, element) => {
|
|||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Org
|
|
||||||
|
|
||||||
insertMarkers(element, data4Layout.markers, data4Layout.type, data4Layout.diagramId);
|
insertMarkers(element, data4Layout.markers, data4Layout.type, data4Layout.diagramId);
|
||||||
clearNodes();
|
clearNodes();
|
||||||
clearEdges();
|
clearEdges();
|
||||||
clearClusters();
|
clearClusters();
|
||||||
clearGraphlib();
|
clearGraphlib();
|
||||||
|
|
||||||
// Add the nodes and edges to the graph
|
|
||||||
data4Layout.nodes.forEach((node) => {
|
data4Layout.nodes.forEach((node) => {
|
||||||
graph.setNode(node.id, { ...node });
|
graph.setNode(node.id, { ...node });
|
||||||
if (node.parentId) {
|
if (node.parentId) {
|
||||||
|
@ -3,39 +3,40 @@ import { log } from '$root/logger.js';
|
|||||||
import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
|
import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
|
||||||
import * as graphlibJson from 'dagre-d3-es/src/graphlib/json.js';
|
import * as graphlibJson from 'dagre-d3-es/src/graphlib/json.js';
|
||||||
|
|
||||||
export let clusterDb = {};
|
export let clusterDb = new Map();
|
||||||
let descendants = {};
|
let descendants = new Map();
|
||||||
let parents = {};
|
let parents = new Map();
|
||||||
|
|
||||||
export const clear = () => {
|
export const clear = () => {
|
||||||
descendants = {};
|
descendants.clear();
|
||||||
parents = {};
|
parents.clear();
|
||||||
clusterDb = {};
|
clusterDb.clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
const isDescendant = (id, ancestorId) => {
|
const isDescendant = (id, ancestorId) => {
|
||||||
log.trace('In isDescendant', ancestorId, ' ', id, ' = ', descendants[ancestorId].includes(id));
|
const ancestorDescendants = descendants.get(ancestorId) || [];
|
||||||
return descendants[ancestorId].includes(id);
|
log.trace('In isDescendant', ancestorId, ' ', id, ' = ', ancestorDescendants.includes(id));
|
||||||
|
return ancestorDescendants.includes(id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const edgeInCluster = (edge, clusterId) => {
|
const edgeInCluster = (edge, clusterId) => {
|
||||||
log.info('Descendants of ', clusterId, ' is ', descendants[clusterId]);
|
const clusterDescendants = descendants.get(clusterId) || [];
|
||||||
|
log.info('Descendants of ', clusterId, ' is ', clusterDescendants);
|
||||||
log.info('Edge is ', edge);
|
log.info('Edge is ', edge);
|
||||||
// Edges to/from the cluster is not in the cluster, they are in the parent
|
|
||||||
if (edge.v === clusterId || edge.w === clusterId) {
|
if (edge.v === clusterId || edge.w === clusterId) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!descendants[clusterId]) {
|
if (!clusterDescendants) {
|
||||||
log.debug('Tilt, ', clusterId, ',not in descendants');
|
log.debug('Tilt, ', clusterId, ',not in descendants');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
descendants[clusterId].includes(edge.v) ||
|
clusterDescendants.includes(edge.v) ||
|
||||||
isDescendant(edge.v, clusterId) ||
|
isDescendant(edge.v, clusterId) ||
|
||||||
isDescendant(edge.w, clusterId) ||
|
isDescendant(edge.w, clusterId) ||
|
||||||
descendants[clusterId].includes(edge.w)
|
clusterDescendants.includes(edge.w)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -51,7 +52,6 @@ const copy = (clusterId, graph, newGraph, rootId) => {
|
|||||||
);
|
);
|
||||||
const nodes = graph.children(clusterId) || [];
|
const nodes = graph.children(clusterId) || [];
|
||||||
|
|
||||||
// Include cluster node if it is not the root
|
|
||||||
if (clusterId !== rootId) {
|
if (clusterId !== rootId) {
|
||||||
nodes.push(clusterId);
|
nodes.push(clusterId);
|
||||||
}
|
}
|
||||||
@ -63,7 +63,7 @@ const copy = (clusterId, graph, newGraph, rootId) => {
|
|||||||
copy(node, graph, newGraph, rootId);
|
copy(node, graph, newGraph, rootId);
|
||||||
} else {
|
} else {
|
||||||
const data = graph.node(node);
|
const data = graph.node(node);
|
||||||
log.info('cp ', node, ' to ', rootId, ' with parent ', clusterId); //,node, data, ' parent is ', clusterId);
|
log.info('cp ', node, ' to ', rootId, ' with parent ', clusterId);
|
||||||
newGraph.setNode(node, data);
|
newGraph.setNode(node, data);
|
||||||
if (rootId !== graph.parent(node)) {
|
if (rootId !== graph.parent(node)) {
|
||||||
log.warn('Setting parent', node, graph.parent(node));
|
log.warn('Setting parent', node, graph.parent(node));
|
||||||
@ -91,7 +91,6 @@ const copy = (clusterId, graph, newGraph, rootId) => {
|
|||||||
const data = graph.edge(edge.v, edge.w, edge.name);
|
const data = graph.edge(edge.v, edge.w, edge.name);
|
||||||
log.info('Edge data', data, rootId);
|
log.info('Edge data', data, rootId);
|
||||||
try {
|
try {
|
||||||
// Do not copy edges in and out of the root cluster, they belong to the parent graph
|
|
||||||
if (edgeInCluster(edge, rootId)) {
|
if (edgeInCluster(edge, rootId)) {
|
||||||
log.info('Copying as ', edge.v, edge.w, data, edge.name);
|
log.info('Copying as ', edge.v, edge.w, data, edge.name);
|
||||||
newGraph.setEdge(edge.v, edge.w, data, edge.name);
|
newGraph.setEdge(edge.v, edge.w, data, edge.name);
|
||||||
@ -117,25 +116,19 @@ const copy = (clusterId, graph, newGraph, rootId) => {
|
|||||||
graph.removeNode(node);
|
graph.removeNode(node);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const extractDescendants = (id, graph) => {
|
export const extractDescendants = (id, graph) => {
|
||||||
// log.debug('Extracting ', id);
|
|
||||||
const children = graph.children(id);
|
const children = graph.children(id);
|
||||||
let res = [...children];
|
let res = [...children];
|
||||||
|
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
parents[child] = id;
|
parents.set(child, id);
|
||||||
res = [...res, ...extractDescendants(child, graph)];
|
res = [...res, ...extractDescendants(child, graph)];
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the graph, checking that all parent child relation points to existing nodes and that
|
|
||||||
* edges between nodes also ia correct. When not correct the function logs the discrepancies.
|
|
||||||
*
|
|
||||||
* @param graph
|
|
||||||
*/
|
|
||||||
export const validate = (graph) => {
|
export const validate = (graph) => {
|
||||||
const edges = graph.edges();
|
const edges = graph.edges();
|
||||||
log.trace('Edges: ', edges);
|
log.trace('Edges: ', edges);
|
||||||
@ -168,13 +161,6 @@ const findCommonEdges = (graph, id1, id2) => {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds a child that is not a cluster. When faking an edge between a node and a cluster.
|
|
||||||
*
|
|
||||||
* @param id
|
|
||||||
* @param {any} graph
|
|
||||||
* @param {string} clusterId
|
|
||||||
*/
|
|
||||||
export const findNonClusterChild = (id, graph, clusterId) => {
|
export const findNonClusterChild = (id, graph, clusterId) => {
|
||||||
const children = graph.children(id);
|
const children = graph.children(id);
|
||||||
log.trace('Searching children of id ', id, children);
|
log.trace('Searching children of id ', id, children);
|
||||||
@ -185,19 +171,10 @@ export const findNonClusterChild = (id, graph, clusterId) => {
|
|||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
const _id = findNonClusterChild(child, graph, clusterId);
|
const _id = findNonClusterChild(child, graph, clusterId);
|
||||||
|
|
||||||
// Edge chase where the cluster has an edge to a node and the selected
|
|
||||||
// child has a link to the same node
|
|
||||||
const commonEdges = findCommonEdges(graph, clusterId, _id);
|
const commonEdges = findCommonEdges(graph, clusterId, _id);
|
||||||
|
|
||||||
if (_id) {
|
if (_id) {
|
||||||
if (commonEdges.length > 0) {
|
if (commonEdges.length > 0) {
|
||||||
// console.log(
|
|
||||||
// '\x1B[44;93;4m abc24 The replacement also has an edge',
|
|
||||||
// clusterId,
|
|
||||||
// ' => ',
|
|
||||||
// _id,
|
|
||||||
// graph.edges()
|
|
||||||
// );
|
|
||||||
reserve = _id;
|
reserve = _id;
|
||||||
} else {
|
} else {
|
||||||
return _id;
|
return _id;
|
||||||
@ -208,17 +185,15 @@ export const findNonClusterChild = (id, graph, clusterId) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getAnchorId = (id) => {
|
const getAnchorId = (id) => {
|
||||||
if (!clusterDb[id]) {
|
if (!clusterDb.has(id)) {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
// If the cluster has no external connections
|
if (!clusterDb.get(id).externalConnections) {
|
||||||
if (!clusterDb[id].externalConnections) {
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the replacement node
|
if (clusterDb.has(id)) {
|
||||||
if (clusterDb[id]) {
|
return clusterDb.get(id).id;
|
||||||
return clusterDb[id].id;
|
|
||||||
}
|
}
|
||||||
return id;
|
return id;
|
||||||
};
|
};
|
||||||
@ -230,8 +205,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
|||||||
} else {
|
} else {
|
||||||
log.debug('Opting in, graph ');
|
log.debug('Opting in, graph ');
|
||||||
}
|
}
|
||||||
// Go through the nodes and for each cluster found, save a replacement node, this can be used when
|
|
||||||
// faking a link to a cluster
|
|
||||||
graph.nodes().forEach(function (id) {
|
graph.nodes().forEach(function (id) {
|
||||||
const children = graph.children(id);
|
const children = graph.children(id);
|
||||||
if (children.length > 0) {
|
if (children.length > 0) {
|
||||||
@ -241,34 +215,24 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
|||||||
' Replacement id in edges: ',
|
' Replacement id in edges: ',
|
||||||
findNonClusterChild(id, graph, id)
|
findNonClusterChild(id, graph, id)
|
||||||
);
|
);
|
||||||
descendants[id] = extractDescendants(id, graph);
|
descendants.set(id, extractDescendants(id, graph));
|
||||||
clusterDb[id] = { id: findNonClusterChild(id, graph, id), clusterData: graph.node(id) };
|
clusterDb.set(id, { id: findNonClusterChild(id, graph, id), clusterData: graph.node(id) });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check incoming and outgoing edges for each cluster
|
|
||||||
graph.nodes().forEach(function (id) {
|
graph.nodes().forEach(function (id) {
|
||||||
const children = graph.children(id);
|
const children = graph.children(id);
|
||||||
const edges = graph.edges();
|
const edges = graph.edges();
|
||||||
if (children.length > 0) {
|
if (children.length > 0) {
|
||||||
log.debug('Cluster identified', id, descendants);
|
log.debug('Cluster identified', id, descendants);
|
||||||
edges.forEach((edge) => {
|
edges.forEach((edge) => {
|
||||||
// log.debug('Edge, descendants: ', edge, descendants[id]);
|
const d1 = isDescendant(edge.v, id);
|
||||||
|
const d2 = isDescendant(edge.w, id);
|
||||||
|
|
||||||
// Check if any edge leaves the cluster (not the actual cluster, that's a link from the box)
|
if (d1 ^ d2) {
|
||||||
if (edge.v !== id && edge.w !== id) {
|
log.warn('Edge: ', edge, ' leaves cluster ', id);
|
||||||
// Any edge where either the one of the nodes is descending to the cluster but not the other
|
log.warn('Descendants of XXX ', id, ': ', descendants.get(id));
|
||||||
// if (descendants[id].indexOf(edge.v) < 0 && descendants[id].indexOf(edge.w) < 0) {
|
clusterDb.get(id).externalConnections = true;
|
||||||
|
|
||||||
const d1 = isDescendant(edge.v, id);
|
|
||||||
const d2 = isDescendant(edge.w, id);
|
|
||||||
|
|
||||||
// d1 xor d2 - if either d1 is true and d2 is false or the other way around
|
|
||||||
if (d1 ^ d2) {
|
|
||||||
log.warn('Edge: ', edge, ' leaves cluster ', id);
|
|
||||||
log.warn('Descendants of XXX ', id, ': ', descendants[id]);
|
|
||||||
clusterDb[id].externalConnections = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -276,18 +240,15 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
for (let id of Object.keys(clusterDb)) {
|
for (let id of clusterDb.keys()) {
|
||||||
const nonClusterChild = clusterDb[id].id;
|
const nonClusterChild = clusterDb.get(id).id;
|
||||||
const parent = graph.parent(nonClusterChild);
|
const parent = graph.parent(nonClusterChild);
|
||||||
|
|
||||||
// Change replacement node of id to parent of current replacement node if valid
|
if (parent !== id && clusterDb.has(parent) && !clusterDb.get(parent).externalConnections) {
|
||||||
if (parent !== id && clusterDb[parent] && !clusterDb[parent].externalConnections) {
|
clusterDb.get(id).id = parent;
|
||||||
clusterDb[id].id = parent;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For clusters with incoming and/or outgoing edges translate those edges to a real node
|
|
||||||
// in the cluster in order to fake the edge
|
|
||||||
graph.edges().forEach(function (e) {
|
graph.edges().forEach(function (e) {
|
||||||
const edge = graph.edge(e);
|
const edge = graph.edge(e);
|
||||||
log.warn('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
log.warn('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
||||||
@ -295,7 +256,6 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
|||||||
|
|
||||||
let v = e.v;
|
let v = e.v;
|
||||||
let w = e.w;
|
let w = e.w;
|
||||||
// Check if link is either from or to a cluster
|
|
||||||
log.warn(
|
log.warn(
|
||||||
'Fix XXX',
|
'Fix XXX',
|
||||||
clusterDb,
|
clusterDb,
|
||||||
@ -303,14 +263,13 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
|||||||
e.v,
|
e.v,
|
||||||
e.w,
|
e.w,
|
||||||
'Translating: ',
|
'Translating: ',
|
||||||
clusterDb[e.v],
|
clusterDb.get(e.v),
|
||||||
' --- ',
|
' --- ',
|
||||||
clusterDb[e.w]
|
clusterDb.get(e.w)
|
||||||
);
|
);
|
||||||
if (clusterDb[e.v] && clusterDb[e.w] && clusterDb[e.v] === clusterDb[e.w]) {
|
if (clusterDb.get(e.v) && clusterDb.get(e.w) && clusterDb.get(e.v) === clusterDb.get(e.w)) {
|
||||||
// cspell:ignore trixing
|
log.warn('Fixing and trying link to self - removing XXX', e.v, e.w, e.name);
|
||||||
log.warn('Fixing and trixing link to self - removing XXX', e.v, e.w, e.name);
|
log.warn('Fixing and trying - removing XXX', e.v, e.w, e.name);
|
||||||
log.warn('Fixing and trixing - removing XXX', e.v, e.w, e.name);
|
|
||||||
v = getAnchorId(e.v);
|
v = getAnchorId(e.v);
|
||||||
w = getAnchorId(e.w);
|
w = getAnchorId(e.w);
|
||||||
graph.removeEdge(e.v, e.w, e.name);
|
graph.removeEdge(e.v, e.w, e.name);
|
||||||
@ -352,19 +311,19 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
|||||||
graph.setEdge(v, specialId1, edge1, e.name + '-cyclic-special-0');
|
graph.setEdge(v, specialId1, edge1, e.name + '-cyclic-special-0');
|
||||||
graph.setEdge(specialId1, specialId2, edgeMid, e.name + '-cyclic-special-1');
|
graph.setEdge(specialId1, specialId2, edgeMid, e.name + '-cyclic-special-1');
|
||||||
graph.setEdge(specialId2, w, edge2, e.name + '-cyclic-special-2');
|
graph.setEdge(specialId2, w, edge2, e.name + '-cyclic-special-2');
|
||||||
} else if (clusterDb[e.v] || clusterDb[e.w]) {
|
} else if (clusterDb.get(e.v) || clusterDb.get(e.w)) {
|
||||||
log.warn('Fixing and trixing - removing XXX', e.v, e.w, e.name);
|
log.warn('Fixing and trying - removing XXX', e.v, e.w, e.name);
|
||||||
v = getAnchorId(e.v);
|
v = getAnchorId(e.v);
|
||||||
w = getAnchorId(e.w);
|
w = getAnchorId(e.w);
|
||||||
graph.removeEdge(e.v, e.w, e.name);
|
graph.removeEdge(e.v, e.w, e.name);
|
||||||
if (v !== e.v) {
|
if (v !== e.v) {
|
||||||
const parent = graph.parent(v);
|
const parent = graph.parent(v);
|
||||||
clusterDb[parent].externalConnections = true;
|
clusterDb.get(parent).externalConnections = true;
|
||||||
edge.fromCluster = e.v;
|
edge.fromCluster = e.v;
|
||||||
}
|
}
|
||||||
if (w !== e.w) {
|
if (w !== e.w) {
|
||||||
const parent = graph.parent(w);
|
const parent = graph.parent(w);
|
||||||
clusterDb[parent].externalConnections = true;
|
clusterDb.get(parent).externalConnections = true;
|
||||||
edge.toCluster = e.w;
|
edge.toCluster = e.w;
|
||||||
}
|
}
|
||||||
log.warn('Fix Replacing with XXX', v, w, e.name);
|
log.warn('Fix Replacing with XXX', v, w, e.name);
|
||||||
@ -390,9 +349,6 @@ export const extractor = (graph, depth) => {
|
|||||||
log.error('Bailing out');
|
log.error('Bailing out');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// For clusters without incoming and/or outgoing edges, create a new cluster-node
|
|
||||||
// containing the nodes and edges in the custer in a new graph
|
|
||||||
// for (let i = 0;)
|
|
||||||
let nodes = graph.nodes();
|
let nodes = graph.nodes();
|
||||||
let hasChildren = false;
|
let hasChildren = false;
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
@ -404,30 +360,23 @@ export const extractor = (graph, depth) => {
|
|||||||
log.debug('Done, no node has children', graph.nodes());
|
log.debug('Done, no node has children', graph.nodes());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// const clusters = Object.keys(clusterDb);
|
|
||||||
// clusters.forEach(clusterId => {
|
|
||||||
log.debug('Nodes = ', nodes, depth);
|
log.debug('Nodes = ', nodes, depth);
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
log.debug(
|
log.debug(
|
||||||
'Extracting node',
|
'Extracting node',
|
||||||
node,
|
node,
|
||||||
clusterDb,
|
clusterDb,
|
||||||
clusterDb[node] && !clusterDb[node].externalConnections,
|
clusterDb.has(node) && !clusterDb.get(node).externalConnections,
|
||||||
!graph.parent(node),
|
!graph.parent(node),
|
||||||
graph.node(node),
|
graph.node(node),
|
||||||
graph.children('D'),
|
graph.children('D'),
|
||||||
' Depth ',
|
' Depth ',
|
||||||
depth
|
depth
|
||||||
);
|
);
|
||||||
// Note that the node might have been removed after the Object.keys call so better check
|
if (!clusterDb.has(node)) {
|
||||||
// that it still is in the game
|
|
||||||
if (!clusterDb[node]) {
|
|
||||||
// Skip if the node is not a cluster
|
|
||||||
log.debug('Not a cluster', node, depth);
|
log.debug('Not a cluster', node, depth);
|
||||||
// break;
|
|
||||||
} else if (
|
} else if (
|
||||||
!clusterDb[node].externalConnections &&
|
!clusterDb.get(node).externalConnections &&
|
||||||
// !graph.parent(node) &&
|
|
||||||
graph.children(node) &&
|
graph.children(node) &&
|
||||||
graph.children(node).length > 0
|
graph.children(node).length > 0
|
||||||
) {
|
) {
|
||||||
@ -439,9 +388,9 @@ export const extractor = (graph, depth) => {
|
|||||||
|
|
||||||
const graphSettings = graph.graph();
|
const graphSettings = graph.graph();
|
||||||
let dir = graphSettings.rankdir === 'TB' ? 'LR' : 'TB';
|
let dir = graphSettings.rankdir === 'TB' ? 'LR' : 'TB';
|
||||||
if (clusterDb[node]?.clusterData?.dir) {
|
if (clusterDb.get(node)?.clusterData?.dir) {
|
||||||
dir = clusterDb[node].clusterData.dir;
|
dir = clusterDb.get(node).clusterData.dir;
|
||||||
log.warn('Fixing dir', clusterDb[node].clusterData.dir, dir);
|
log.warn('Fixing dir', clusterDb.get(node).clusterData.dir, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
const clusterGraph = new graphlib.Graph({
|
const clusterGraph = new graphlib.Graph({
|
||||||
@ -449,7 +398,7 @@ export const extractor = (graph, depth) => {
|
|||||||
compound: true,
|
compound: true,
|
||||||
})
|
})
|
||||||
.setGraph({
|
.setGraph({
|
||||||
rankdir: dir, // Todo: set proper spacing
|
rankdir: dir,
|
||||||
nodesep: 50,
|
nodesep: 50,
|
||||||
ranksep: 50,
|
ranksep: 50,
|
||||||
marginx: 8,
|
marginx: 8,
|
||||||
@ -464,8 +413,8 @@ export const extractor = (graph, depth) => {
|
|||||||
graph.setNode(node, {
|
graph.setNode(node, {
|
||||||
clusterNode: true,
|
clusterNode: true,
|
||||||
id: node,
|
id: node,
|
||||||
clusterData: clusterDb[node].clusterData,
|
clusterData: clusterDb.get(node).clusterData,
|
||||||
label: clusterDb[node].label,
|
label: clusterDb.get(node).label,
|
||||||
graph: clusterGraph,
|
graph: clusterGraph,
|
||||||
});
|
});
|
||||||
log.warn('New graph after copy node: (', node, ')', graphlibJson.write(clusterGraph));
|
log.warn('New graph after copy node: (', node, ')', graphlibJson.write(clusterGraph));
|
||||||
@ -475,7 +424,7 @@ export const extractor = (graph, depth) => {
|
|||||||
'Cluster ** ',
|
'Cluster ** ',
|
||||||
node,
|
node,
|
||||||
' **not meeting the criteria !externalConnections:',
|
' **not meeting the criteria !externalConnections:',
|
||||||
!clusterDb[node].externalConnections,
|
!clusterDb.get(node).externalConnections,
|
||||||
' no parent: ',
|
' no parent: ',
|
||||||
!graph.parent(node),
|
!graph.parent(node),
|
||||||
' children ',
|
' children ',
|
||||||
@ -502,7 +451,7 @@ const sorter = (graph, nodes) => {
|
|||||||
if (nodes.length === 0) {
|
if (nodes.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
let result = Object.assign(nodes);
|
let result = Object.assign([], nodes);
|
||||||
nodes.forEach((node) => {
|
nodes.forEach((node) => {
|
||||||
const children = graph.children(node);
|
const children = graph.children(node);
|
||||||
const sorted = sorter(graph, children);
|
const sorted = sorter(graph, children);
|
||||||
|
@ -154,7 +154,6 @@ export const positionEdgeLabel = (edge, paths) => {
|
|||||||
let x = edge.x;
|
let x = edge.x;
|
||||||
let y = edge.y;
|
let y = edge.y;
|
||||||
if (path) {
|
if (path) {
|
||||||
// // debugger;
|
|
||||||
const pos = utils.calcLabelPosition(path);
|
const pos = utils.calcLabelPosition(path);
|
||||||
log.debug(
|
log.debug(
|
||||||
'Moving label ' + edge.label + ' from (',
|
'Moving label ' + edge.label + ' from (',
|
||||||
@ -175,13 +174,11 @@ export const positionEdgeLabel = (edge, paths) => {
|
|||||||
el.attr('transform', `translate(${x}, ${y + subGraphTitleTotalMargin / 2})`);
|
el.attr('transform', `translate(${x}, ${y + subGraphTitleTotalMargin / 2})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
//let path = paths.updatedPath ? paths.updatedPath : paths.originalPath;
|
|
||||||
if (edge.startLabelLeft) {
|
if (edge.startLabelLeft) {
|
||||||
const el = terminalLabels.get(edge.id).startLeft;
|
const el = terminalLabels.get(edge.id).startLeft;
|
||||||
let x = edge.x;
|
let x = edge.x;
|
||||||
let y = edge.y;
|
let y = edge.y;
|
||||||
if (path) {
|
if (path) {
|
||||||
// debugger;
|
|
||||||
const pos = utils.calcTerminalLabelPosition(edge.arrowTypeStart ? 10 : 0, 'start_left', path);
|
const pos = utils.calcTerminalLabelPosition(edge.arrowTypeStart ? 10 : 0, 'start_left', path);
|
||||||
x = pos.x;
|
x = pos.x;
|
||||||
y = pos.y;
|
y = pos.y;
|
||||||
@ -193,7 +190,6 @@ export const positionEdgeLabel = (edge, paths) => {
|
|||||||
let x = edge.x;
|
let x = edge.x;
|
||||||
let y = edge.y;
|
let y = edge.y;
|
||||||
if (path) {
|
if (path) {
|
||||||
// debugger;
|
|
||||||
const pos = utils.calcTerminalLabelPosition(
|
const pos = utils.calcTerminalLabelPosition(
|
||||||
edge.arrowTypeStart ? 10 : 0,
|
edge.arrowTypeStart ? 10 : 0,
|
||||||
'start_right',
|
'start_right',
|
||||||
@ -209,7 +205,6 @@ export const positionEdgeLabel = (edge, paths) => {
|
|||||||
let x = edge.x;
|
let x = edge.x;
|
||||||
let y = edge.y;
|
let y = edge.y;
|
||||||
if (path) {
|
if (path) {
|
||||||
// debugger;
|
|
||||||
const pos = utils.calcTerminalLabelPosition(edge.arrowTypeEnd ? 10 : 0, 'end_left', path);
|
const pos = utils.calcTerminalLabelPosition(edge.arrowTypeEnd ? 10 : 0, 'end_left', path);
|
||||||
x = pos.x;
|
x = pos.x;
|
||||||
y = pos.y;
|
y = pos.y;
|
||||||
@ -275,21 +270,18 @@ export const intersection = (node, outsidePoint, insidePoint) => {
|
|||||||
res.y = outsidePoint.y;
|
res.y = outsidePoint.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug(`abc89 topp/bott calc, Q ${Q}, q ${q}, R ${R}, r ${r}`, res); // cspell: disable-line
|
log.debug(`abc89 top/bottom calc, Q ${Q}, q ${q}, R ${R}, r ${r}`, res);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
} else {
|
} else {
|
||||||
// Intersection onn sides of rect
|
// Intersection on sides of rect
|
||||||
if (insidePoint.x < outsidePoint.x) {
|
if (insidePoint.x < outsidePoint.x) {
|
||||||
r = outsidePoint.x - w - x;
|
r = outsidePoint.x - w - x;
|
||||||
} else {
|
} else {
|
||||||
r = x - w - outsidePoint.x;
|
r = x - w - outsidePoint.x;
|
||||||
}
|
}
|
||||||
let q = (Q * r) / R;
|
let q = (Q * r) / R;
|
||||||
// OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x + dx - w;
|
|
||||||
// OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r;
|
|
||||||
let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r;
|
let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r;
|
||||||
// let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r;
|
|
||||||
let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q;
|
let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q;
|
||||||
log.debug(`sides calc abc89, Q ${Q}, q ${q}, R ${R}, r ${r}`, { _x, _y });
|
log.debug(`sides calc abc89, Q ${Q}, q ${q}, R ${R}, r ${r}`, { _x, _y });
|
||||||
if (r === 0) {
|
if (r === 0) {
|
||||||
@ -306,49 +298,34 @@ export const intersection = (node, outsidePoint, insidePoint) => {
|
|||||||
return { x: _x, y: _y };
|
return { x: _x, y: _y };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/**
|
|
||||||
* This function will page a path and node where the last point(s) in the path is inside the node
|
|
||||||
* and return an update path ending by the border of the node.
|
|
||||||
*
|
|
||||||
* @param {Array} _points
|
|
||||||
* @param {any} boundaryNode
|
|
||||||
* @returns {Array} Points
|
|
||||||
*/
|
|
||||||
const cutPathAtIntersect = (_points, boundaryNode) => {
|
const cutPathAtIntersect = (_points, boundaryNode) => {
|
||||||
log.warn('abc88 cutPathAtIntersect', _points, boundaryNode);
|
log.warn('abc88 cutPathAtIntersect', _points, boundaryNode);
|
||||||
let points = [];
|
let points = [];
|
||||||
let lastPointOutside = _points[0];
|
let lastPointOutside = _points[0];
|
||||||
let isInside = false;
|
let isInside = false;
|
||||||
_points.forEach((point) => {
|
_points.forEach((point) => {
|
||||||
// const node = clusterDb[edge.toCluster].node;
|
|
||||||
log.info('abc88 checking point', point, boundaryNode);
|
log.info('abc88 checking point', point, boundaryNode);
|
||||||
|
|
||||||
// check if point is inside the boundary rect
|
|
||||||
if (!outsideNode(boundaryNode, point) && !isInside) {
|
if (!outsideNode(boundaryNode, point) && !isInside) {
|
||||||
// First point inside the rect found
|
|
||||||
// Calc the intersection coord between the point anf the last point outside the rect
|
|
||||||
const inter = intersection(boundaryNode, lastPointOutside, point);
|
const inter = intersection(boundaryNode, lastPointOutside, point);
|
||||||
log.debug('abc88 inside', point, lastPointOutside, inter);
|
log.debug('abc88 inside', point, lastPointOutside, inter);
|
||||||
log.debug('abc88 intersection', inter, boundaryNode);
|
log.debug('abc88 intersection', inter, boundaryNode);
|
||||||
|
|
||||||
// // Check case where the intersection is the same as the last point
|
|
||||||
let pointPresent = false;
|
let pointPresent = false;
|
||||||
points.forEach((p) => {
|
points.forEach((p) => {
|
||||||
pointPresent = pointPresent || (p.x === inter.x && p.y === inter.y);
|
pointPresent = pointPresent || (p.x === inter.x && p.y === inter.y);
|
||||||
});
|
});
|
||||||
// // if (!pointPresent) {
|
|
||||||
if (!points.some((e) => e.x === inter.x && e.y === inter.y)) {
|
if (!points.some((e) => e.x === inter.x && e.y === inter.y)) {
|
||||||
points.push(inter);
|
points.push(inter);
|
||||||
} else {
|
} else {
|
||||||
log.warn('abc88 no intersect', inter, points);
|
log.warn('abc88 no intersect', inter, points);
|
||||||
}
|
}
|
||||||
// points.push(inter);
|
|
||||||
isInside = true;
|
isInside = true;
|
||||||
} else {
|
} else {
|
||||||
// Outside
|
|
||||||
log.warn('abc88 outside', point, lastPointOutside);
|
log.warn('abc88 outside', point, lastPointOutside);
|
||||||
lastPointOutside = point;
|
lastPointOutside = point;
|
||||||
// points.push(point);
|
|
||||||
if (!isInside) {
|
if (!isInside) {
|
||||||
points.push(point);
|
points.push(point);
|
||||||
}
|
}
|
||||||
@ -358,13 +335,6 @@ const cutPathAtIntersect = (_points, boundaryNode) => {
|
|||||||
return points;
|
return points;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Given an edge, this function will return the corner points of the edge. This is defined as:
|
|
||||||
* one point that has a previous point and a next point such as the angle between the previous
|
|
||||||
* point and the next point is 90 degrees. Meaning that the previous point has the same x coordinate
|
|
||||||
* as the center point and at the same time the next point has the same y coordinate or vice versa.
|
|
||||||
* @param points
|
|
||||||
*/
|
|
||||||
function extractCornerPoints(points) {
|
function extractCornerPoints(points) {
|
||||||
const cornerPoints = [];
|
const cornerPoints = [];
|
||||||
const cornerPointPositions = [];
|
const cornerPointPositions = [];
|
||||||
@ -401,11 +371,6 @@ const findAdjacentPoint = function (pointA, pointB, distance) {
|
|||||||
return { x: pointB.x - ratio * xDiff, y: pointB.y - ratio * yDiff };
|
return { x: pointB.x - ratio * xDiff, y: pointB.y - ratio * yDiff };
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Given an array of points, this function will return a new array of points where the corners have been removed and replaced with
|
|
||||||
* adjacent points in each direction. SO a corder will be replaced with a point before and the point after the corner.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const fixCorners = function (lineData) {
|
const fixCorners = function (lineData) {
|
||||||
const { cornerPointPositions } = extractCornerPoints(lineData);
|
const { cornerPointPositions } = extractCornerPoints(lineData);
|
||||||
const newLineData = [];
|
const newLineData = [];
|
||||||
@ -415,7 +380,6 @@ const fixCorners = function (lineData) {
|
|||||||
const nextPoint = lineData[i + 1];
|
const nextPoint = lineData[i + 1];
|
||||||
const cornerPoint = lineData[i];
|
const cornerPoint = lineData[i];
|
||||||
|
|
||||||
// Find a new point on the line point 5 points back and push it to the new array
|
|
||||||
const newPrevPoint = findAdjacentPoint(prevPoint, cornerPoint, 5);
|
const newPrevPoint = findAdjacentPoint(prevPoint, cornerPoint, 5);
|
||||||
const newNextPoint = findAdjacentPoint(nextPoint, cornerPoint, 5);
|
const newNextPoint = findAdjacentPoint(nextPoint, cornerPoint, 5);
|
||||||
|
|
||||||
@ -466,9 +430,7 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
|
|||||||
var head = endNode;
|
var head = endNode;
|
||||||
|
|
||||||
if (head.intersect && tail.intersect) {
|
if (head.intersect && tail.intersect) {
|
||||||
// log.info('abc88 InsertEdge: 0.5', edge.start, '-->', edge.end, JSON.stringify(points));
|
|
||||||
points = points.slice(1, edge.points.length - 1);
|
points = points.slice(1, edge.points.length - 1);
|
||||||
// log.info('abc88 InsertEdge APA12: 0.7', edge.start, '-->', edge.end, JSON.stringify(points));
|
|
||||||
points.unshift(tail.intersect(points[0]));
|
points.unshift(tail.intersect(points[0]));
|
||||||
log.debug(
|
log.debug(
|
||||||
'Last point APA12',
|
'Last point APA12',
|
||||||
@ -481,59 +443,43 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
|
|||||||
);
|
);
|
||||||
points.push(head.intersect(points[points.length - 1]));
|
points.push(head.intersect(points[points.length - 1]));
|
||||||
}
|
}
|
||||||
// log.info('abc88 InsertEdge 2 SPLIT: ', points);
|
|
||||||
if (edge.toCluster) {
|
if (edge.toCluster) {
|
||||||
log.info('to cluster abc88', clusterDb[edge.toCluster]);
|
log.info('to cluster abc88', clusterDb.get(edge.toCluster));
|
||||||
points = cutPathAtIntersect(edge.points, clusterDb[edge.toCluster].node);
|
points = cutPathAtIntersect(edge.points, clusterDb.get(edge.toCluster).node);
|
||||||
|
|
||||||
pointsHasChanged = true;
|
pointsHasChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (edge.fromCluster) {
|
if (edge.fromCluster) {
|
||||||
log.debug('from cluster abc88', clusterDb[edge.fromCluster], JSON.stringify(points, null, 2));
|
log.debug(
|
||||||
points = cutPathAtIntersect(points.reverse(), clusterDb[edge.fromCluster].node).reverse();
|
'from cluster abc88',
|
||||||
// log.info('from cluster abc88 fixed', JSON.stringify(points, null, 2));
|
clusterDb.get(edge.fromCluster),
|
||||||
|
JSON.stringify(points, null, 2)
|
||||||
|
);
|
||||||
|
points = cutPathAtIntersect(points.reverse(), clusterDb.get(edge.fromCluster).node).reverse();
|
||||||
|
|
||||||
pointsHasChanged = true;
|
pointsHasChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The data for our line
|
|
||||||
let lineData = points.filter((p) => !Number.isNaN(p.y));
|
let lineData = points.filter((p) => !Number.isNaN(p.y));
|
||||||
// const { cornerPoints, cornerPointPositions } = extractCornerPoints(lineData);
|
|
||||||
lineData = fixCorners(lineData);
|
lineData = fixCorners(lineData);
|
||||||
let lastPoint = lineData[lineData.length - 1];
|
let lastPoint = lineData[lineData.length - 1];
|
||||||
if (lineData.length > 1) {
|
if (lineData.length > 1) {
|
||||||
lastPoint = lineData[lineData.length - 1];
|
lastPoint = lineData[lineData.length - 1];
|
||||||
const secondLastPoint = lineData[lineData.length - 2];
|
const secondLastPoint = lineData[lineData.length - 2];
|
||||||
// Calculate the mid point of the last two points
|
|
||||||
const diffX = (lastPoint.x - secondLastPoint.x) / 2;
|
const diffX = (lastPoint.x - secondLastPoint.x) / 2;
|
||||||
const diffY = (lastPoint.y - secondLastPoint.y) / 2;
|
const diffY = (lastPoint.y - secondLastPoint.y) / 2;
|
||||||
const midPoint = { x: secondLastPoint.x + diffX, y: secondLastPoint.y + diffY };
|
const midPoint = { x: secondLastPoint.x + diffX, y: secondLastPoint.y + diffY };
|
||||||
lineData.splice(-1, 0, midPoint);
|
lineData.splice(-1, 0, midPoint);
|
||||||
}
|
}
|
||||||
// This is the accessor function we talked about above
|
|
||||||
let curve = curveBasis;
|
let curve = curveBasis;
|
||||||
// curve = curveCardinal;
|
|
||||||
// curve = curveLinear;
|
|
||||||
// curve = curveNatural;
|
|
||||||
// curve = curveCatmullRom.alpha(0.5);
|
|
||||||
// curve = curveCatmullRom;
|
|
||||||
// curve = curveCardinal.tension(0.7);
|
|
||||||
// curve = curveMonotoneY;
|
|
||||||
// let curve = interpolateToCurve([5], curveNatural, 0.01, 10);
|
|
||||||
|
|
||||||
// Currently only flowcharts get the curve from the settings, perhaps this should
|
|
||||||
// be expanded to a common setting? Restricting it for now in order not to cause side-effects that
|
|
||||||
// have not been thought through
|
|
||||||
if (edge.curve) {
|
if (edge.curve) {
|
||||||
curve = edge.curve;
|
curve = edge.curve;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { x, y } = getLineFunctionsWithOffset(edge);
|
const { x, y } = getLineFunctionsWithOffset(edge);
|
||||||
// const lineFunction = edge.curve ? line().x(x).y(y).curve(curve) : roundedCornersLine;
|
|
||||||
const lineFunction = line().x(x).y(y).curve(curve);
|
const lineFunction = line().x(x).y(y).curve(curve);
|
||||||
|
|
||||||
// Construct stroke classes based on properties
|
|
||||||
let strokeClasses;
|
let strokeClasses;
|
||||||
switch (edge.thickness) {
|
switch (edge.thickness) {
|
||||||
case 'normal':
|
case 'normal':
|
||||||
@ -563,16 +509,11 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
|
|||||||
}
|
}
|
||||||
let svgPath;
|
let svgPath;
|
||||||
let linePath = lineFunction(lineData);
|
let linePath = lineFunction(lineData);
|
||||||
//convert to array (if not already)
|
|
||||||
const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style];
|
const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style];
|
||||||
if (edge.look === 'handdrawn') {
|
if (edge.look === 'handdrawn') {
|
||||||
const rc = rough.svg(elem);
|
const rc = rough.svg(elem);
|
||||||
Object.assign([], lineData);
|
Object.assign([], lineData);
|
||||||
// const svgPathNode = rc.path(lineFunction(ld.splice(0, ld.length-1)), {
|
|
||||||
// const svgPathNode = rc.path(lineFunction(ld), {
|
|
||||||
// roughness: 0.3,
|
|
||||||
// seed: handdrawnSeed,
|
|
||||||
// });
|
|
||||||
const svgPathNode = rc.path(linePath, {
|
const svgPathNode = rc.path(linePath, {
|
||||||
roughness: 0.3,
|
roughness: 0.3,
|
||||||
seed: handdrawnSeed,
|
seed: handdrawnSeed,
|
||||||
@ -582,7 +523,6 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
|
|||||||
|
|
||||||
svgPath = select(svgPathNode)
|
svgPath = select(svgPathNode)
|
||||||
.select('path')
|
.select('path')
|
||||||
// .attr('d', lineFunction(lineData))
|
|
||||||
.attr('id', edge.id)
|
.attr('id', edge.id)
|
||||||
.attr('class', ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : ''))
|
.attr('class', ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : ''))
|
||||||
.attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : '');
|
.attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : '');
|
||||||
@ -597,29 +537,8 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
|
|||||||
.attr('class', ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : ''))
|
.attr('class', ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : ''))
|
||||||
.attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : '');
|
.attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : '');
|
||||||
}
|
}
|
||||||
// DEBUG code, adds a red circle at each edge coordinate
|
|
||||||
// cornerPoints.forEach((point) => {
|
|
||||||
// elem
|
|
||||||
// .append('circle')
|
|
||||||
// .style('stroke', 'blue')
|
|
||||||
// .style('fill', 'blue')
|
|
||||||
// .attr('r', 3)
|
|
||||||
// .attr('cx', point.x)
|
|
||||||
// .attr('cy', point.y);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// lineData.forEach((point) => {
|
|
||||||
// elem
|
|
||||||
// .append('circle')
|
|
||||||
// .style('stroke', 'red')
|
|
||||||
// .style('fill', 'red')
|
|
||||||
// .attr('r', 1)
|
|
||||||
// .attr('cx', point.x)
|
|
||||||
// .attr('cy', point.y);
|
|
||||||
// });
|
|
||||||
|
|
||||||
let url = '';
|
let url = '';
|
||||||
// // TODO: Can we load this config only from the rendered graph type?
|
|
||||||
if (getConfig().flowchart.arrowMarkerAbsolute || getConfig().state.arrowMarkerAbsolute) {
|
if (getConfig().flowchart.arrowMarkerAbsolute || getConfig().state.arrowMarkerAbsolute) {
|
||||||
url =
|
url =
|
||||||
window.location.protocol +
|
window.location.protocol +
|
||||||
@ -627,8 +546,7 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
|
|||||||
window.location.host +
|
window.location.host +
|
||||||
window.location.pathname +
|
window.location.pathname +
|
||||||
window.location.search;
|
window.location.search;
|
||||||
url = url.replace(/\(/g, '\\(');
|
url = url.replace(/\(/g, '\\(').replace(/\)/g, '\\)');
|
||||||
url = url.replace(/\)/g, '\\)');
|
|
||||||
}
|
}
|
||||||
log.info('arrowTypeStart', edge.arrowTypeStart);
|
log.info('arrowTypeStart', edge.arrowTypeStart);
|
||||||
log.info('arrowTypeEnd', edge.arrowTypeEnd);
|
log.info('arrowTypeEnd', edge.arrowTypeEnd);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user