Merge pull request #1481 from mermaid-js/1474_missplaced_transitions

1474 missplaced transitions
This commit is contained in:
Knut Sveidqvist 2020-06-19 11:38:43 +02:00 committed by GitHub
commit 98a7800fae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 189 additions and 60 deletions

View File

@ -4,15 +4,16 @@
href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" href="https://fonts.googleapis.com/css?family=Montserrat&display=swap"
rel="stylesheet" rel="stylesheet"
/> />
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap" rel="stylesheet">
<style> <style>
body { body {
/* background: rgb(221, 208, 208); */ /* background: rgb(221, 208, 208); */
background:#333; /* background:#333; */
font-family: 'Arial'; font-family: 'Arial';
} }
h1 { color: white;} h1 { color: grey;}
.mermaid2 { .mermaid2 {
display: none; display: none;
} }
@ -20,44 +21,58 @@
</head> </head>
<body> <body>
<h1>info below</h1> <h1>info below</h1>
<div class="flex">
<div class="mermaid2" style="width: 50%; height: 20%;">
flowchart BT
subgraph two
b1
end
subgraph three
c1-->c2
end
c1 --apa apa apa--> b1
two --> c2
</div>
<div class="mermaid" style="width: 50%; height: 20%;"> <div class="mermaid" style="width: 50%; height: 20%;">
sequenceDiagram sequenceDiagram
Alice->>Bob:Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be Alice->>Bob:Extremely utterly long line of longness which had preivously overflown the actor box as it is much longer than what it should be
Bob->>Alice: I'm short though Bob->>Alice: I'm short though
</div> </div>
<div class="mermaid2" style="width: 50%; height: 20%;"> <div class="mermaid2" style="width: 50%; height: 20%;">
flowchart TB flowchart TB
subgraph 1 subgraph two
A --> B; b1
A -.-> C; end
A ==> D; subgraph three
A ==> E; c1-->c2
B <--> F end
C <--> F c1 --apa apa apa--> b1
D <--> F b1 --> c2
E <--> F </div>
end <div class="mermaid" style="width: 50%; height: 20%;">
subgraph 2
A2 --x B2; flowchart BT
A2 -.-x C2; subgraph a
A2 ==x D2; b1 -- ok --> b2
A2 ==x E2; end
B2 x--x F2 a -- sert --> c
C2 x--x F2 c --> d
D2 x--x F2 b1 --> d
E2 x--x F2 a --asd123 --> d
end </div>
subgraph 3 <div class="mermaid2" style="width: 50%; height: 20%;">
A3 --o B3; stateDiagram-v2
A3 -.-o C3; state A {
A3 ==o D3; B1 --> B2: ok
A3 ==o E3; }
B3 o--o F3 A --> C: sert
C3 o--o F3 C --> D
D3 o--o F3 B1 --> D
E3 o--o F3 A --> D: asd123
end </div>
</div>
<div class="mermaid2" style="width: 50%; height: 20%;">
</div> </div>
<script src="./mermaid.js"></script> <script src="./mermaid.js"></script>
@ -66,7 +81,7 @@ flowchart TB
// console.error('Mermaid error: ', err); // console.error('Mermaid error: ', err);
}; };
mermaid.initialize({ mermaid.initialize({
theme: 'dark', // theme: 'dark',
// arrowMarkerAbsolute: true, // arrowMarkerAbsolute: true,
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}', // themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
logLevel: 0, logLevel: 0,

View File

@ -2,6 +2,8 @@ import { logger } from '../logger'; // eslint-disable-line
import createLabel from './createLabel'; import createLabel from './createLabel';
import { line, curveBasis, select } from 'd3'; import { line, curveBasis, select } from 'd3';
import { getConfig } from '../config'; import { getConfig } from '../config';
import utils from '../utils';
// import { calcLabelPosition } from '../utils';
let edgeLabels = {}; let edgeLabels = {};
@ -39,11 +41,19 @@ export const insertEdgeLabel = (elem, edge) => {
edge.height = bbox.height; edge.height = bbox.height;
}; };
export const positionEdgeLabel = edge => { export const positionEdgeLabel = (edge, points) => {
logger.info('Moving label', edge.id, edge.label, edgeLabels[edge.id]); logger.info('Moving label', edge.id, edge.label, edgeLabels[edge.id]);
if (edge.label) { if (edge.label) {
const el = edgeLabels[edge.id]; const el = edgeLabels[edge.id];
el.attr('transform', 'translate(' + edge.x + ', ' + edge.y + ')'); let x = edge.x;
let y = edge.y;
if (points) {
// debugger;
const pos = utils.calcLabelPosition(points);
x = pos.x;
y = pos.y;
}
el.attr('transform', 'translate(' + x + ', ' + y + ')');
} }
}; };
@ -61,47 +71,80 @@ export const positionEdgeLabel = edge => {
// }; // };
const outsideNode = (node, point) => { const outsideNode = (node, point) => {
// logger.warn('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);
const dy = Math.abs(point.y - y); const dy = Math.abs(point.y - y);
const w = node.width / 2; const w = node.width / 2;
const h = node.height / 2; const h = node.height / 2;
if (dx > w || dy > h) { if (dx >= w || dy >= h) {
return true; return true;
} }
return false; return false;
}; };
const intersection = (node, outsidePoint, insidePoint) => { export const intersection = (node, outsidePoint, insidePoint) => {
logger.trace('intersection o:', outsidePoint, ' i:', insidePoint, node); logger.warn('intersection calc o:', outsidePoint, ' i:', insidePoint, node);
const x = node.x; const x = node.x;
const y = node.y; const y = node.y;
const dx = Math.abs(x - insidePoint.x); const dx = Math.abs(x - insidePoint.x);
const w = node.width / 2; const w = node.width / 2;
let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx; let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx;
const dy = Math.abs(y - insidePoint.y);
const h = node.height / 2; const h = node.height / 2;
let q = insidePoint.y < outsidePoint.y ? h - dy : h - dy;
const edges = {
x1: x - w,
x2: x + w,
y1: y - h,
y2: y + h
};
if (
outsidePoint.x === edges.x1 ||
outsidePoint.x === edges.x2 ||
outsidePoint.y === edges.y1 ||
outsidePoint.y === edges.y2
) {
// logger.warn('calc equals on edge');
return outsidePoint;
}
const Q = Math.abs(outsidePoint.y - insidePoint.y); const Q = Math.abs(outsidePoint.y - insidePoint.y);
const R = Math.abs(outsidePoint.x - insidePoint.x); const R = Math.abs(outsidePoint.x - insidePoint.x);
if (Math.abs(y - outsidePoint.y) * w > Math.abs(x - outsidePoint.x) * h || false) { // eslint-disable-line // log.warn();
if (Math.abs(y - outsidePoint.y) * w > Math.abs(x - outsidePoint.x) * h) { // eslint-disable-line
// Intersection is top or bottom of rect. // Intersection is top or bottom of rect.
// let q = insidePoint.y < outsidePoint.y ? outsidePoint.y - h - y : y - h - outsidePoint.y;
let q = insidePoint.y < outsidePoint.y ? outsidePoint.y - h - y : y - h - outsidePoint.y;
r = (R * q) / Q; r = (R * q) / Q;
const res = {
return { x: insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - r,
x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x - r, y: outsidePoint.y + q
y: insidePoint.y + q
}; };
logger.warn(`topp/bott calc, Q ${Q}, q ${q}, R ${R}, r ${r}`, res);
return res;
} else { } else {
q = (Q * r) / R; // Intersection onn sides of rect
r = (R * q) / Q; // q = (Q * r) / R;
// q = 2;
// r = (R * q) / Q;
if (insidePoint.x < outsidePoint.x) {
r = outsidePoint.x - w - x;
} else {
// r = outsidePoint.x - w - x;
r = x - w - outsidePoint.x;
}
let q = (q = (Q * r) / R);
logger.warn(`sides calc, Q ${Q}, q ${q}, R ${R}, r ${r}`, {
x: insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x + dx - w,
y: insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q
});
return { return {
x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x + dx - w, x: insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x + dx - w,
y: insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q y: insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q
}; };
} }
@ -110,7 +153,7 @@ const intersection = (node, outsidePoint, insidePoint) => {
//(edgePaths, e, edge, clusterDb, diagramtype, graph) //(edgePaths, e, edge, clusterDb, diagramtype, graph)
export const insertEdge = function(elem, e, edge, clusterDb, diagramType, graph) { export const insertEdge = function(elem, e, edge, clusterDb, diagramType, graph) {
let points = edge.points; let points = edge.points;
let pointsHasChanged = false;
const tail = graph.node(e.v); const tail = graph.node(e.v);
var head = graph.node(e.w); var head = graph.node(e.w);
@ -147,11 +190,12 @@ export const insertEdge = function(elem, e, edge, clusterDb, diagramType, graph)
} }
lastPointOutside = point; lastPointOutside = point;
}); });
pointsHasChanged = true;
} }
if (edge.fromCluster) { if (edge.fromCluster) {
logger.trace('edge', edge); logger.trace('edge', edge);
logger.trace('from cluster', clusterDb[edge.toCluster]); logger.warn('from cluster', clusterDb[edge.fromCluster]);
const updatedPoints = []; const updatedPoints = [];
let lastPointOutside; let lastPointOutside;
let isInside = false; let isInside = false;
@ -160,7 +204,7 @@ export const insertEdge = function(elem, e, edge, clusterDb, diagramType, graph)
const node = clusterDb[edge.fromCluster].node; const node = clusterDb[edge.fromCluster].node;
if (!outsideNode(node, point) && !isInside) { if (!outsideNode(node, point) && !isInside) {
logger.trace('inside', edge.toCluster, point); logger.warn('inside', edge.fromCluster, point, node);
// First point inside the rect // First point inside the rect
const insterection = intersection(node, lastPointOutside, point); const insterection = intersection(node, lastPointOutside, point);
@ -176,6 +220,7 @@ export const insertEdge = function(elem, e, edge, clusterDb, diagramType, graph)
lastPointOutside = point; lastPointOutside = point;
} }
points = updatedPoints; points = updatedPoints;
pointsHasChanged = true;
} }
// The data for our line // The data for our line
@ -275,4 +320,8 @@ export const insertEdge = function(elem, e, edge, clusterDb, diagramType, graph)
break; break;
default: default:
} }
if (pointsHasChanged) {
return points;
}
}; };

