mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-14 06:43:25 +08:00
#945 Rendering of labels and new label positioning algorithm
This commit is contained in:
parent
13baa43081
commit
3cffd1e3ed
@ -13,4 +13,33 @@ describe('State diagram', () => {
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('should render a simple state diagrams', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram
|
||||
[*] --> State1
|
||||
State1 --> State2
|
||||
State1 --> State3
|
||||
State1 --> [*]
|
||||
`,
|
||||
{ logLevel: 0 }
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('should render a simple state diagrams with labels', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
stateDiagram
|
||||
[*] --> State1
|
||||
State1 --> State2 : Transition 1
|
||||
State1 --> State3 : Transition 2
|
||||
State1 --> State4 : Transition 3
|
||||
State1 --> State5 : Transition 4
|
||||
State2 --> State3 : Transition 5
|
||||
State1 --> [*]
|
||||
`,
|
||||
{ logLevel: 0 }
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
});
|
||||
|
@ -106,7 +106,7 @@ line
|
||||
statement
|
||||
: idStatement DESCR
|
||||
| idStatement '-->' idStatement {yy.addRelation($1, $3);}
|
||||
| idStatement '-->' idStatement DESCR
|
||||
| idStatement '-->' idStatement DESCR {yy.addRelation($1, $3, $4.substr(1).trim());}
|
||||
| HIDE_EMPTY
|
||||
| scale WIDTH
|
||||
| COMPOSIT_STATE
|
||||
|
@ -41,7 +41,7 @@ export const getRelations = function() {
|
||||
return relations;
|
||||
};
|
||||
|
||||
export const addRelation = function(_id1, _id2) {
|
||||
export const addRelation = function(_id1, _id2, title) {
|
||||
let id1 = _id1;
|
||||
let id2 = _id2;
|
||||
let type1 = 'default';
|
||||
@ -56,10 +56,10 @@ export const addRelation = function(_id1, _id2) {
|
||||
id2 = 'end' + startCnt;
|
||||
type2 = 'end';
|
||||
}
|
||||
console.log(id1, id2);
|
||||
console.log(id1, id2, title);
|
||||
addState(id1, type1);
|
||||
addState(id2, type2);
|
||||
relations.push({ id1, id2 });
|
||||
relations.push({ id1, id2, title });
|
||||
};
|
||||
|
||||
export const addMember = function(className, member) {
|
||||
|
@ -304,6 +304,24 @@ describe('state diagram, ', function() {
|
||||
note right of NotShooting : This is a note on a composite state
|
||||
`;
|
||||
|
||||
parser.parse(str);
|
||||
});
|
||||
xit('should handle if statements', function() {
|
||||
const str = `stateDiagram\n
|
||||
[*] --> "Order Submitted"
|
||||
if "Payment Accepted" then
|
||||
-->[yes] "Pack products"
|
||||
--> "Send parcel"
|
||||
-right-> (*)
|
||||
else
|
||||
->[no] "Send error message"
|
||||
-->[Cancel Order] [*]
|
||||
endif
|
||||
}
|
||||
|
||||
note right of NotShooting : This is a note on a composite state
|
||||
`;
|
||||
|
||||
parser.parse(str);
|
||||
});
|
||||
});
|
||||
|
@ -4,6 +4,7 @@ import graphlib from 'graphlibrary';
|
||||
import { logger } from '../../logger';
|
||||
import stateDb from './stateDb';
|
||||
import { parser } from './parser/stateDiagram';
|
||||
import utils from '../../utils';
|
||||
|
||||
parser.yy = stateDb;
|
||||
|
||||
@ -136,7 +137,7 @@ const insertMarkers = function(elem) {
|
||||
.attr('markerHeight', 28)
|
||||
.attr('orient', 'auto')
|
||||
.append('path')
|
||||
.attr('d', 'M 18,7 L9,13 L14,7 L9,1 Z');
|
||||
.attr('d', 'M 19,7 L9,13 L14,7 L9,1 Z');
|
||||
};
|
||||
const drawStart = function(elem, stateDef) {
|
||||
logger.info('Rendering class ' + stateDef);
|
||||
@ -285,48 +286,67 @@ const drawEdge = function(elem, path, relation) {
|
||||
url = url.replace(/\)/g, '\\)');
|
||||
}
|
||||
|
||||
svgPath.attr(
|
||||
'marker-start',
|
||||
'url(' + url + '#' + getRelationType(stateDb.relationType.DEPENDENCY) + 'Start' + ')'
|
||||
);
|
||||
// svgPath.attr(
|
||||
// 'marker-start',
|
||||
// 'url(' + url + '#' + getRelationType(stateDb.relationType.DEPENDENCY) + 'Start' + ')'
|
||||
// );
|
||||
svgPath.attr(
|
||||
'marker-end',
|
||||
'url(' + url + '#' + getRelationType(stateDb.relationType.DEPENDENCY) + 'End' + ')'
|
||||
);
|
||||
|
||||
let x, y;
|
||||
const l = path.points.length;
|
||||
if (l % 2 !== 0 && l > 1) {
|
||||
const p1 = path.points[Math.floor(l / 2)];
|
||||
const p2 = path.points[Math.ceil(l / 2)];
|
||||
x = (p1.x + p2.x) / 2;
|
||||
y = (p1.y + p2.y) / 2;
|
||||
} else {
|
||||
const p = path.points[Math.floor(l / 2)];
|
||||
x = p.x;
|
||||
y = p.y;
|
||||
}
|
||||
// Figure ou where to put the label given the points
|
||||
// let x, y;
|
||||
// const l = path.points.length;
|
||||
// if (l % 2 !== 0 && l > 1) {
|
||||
// const p1 = path.points[Math.floor(l / 2)];
|
||||
// const p2 = path.points[Math.ceil(l / 2)];
|
||||
// x = (p1.x + p2.x) / 2;
|
||||
// y = (p1.y + p2.y) / 2;
|
||||
// } else {
|
||||
// const p = path.points[Math.floor(l / 2)];
|
||||
// x = p.x;
|
||||
// y = p.y;
|
||||
// }
|
||||
|
||||
// console.log('calcLabelPosition', utils);
|
||||
|
||||
if (typeof relation.title !== 'undefined') {
|
||||
const g = elem.append('g').attr('class', 'classLabel');
|
||||
const label = g
|
||||
.append('text')
|
||||
.attr('class', 'label')
|
||||
.attr('x', x)
|
||||
.attr('y', y)
|
||||
.attr('fill', 'red')
|
||||
.attr('text-anchor', 'middle')
|
||||
.text(relation.title);
|
||||
|
||||
window.label = label;
|
||||
const bounds = label.node().getBBox();
|
||||
const { x, y } = utils.calcLabelPosition(path.points);
|
||||
label.attr('x', x).attr('y', y);
|
||||
|
||||
const bounds = label.node().getBBox();
|
||||
g.insert('rect', ':first-child')
|
||||
.attr('class', 'box')
|
||||
.attr('x', bounds.x - conf.padding / 2)
|
||||
.attr('y', bounds.y - conf.padding / 2)
|
||||
.attr('width', bounds.width + conf.padding)
|
||||
.attr('height', bounds.height + conf.padding);
|
||||
|
||||
// Debug points
|
||||
// path.points.forEach(point => {
|
||||
// g.append('circle')
|
||||
// .style('stroke', 'red')
|
||||
// .style('fill', 'red')
|
||||
// .attr('r', 1)
|
||||
// .attr('cx', point.x)
|
||||
// .attr('cy', point.y);
|
||||
// });
|
||||
|
||||
// g.append('circle')
|
||||
// .style('stroke', 'blue')
|
||||
// .style('fill', 'blue')
|
||||
// .attr('r', 1)
|
||||
// .attr('cx', x)
|
||||
// .attr('cy', y);
|
||||
}
|
||||
|
||||
edgeCount++;
|
||||
@ -338,7 +358,7 @@ const drawEdge = function(elem, path, relation) {
|
||||
* @param {*} stateDef
|
||||
*/
|
||||
const drawState = function(elem, stateDef) {
|
||||
logger.info('Rendering class ' + stateDef);
|
||||
// logger.info('Rendering class ' + stateDef);
|
||||
|
||||
const addTspan = function(textEl, txt, isFirst) {
|
||||
const tSpan = textEl
|
||||
@ -416,14 +436,11 @@ export const draw = function(text, id) {
|
||||
// metadata about the node. In this case we're going to add labels to each of
|
||||
// our nodes.
|
||||
graph.setNode(node.id, node);
|
||||
logger.info('Org height: ' + node.height);
|
||||
// logger.info('Org height: ' + node.height);
|
||||
}
|
||||
|
||||
const relations = stateDb.getRelations();
|
||||
relations.forEach(function(relation) {
|
||||
logger.info(
|
||||
'tjoho' + getGraphId(relation.id1) + getGraphId(relation.id2) + JSON.stringify(relation)
|
||||
);
|
||||
graph.setEdge(getGraphId(relation.id1), getGraphId(relation.id2), {
|
||||
relation: relation
|
||||
});
|
||||
|
50
src/utils.js
50
src/utils.js
@ -73,8 +73,56 @@ export const interpolateToCurve = (interpolate, defaultCurve) => {
|
||||
return d3[curveName] || defaultCurve;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
export default {
|
||||
detectType,
|
||||
isSubstringInArray,
|
||||
interpolateToCurve
|
||||
interpolateToCurve,
|
||||
calcLabelPosition
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user