mermaid/src/utils.js

179 lines
5 KiB
JavaScript
Raw Normal View History

2019-09-12 21:58:57 +02:00
import * as d3 from 'd3';
import { logger } from './logger';
/**
* @function detectType
* Detects the type of the graph text.
* ```mermaid
* graph LR
* a-->b
* b-->c
* c-->d
* d-->e
* e-->f
* f-->g
* g-->h
* ```
*
* @param {string} text The text defining the graph
* @returns {string} A graph definition key
*/
2019-09-12 21:58:57 +02:00
export const detectType = function(text) {
text = text.replace(/^\s*%%.*\n/g, '\n');
logger.debug('Detecting diagram type based on the text ' + text);
2017-04-11 16:14:25 +02:00
if (text.match(/^\s*sequenceDiagram/)) {
2019-09-12 21:58:57 +02:00
return 'sequence';
2017-04-11 16:14:25 +02:00
}
2017-04-11 16:14:25 +02:00
if (text.match(/^\s*gantt/)) {
2019-09-12 21:58:57 +02:00
return 'gantt';
2017-04-11 16:14:25 +02:00
}
2017-04-11 16:14:25 +02:00
if (text.match(/^\s*classDiagram/)) {
2019-09-12 21:58:57 +02:00
return 'class';
2017-04-11 16:14:25 +02:00
}
2015-10-30 10:47:25 +01:00
2019-09-25 21:01:21 +02:00
if (text.match(/^\s*stateDiagram/)) {
return 'state';
}
2017-04-11 16:14:25 +02:00
if (text.match(/^\s*gitGraph/)) {
2019-09-12 21:58:57 +02:00
return 'git';
2017-04-11 16:14:25 +02:00
}
if (text.match(/^\s*info/)) {
2019-09-12 21:58:57 +02:00
return 'info';
}
2019-09-11 21:20:28 +02:00
if (text.match(/^\s*pie/)) {
2019-09-12 21:58:57 +02:00
return 'pie';
2019-09-11 21:20:28 +02:00
}
2019-09-12 21:58:57 +02:00
return 'flowchart';
};
/**
* @function isSubstringInArray
* Detects whether a substring in present in a given array
* @param {string} str The substring to detect
* @param {array} arr The array to search
* @returns {number} the array index containing the substring or -1 if not present
**/
2019-09-12 21:58:57 +02:00
export const isSubstringInArray = function(str, arr) {
2017-09-09 08:46:58 +02:00
for (let i = 0; i < arr.length; i++) {
2019-09-12 21:58:57 +02:00
if (arr[i].match(str)) return i;
}
2019-09-12 21:58:57 +02:00
return -1;
};
2017-09-10 13:41:34 +02:00
2018-03-09 06:33:35 +01:00
export const interpolateToCurve = (interpolate, defaultCurve) => {
2018-03-18 02:35:28 +01:00
if (!interpolate) {
2019-09-12 21:58:57 +02:00
return defaultCurve;
2018-03-18 02:35:28 +01:00
}
2019-09-12 21:58:57 +02:00
const curveName = `curve${interpolate.charAt(0).toUpperCase() + interpolate.slice(1)}`;
return d3[curveName] || defaultCurve;
};
2018-03-09 06:33:35 +01:00
const distance = (p1, p2) =>
p1 && p2 ? Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2)) : 0;
const traverseEdge = points => {
let prevPoint;
let totalDistance = 0;
points.forEach(point => {
totalDistance += distance(point, prevPoint);
prevPoint = point;
});
// Traverse half of total distance along points
const distanceToLabel = totalDistance / 2;
let remainingDistance = distanceToLabel;
let center;
prevPoint = undefined;
points.forEach(point => {
if (prevPoint && !center) {
const vectorDistance = distance(point, prevPoint);
if (vectorDistance < remainingDistance) {
remainingDistance -= vectorDistance;
} else {
// The point is remainingDistance from prevPoint in the vector between prevPoint and point
// Calculate the coordinates
const distanceRatio = remainingDistance / vectorDistance;
if (distanceRatio <= 0) center = prevPoint;
if (distanceRatio >= 1) center = { x: point.x, y: point.y };
if (distanceRatio > 0 && distanceRatio < 1) {
center = {
x: (1 - distanceRatio) * prevPoint.x + distanceRatio * point.x,
y: (1 - distanceRatio) * prevPoint.y + distanceRatio * point.y
};
}
}
}
prevPoint = point;
});
return center;
};
const calcLabelPosition = points => {
const p = traverseEdge(points);
return p;
};
const calcCardinalityPosition = (isRelationTypePresent, points, initialPosition) => {
let prevPoint;
2019-10-27 15:24:56 +01:00
let totalDistance = 0; // eslint-disable-line
if (points[0] !== initialPosition) {
points = points.reverse();
}
points.forEach(point => {
totalDistance += distance(point, prevPoint);
prevPoint = point;
});
// Traverse only 25 total distance along points to find cardinality point
const distanceToCardinalityPoint = 25;
let remainingDistance = distanceToCardinalityPoint;
let center;
prevPoint = undefined;
points.forEach(point => {
if (prevPoint && !center) {
const vectorDistance = distance(point, prevPoint);
if (vectorDistance < remainingDistance) {
remainingDistance -= vectorDistance;
} else {
// The point is remainingDistance from prevPoint in the vector between prevPoint and point
// Calculate the coordinates
const distanceRatio = remainingDistance / vectorDistance;
if (distanceRatio <= 0) center = prevPoint;
if (distanceRatio >= 1) center = { x: point.x, y: point.y };
if (distanceRatio > 0 && distanceRatio < 1) {
center = {
x: (1 - distanceRatio) * prevPoint.x + distanceRatio * point.x,
y: (1 - distanceRatio) * prevPoint.y + distanceRatio * point.y
};
}
}
}
prevPoint = point;
});
// if relation is present (Arrows will be added), change cardinality point off-set distance (d)
let d = isRelationTypePresent ? 10 : 5;
//Calculate Angle for x and y axis
let angle = Math.atan2(points[0].y - center.y, points[0].x - center.x);
let cardinalityPosition = { x: 0, y: 0 };
//Calculation cardinality position using angle, center point on the line/curve but pendicular and with offset-distance
cardinalityPosition.x = Math.sin(angle) * d + (points[0].x + center.x) / 2;
cardinalityPosition.y = -Math.cos(angle) * d + (points[0].y + center.y) / 2;
return cardinalityPosition;
};
2017-09-10 13:41:34 +02:00
export default {
detectType,
2018-03-09 06:33:35 +01:00
isSubstringInArray,
interpolateToCurve,
calcLabelPosition,
calcCardinalityPosition
2019-09-12 21:58:57 +02:00
};