View File

@ -0,0 +1,63 @@
import { intersection } from './edges';
import { setLogLevel, logger } from '../logger';
describe('Graphlib decorations', () => {
let node;
beforeEach(function () {
setLogLevel(1);
node = { x:171, y:100, width: 210, height: 184};
});
describe('intersection', function () {
it('case 1 - intersection on left edge of box', function () {
const o = {x: 31, y: 143.2257070163421};
const i = {x: 99.3359375, y: 100}
const int = intersection(node, o, i);
expect(int.x).toBe(66)
expect(int.y).toBeCloseTo(122.139)
});
it('case 2 - intersection on left edge of box', function () {
const o = {x: 310.2578125, y: 169.88002060631462};
const i = {x: 127.96875, y: 100};
const node2 = {
height: 337.5,
width: 184.4609375,
x: 100.23046875,
y: 176.75
}
const int = intersection(node2, o, i);
expect(int.x).toBeCloseTo(192.4609375)
expect(int.y).toBeCloseTo(145.15711441743503)
});
it('case 3 - intersection on otop of box outside point greater then inside point', function () {
const o = {x: 157.21875, y: 38.83361558001693};
const i = {x: 104.1328125, y: 105};
const node2 = {
width: 211.96875,
x: 113.984375,
y: 164.25,
height: 176.5
}
const int = intersection(node2, o, i);
expect(int.x).toBeCloseTo(127.39979619565217)
expect(int.y).toBeCloseTo(76)
});
it('case 4 - intersection on top of box inside point greater then inside point', function () {
const o = {x: 144.65625, y: 38.83361558001693};
const i = {x: 197.7421875, y: 105};
const node2 = {
width: 211.96875,
x: 113.984375,
y: 164.25,
height: 176.5
}
const int = intersection(node2, o, i);
expect(int.x).toBeCloseTo(167.9232336956522)
expect(int.y).toBeCloseTo(76)
});
});
});

View File

@ -124,8 +124,8 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
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);
insertEdge(edgePaths, e, edge, clusterDb, diagramtype, graph); const updatedPath = insertEdge(edgePaths, e, edge, clusterDb, diagramtype, graph);
positionEdgeLabel(edge); positionEdgeLabel(edge, updatedPath);
}); });
return elem; return elem;

View File

@ -171,7 +171,9 @@ export const validate = graph => {
export const findNonClusterChild = (id, graph) => { export const findNonClusterChild = (id, graph) => {
// const node = graph.node(id); // const node = graph.node(id);
log.trace('Searching', id); log.trace('Searching', id);
const children = graph.children(id); // const children = graph.children(id).reverse();
const children = graph.children(id); //.reverse();
log.trace('Searching children of id ', id, children);
if (children.length < 1) { if (children.length < 1) {
log.trace('This is a valid node', id); log.trace('This is a valid node', id);
return id; return id;
@ -213,7 +215,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
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) {
log.trace( log.warn(
'Cluster identified', 'Cluster identified',
id, id,
' Replacement id in edges: ', ' Replacement id in edges: ',
@ -266,17 +268,17 @@ export const adjustClustersAndEdges = (graph, depth) => {
// Check if link is either from or to a cluster // Check if link is either from or to a cluster
log.trace('Fix', clusterDb, 'ids:', e.v, e.w, 'Translateing: ', clusterDb[e.v], clusterDb[e.w]); log.trace('Fix', clusterDb, 'ids:', e.v, e.w, 'Translateing: ', clusterDb[e.v], clusterDb[e.w]);
if (clusterDb[e.v] || clusterDb[e.w]) { if (clusterDb[e.v] || clusterDb[e.w]) {
log.trace('Fixing and trixing - removing', e.v, e.w, e.name); log.warn('Fixing and trixing - removing', 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) edge.fromCluster = e.v; if (v !== e.v) edge.fromCluster = e.v;
if (w !== e.w) edge.toCluster = e.w; if (w !== e.w) edge.toCluster = e.w;
log.trace('Replacing with', v, w, e.name); log.warn('Replacing with', v, w, e.name);
graph.setEdge(v, w, edge, e.name); graph.setEdge(v, w, edge, e.name);
} }
}); });
log.debug('Adjusted Graph', graphlib.json.write(graph)); log.warn('Adjusted Graph', graphlib.json.write(graph));
log.trace(clusterDb); log.trace(clusterDb);