mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-16 22:09:57 +02:00
Use markers with rounded crows feet
This commit is contained in:
@@ -1,21 +1,16 @@
|
|||||||
//import * as d3 from 'd3';
|
|
||||||
|
|
||||||
const ERMarkers = {
|
const ERMarkers = {
|
||||||
ONLY_ONE_START: 'ONLY_ONE_START',
|
ONLY_ONE_START: 'ONLY_ONE_START',
|
||||||
ONLY_ONE_END: 'ONLY_ONE_END',
|
ONLY_ONE_END: 'ONLY_ONE_END',
|
||||||
|
|
||||||
ZERO_OR_ONE_START: 'ZERO_OR_ONE_START',
|
ZERO_OR_ONE_START: 'ZERO_OR_ONE_START',
|
||||||
ZERO_OR_ONE_END: 'ZERO_OR_ONE_END',
|
ZERO_OR_ONE_END: 'ZERO_OR_ONE_END',
|
||||||
|
|
||||||
ONE_OR_MORE_START: 'ONE_OR_MORE_START',
|
ONE_OR_MORE_START: 'ONE_OR_MORE_START',
|
||||||
ONE_OR_MORE_END: 'ONE_OR_MORE_END',
|
ONE_OR_MORE_END: 'ONE_OR_MORE_END',
|
||||||
|
|
||||||
ZERO_OR_MORE_START: 'ZERO_OR_MORE_START',
|
ZERO_OR_MORE_START: 'ZERO_OR_MORE_START',
|
||||||
ZERO_OR_MORE_END: 'ZERO_OR_MORE_END'
|
ZERO_OR_MORE_END: 'ZERO_OR_MORE_END'
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Put the markers into the svg DOM for use in paths
|
* Put the markers into the svg DOM for later use with edge paths
|
||||||
*/
|
*/
|
||||||
const insertMarkers = function(elem, conf) {
|
const insertMarkers = function(elem, conf) {
|
||||||
let marker;
|
let marker;
|
||||||
@@ -96,73 +91,73 @@ const insertMarkers = function(elem, conf) {
|
|||||||
.append('defs')
|
.append('defs')
|
||||||
.append('marker')
|
.append('marker')
|
||||||
.attr('id', ERMarkers.ONE_OR_MORE_START)
|
.attr('id', ERMarkers.ONE_OR_MORE_START)
|
||||||
.attr('refX', 0)
|
.attr('refX', 18)
|
||||||
.attr('refY', 9)
|
.attr('refY', 18)
|
||||||
.attr('markerWidth', 18)
|
.attr('markerWidth', 45)
|
||||||
.attr('markerHeight', 18)
|
.attr('markerHeight', 36)
|
||||||
.attr('orient', 'auto')
|
.attr('orient', 'auto')
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('stroke', conf.stroke)
|
.attr('stroke', conf.stroke)
|
||||||
.attr('fill', 'none')
|
.attr('fill', 'none')
|
||||||
.attr('d', 'M0,0 L9,9 L0,18 M15,0 L15,18');
|
.attr('d', 'M0,18 Q 18,0 36,18 Q 18,36 0,18 M42,9 L42,27');
|
||||||
|
|
||||||
elem
|
elem
|
||||||
.append('defs')
|
.append('defs')
|
||||||
.append('marker')
|
.append('marker')
|
||||||
.attr('id', ERMarkers.ONE_OR_MORE_END)
|
.attr('id', ERMarkers.ONE_OR_MORE_END)
|
||||||
.attr('refX', 18)
|
.attr('refX', 27)
|
||||||
.attr('refY', 9)
|
.attr('refY', 18)
|
||||||
.attr('markerWidth', 21)
|
.attr('markerWidth', 45)
|
||||||
.attr('markerHeight', 18)
|
.attr('markerHeight', 36)
|
||||||
.attr('orient', 'auto')
|
.attr('orient', 'auto')
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('stroke', conf.stroke)
|
.attr('stroke', conf.stroke)
|
||||||
.attr('fill', 'none')
|
.attr('fill', 'none')
|
||||||
.attr('d', 'M3,0 L3,18 M18,0 L9,9 L18,18');
|
.attr('d', 'M3,9 L3,27 M9,18 Q27,0 45,18 Q27,36 9,18');
|
||||||
|
|
||||||
marker = elem
|
marker = elem
|
||||||
.append('defs')
|
.append('defs')
|
||||||
.append('marker')
|
.append('marker')
|
||||||
.attr('id', ERMarkers.ZERO_OR_MORE_START)
|
.attr('id', ERMarkers.ZERO_OR_MORE_START)
|
||||||
.attr('refX', 0)
|
.attr('refX', 18)
|
||||||
.attr('refY', 9)
|
.attr('refY', 18)
|
||||||
.attr('markerWidth', 30)
|
.attr('markerWidth', 57)
|
||||||
.attr('markerHeight', 18)
|
.attr('markerHeight', 36)
|
||||||
.attr('orient', 'auto');
|
.attr('orient', 'auto');
|
||||||
marker
|
marker
|
||||||
.append('circle')
|
.append('circle')
|
||||||
.attr('stroke', conf.stroke)
|
.attr('stroke', conf.stroke)
|
||||||
.attr('fill', 'white')
|
.attr('fill', 'white')
|
||||||
.attr('cx', 21)
|
.attr('cx', 48)
|
||||||
.attr('cy', 9)
|
.attr('cy', 18)
|
||||||
.attr('r', 6);
|
.attr('r', 6);
|
||||||
marker
|
marker
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('stroke', conf.stroke)
|
.attr('stroke', conf.stroke)
|
||||||
.attr('fill', 'none')
|
.attr('fill', 'none')
|
||||||
.attr('d', 'M0,0 L9,9 L0,18');
|
.attr('d', 'M0,18 Q18,0 36,18 Q18,36 0,18');
|
||||||
|
|
||||||
marker = elem
|
marker = elem
|
||||||
.append('defs')
|
.append('defs')
|
||||||
.append('marker')
|
.append('marker')
|
||||||
.attr('id', ERMarkers.ZERO_OR_MORE_END)
|
.attr('id', ERMarkers.ZERO_OR_MORE_END)
|
||||||
.attr('refX', 30)
|
.attr('refX', 39)
|
||||||
.attr('refY', 9)
|
.attr('refY', 18)
|
||||||
.attr('markerWidth', 30)
|
.attr('markerWidth', 57)
|
||||||
.attr('markerHeight', 18)
|
.attr('markerHeight', 36)
|
||||||
.attr('orient', 'auto');
|
.attr('orient', 'auto');
|
||||||
marker
|
marker
|
||||||
.append('circle')
|
.append('circle')
|
||||||
.attr('stroke', conf.stroke)
|
.attr('stroke', conf.stroke)
|
||||||
.attr('fill', 'white')
|
.attr('fill', 'white')
|
||||||
.attr('cx', 9)
|
.attr('cx', 9)
|
||||||
.attr('cy', 9)
|
.attr('cy', 18)
|
||||||
.attr('r', 6);
|
.attr('r', 6);
|
||||||
marker
|
marker
|
||||||
.append('path')
|
.append('path')
|
||||||
.attr('stroke', conf.stroke)
|
.attr('stroke', conf.stroke)
|
||||||
.attr('fill', 'none')
|
.attr('fill', 'none')
|
||||||
.attr('d', 'M30,0 L21,9 L30,18');
|
.attr('d', 'M21,18 Q39,0 57,18 Q39,36 21,18');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@@ -16,9 +16,10 @@ export const setConf = function(cnf) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that adds the entities as vertices
|
* Function that adds the entities as vertices in the graph prior to laying out
|
||||||
* @param entities The entities to be added to the graph
|
* @param entities The entities to be added to the graph
|
||||||
* @param g The graph that is to be drawn
|
* @param g The graph that is to be drawn
|
||||||
|
* @returns {Object} The object containing all the entities as properties
|
||||||
*/
|
*/
|
||||||
const addEntities = function(entities, g) {
|
const addEntities = function(entities, g) {
|
||||||
const keys = Object.keys(entities);
|
const keys = Object.keys(entities);
|
||||||
@@ -36,6 +37,7 @@ const addEntities = function(entities, g) {
|
|||||||
id: entity
|
id: entity
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
return entities;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,26 +94,38 @@ const drawEntities = function(diagram, entities, g, svgId) {
|
|||||||
});
|
});
|
||||||
}; // drawEntities
|
}; // drawEntities
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add each relationship to the graph
|
||||||
|
* @param relationships the relationships to be added
|
||||||
|
* @param g the graph
|
||||||
|
* @return {Array} The array of relationships
|
||||||
|
*/
|
||||||
const addRelationships = function(relationships, g) {
|
const addRelationships = function(relationships, g) {
|
||||||
relationships.forEach(function(r) {
|
relationships.forEach(function(r) {
|
||||||
g.setEdge(r.entityA, r.entityB, { relationship: r });
|
g.setEdge(r.entityA, r.entityB, { relationship: r });
|
||||||
});
|
});
|
||||||
|
return relationships;
|
||||||
}; // addRelationships
|
}; // addRelationships
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
const drawRelationships = function(diagram, relationships, g) {
|
const drawRelationships = function(diagram, relationships, g) {
|
||||||
relationships.forEach(function(rel) {
|
relationships.forEach(function(rel) {
|
||||||
//drawRelationship(diagram, rel, g);
|
|
||||||
drawRelationshipFromLayout(diagram, rel, g);
|
drawRelationshipFromLayout(diagram, rel, g);
|
||||||
});
|
});
|
||||||
}; // drawRelationships
|
}; // drawRelationships
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw a relationship using edge information from the graph
|
||||||
|
* @param diagram the svg node
|
||||||
|
* @param rel the relationship to draw in the svg
|
||||||
|
* @param g the graph containing the edge information
|
||||||
|
*/
|
||||||
const drawRelationshipFromLayout = function(diagram, rel, g) {
|
const drawRelationshipFromLayout = function(diagram, rel, g) {
|
||||||
// Find the edge relating to this relationship
|
// Find the edge relating to this relationship
|
||||||
const edge = g.edge({ v: rel.entityA, w: rel.entityB });
|
const edge = g.edge({ v: rel.entityA, w: rel.entityB });
|
||||||
|
|
||||||
// Using it's points, generate a line function
|
|
||||||
edge.points = edge.points.filter(p => !Number.isNaN(p.y)); // TODO: why is necessary?
|
|
||||||
|
|
||||||
// Get a function that will generate the line path
|
// Get a function that will generate the line path
|
||||||
const lineFunction = d3
|
const lineFunction = d3
|
||||||
.line()
|
.line()
|
||||||
@@ -130,7 +144,7 @@ const drawRelationshipFromLayout = function(diagram, rel, g) {
|
|||||||
.attr('stroke', conf.stroke)
|
.attr('stroke', conf.stroke)
|
||||||
.attr('fill', 'none');
|
.attr('fill', 'none');
|
||||||
|
|
||||||
// TODO: Understand this
|
// TODO: Understand this better
|
||||||
let url = '';
|
let url = '';
|
||||||
if (conf.arrowMarkerAbsolute) {
|
if (conf.arrowMarkerAbsolute) {
|
||||||
url =
|
url =
|
||||||
@@ -143,8 +157,8 @@ const drawRelationshipFromLayout = function(diagram, rel, g) {
|
|||||||
url = url.replace(/\)/g, '\\)');
|
url = url.replace(/\)/g, '\\)');
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: change the way enums are imported
|
// Decide which start and end markers it needs. It may be possible to be more concise here
|
||||||
// Decide which start and end markers it needs
|
// by reversing a start marker to make an end marker...but this will do for now
|
||||||
switch (rel.cardinality) {
|
switch (rel.cardinality) {
|
||||||
case erDb.Cardinality.ONLY_ONE_TO_ONE_OR_MORE:
|
case erDb.Cardinality.ONLY_ONE_TO_ONE_OR_MORE:
|
||||||
svgPath.attr('marker-start', 'url(' + url + '#' + erMarkers.ERMarkers.ONLY_ONE_START + ')');
|
svgPath.attr('marker-start', 'url(' + url + '#' + erMarkers.ERMarkers.ONLY_ONE_START + ')');
|
||||||
@@ -249,285 +263,10 @@ const drawRelationshipFromLayout = function(diagram, rel, g) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
const drawRelationship = function(diagram, relationship, g) {
|
|
||||||
// Set the from and to co-ordinates using the graph vertices
|
|
||||||
|
|
||||||
let from = {
|
|
||||||
x: g.node(relationship.entityA).x,
|
|
||||||
y: g.node(relationship.entityA).y
|
|
||||||
};
|
|
||||||
|
|
||||||
let to = {
|
|
||||||
x: g.node(relationship.entityB).x,
|
|
||||||
y: g.node(relationship.entityB).y
|
|
||||||
};
|
|
||||||
|
|
||||||
diagram
|
|
||||||
.append('line')
|
|
||||||
.attr('x1', from.x)
|
|
||||||
.attr('y1', from.y)
|
|
||||||
.attr('x2', to.x)
|
|
||||||
.attr('y2', to.y)
|
|
||||||
.attr('stroke', conf.stroke);
|
|
||||||
}; // drawRelationship
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
const drawFeet = function(diagram, relationships, g) {
|
|
||||||
relationships.forEach(function(rel) {
|
|
||||||
// Get the points of intersection with the entities
|
|
||||||
const nodeA = g.node(rel.entityA);
|
|
||||||
const nodeB = g.node(rel.entityB);
|
|
||||||
|
|
||||||
const fromIntersect = getIntersection(
|
|
||||||
nodeB.x - nodeA.x,
|
|
||||||
nodeB.y - nodeA.y,
|
|
||||||
nodeA.x,
|
|
||||||
nodeA.y,
|
|
||||||
nodeA.width / 2,
|
|
||||||
nodeA.height / 2
|
|
||||||
);
|
|
||||||
|
|
||||||
dot(diagram, fromIntersect, conf.intersectColor);
|
|
||||||
|
|
||||||
const toIntersect = getIntersection(
|
|
||||||
nodeA.x - nodeB.x,
|
|
||||||
nodeA.y - nodeB.y,
|
|
||||||
nodeB.x,
|
|
||||||
nodeB.y,
|
|
||||||
nodeB.width / 1,
|
|
||||||
nodeB.height / 2
|
|
||||||
);
|
|
||||||
|
|
||||||
dot(diagram, toIntersect, conf.intersectColor);
|
|
||||||
|
|
||||||
// Get the ankle and heel points
|
|
||||||
const anklePoints = getJoints(rel, fromIntersect, toIntersect, conf.ankleDistance);
|
|
||||||
|
|
||||||
dot(diagram, { x: anklePoints.from.x, y: anklePoints.from.y }, conf.ankleColor);
|
|
||||||
dot(diagram, { x: anklePoints.to.x, y: anklePoints.to.y }, conf.ankleColor);
|
|
||||||
|
|
||||||
const heelPoints = getJoints(rel, fromIntersect, toIntersect, conf.heelDistance);
|
|
||||||
|
|
||||||
dot(diagram, { x: heelPoints.from.x, y: heelPoints.from.y }, conf.heelColor);
|
|
||||||
dot(diagram, { x: heelPoints.to.x, y: heelPoints.to.y }, conf.heelColor);
|
|
||||||
|
|
||||||
// Get the toe points
|
|
||||||
const toePoints = getToes(rel, fromIntersect, toIntersect, conf.toeDistance);
|
|
||||||
|
|
||||||
if (toePoints) {
|
|
||||||
dot(diagram, { x: toePoints.from.top.x, y: toePoints.from.top.y }, conf.toeColor);
|
|
||||||
dot(diagram, { x: toePoints.from.bottom.x, y: toePoints.from.bottom.y }, conf.toeColor);
|
|
||||||
dot(diagram, { x: toePoints.to.top.x, y: toePoints.to.top.y }, conf.toeColor);
|
|
||||||
dot(diagram, { x: toePoints.to.bottom.x, y: toePoints.to.bottom.y }, conf.toeColor);
|
|
||||||
|
|
||||||
let paths = [];
|
|
||||||
paths.push(getToePath(heelPoints.from, toePoints.from.top, nodeA));
|
|
||||||
paths.push(getToePath(heelPoints.from, toePoints.from.bottom, nodeA));
|
|
||||||
paths.push(getToePath(heelPoints.to, toePoints.to.top, nodeB));
|
|
||||||
paths.push(getToePath(heelPoints.to, toePoints.to.bottom, nodeB));
|
|
||||||
|
|
||||||
for (const path of paths) {
|
|
||||||
diagram
|
|
||||||
.append('path')
|
|
||||||
.attr('d', path)
|
|
||||||
.attr('stroke', conf.stroke)
|
|
||||||
.attr('fill', 'none');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}; // drawFeet
|
|
||||||
|
|
||||||
const getToePath = function(heel, toe, tip) {
|
|
||||||
if (conf.toeStyle === 'straight') {
|
|
||||||
return `M ${heel.x} ${heel.y} L ${toe.x} ${toe.y} L ${tip.x} ${tip.y}`;
|
|
||||||
} else {
|
|
||||||
return `M ${heel.x} ${heel.y} Q ${toe.x} ${toe.y} ${tip.x} ${tip.y}`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
const getToes = function(relationship, fromPoint, toPoint, distance) {
|
|
||||||
if (conf.toeStyle === 'curved') {
|
|
||||||
distance *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
const gradient = (fromPoint.y - toPoint.y) / (fromPoint.x - toPoint.x);
|
|
||||||
const toeYDelta = getXDelta(distance, gradient);
|
|
||||||
const toeXDelta = toeYDelta * Math.abs(gradient);
|
|
||||||
|
|
||||||
if (gradient > 0) {
|
|
||||||
const topToe = function(point) {
|
|
||||||
return {
|
|
||||||
x: point.x + toeXDelta,
|
|
||||||
y: point.y - toeYDelta
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const bottomToe = function(point) {
|
|
||||||
return {
|
|
||||||
x: point.x - toeXDelta,
|
|
||||||
y: point.y + toeYDelta
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const lower = {
|
|
||||||
top: fromPoint.x < toPoint.x ? topToe(toPoint) : topToe(fromPoint),
|
|
||||||
bottom: fromPoint.x < toPoint.x ? bottomToe(toPoint) : bottomToe(fromPoint)
|
|
||||||
};
|
|
||||||
|
|
||||||
const upper = {
|
|
||||||
top: fromPoint.x < toPoint.x ? topToe(fromPoint) : topToe(toPoint),
|
|
||||||
bottom: fromPoint.x < toPoint.x ? bottomToe(fromPoint) : bottomToe(toPoint)
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
to: fromPoint.x < toPoint.x ? lower : upper,
|
|
||||||
from: fromPoint.x < toPoint.x ? upper : lower
|
|
||||||
};
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
if (fromPoint.x < toPoint.x) {
|
|
||||||
// Scenario A
|
|
||||||
|
|
||||||
return {
|
|
||||||
to: {
|
|
||||||
top: {
|
|
||||||
x: toPoint.x + toeXDelta,
|
|
||||||
y: toPoint.y - toeYDelta
|
|
||||||
},
|
|
||||||
bottom: {
|
|
||||||
x: toPoint.x - toeXDelta,
|
|
||||||
y: toPoint.y + toeYDelta
|
|
||||||
}
|
|
||||||
},
|
|
||||||
from: {
|
|
||||||
top: {
|
|
||||||
x: fromPoint.x + toeXDelta,
|
|
||||||
y: fromPoint.y - toeYDelta
|
|
||||||
},
|
|
||||||
bottom: {
|
|
||||||
x: fromPoint.x - toeXDelta,
|
|
||||||
y: fromPoint.y + toeYDelta
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Scenario E
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
}; // getToes
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
const getJoints = function(relationship, fromPoint, toPoint, distance) {
|
|
||||||
const gradient = (fromPoint.y - toPoint.y) / (fromPoint.x - toPoint.x);
|
|
||||||
let jointXDelta = getXDelta(distance, gradient);
|
|
||||||
let jointYDelta = jointXDelta * Math.abs(gradient);
|
|
||||||
|
|
||||||
let toX, toY;
|
|
||||||
let fromX, fromY;
|
|
||||||
|
|
||||||
if (gradient > 0) {
|
|
||||||
if (fromPoint.x < toPoint.x) {
|
|
||||||
// Scenario A
|
|
||||||
} else {
|
|
||||||
// Scenario E
|
|
||||||
jointXDelta *= -1;
|
|
||||||
jointYDelta *= -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
toX = toPoint.x - jointXDelta;
|
|
||||||
toY = toPoint.y - jointYDelta;
|
|
||||||
fromX = fromPoint.x + jointXDelta;
|
|
||||||
fromY = fromPoint.y + jointYDelta;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gradient < 0) {
|
|
||||||
if (fromPoint.x < toPoint.x) {
|
|
||||||
// Scenario C
|
|
||||||
jointXDelta *= -1;
|
|
||||||
jointYDelta *= -1;
|
|
||||||
} else {
|
|
||||||
// Scenario G
|
|
||||||
}
|
|
||||||
|
|
||||||
toX = toPoint.x + jointXDelta;
|
|
||||||
toY = toPoint.y - jointYDelta;
|
|
||||||
fromX = fromPoint.x - jointXDelta;
|
|
||||||
fromY = fromPoint.y + jointYDelta;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isFinite(gradient)) {
|
|
||||||
if (fromPoint.y < toPoint.y) {
|
|
||||||
// Scenario B
|
|
||||||
} else {
|
|
||||||
// Scenario F
|
|
||||||
jointXDelta *= -1;
|
|
||||||
jointYDelta *= -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
toX = toPoint.x;
|
|
||||||
toY = toPoint.y - distance;
|
|
||||||
fromX = fromPoint.x;
|
|
||||||
fromY = fromPoint.y + distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gradient === 0) {
|
|
||||||
if (fromPoint.x < toPoint.x) {
|
|
||||||
// Scenario D
|
|
||||||
} else {
|
|
||||||
// Scenario H
|
|
||||||
jointXDelta *= -1;
|
|
||||||
jointYDelta *= -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
toX = toPoint.x - distance;
|
|
||||||
toY = toPoint.y;
|
|
||||||
fromX = fromPoint.x + distance;
|
|
||||||
fromY = fromPoint.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
from: { x: fromX, y: fromY },
|
|
||||||
to: { x: toX, y: toY }
|
|
||||||
};
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
const getXDelta = function(hypotenuse, gradient) {
|
|
||||||
return Math.sqrt((hypotenuse * hypotenuse) / (Math.abs(gradient) + 1));
|
|
||||||
};
|
|
||||||
|
|
||||||
const getIntersection = function(dx, dy, cx, cy, w, h) {
|
|
||||||
if (Math.abs(dy / dx) < h / w) {
|
|
||||||
// Hit vertical edge of box
|
|
||||||
return { x: cx + (dx > 0 ? w : -w), y: cy + (dy * w) / Math.abs(dx) };
|
|
||||||
} else {
|
|
||||||
// Hit horizontal edge of box
|
|
||||||
return { x: cx + (dx * h) / Math.abs(dy), y: cy + (dy > 0 ? h : -h) };
|
|
||||||
}
|
|
||||||
}; // getIntersection
|
|
||||||
|
|
||||||
const dot = function(diagram, p, color) {
|
|
||||||
// stick a small circle at point p
|
|
||||||
if (conf.dots) {
|
|
||||||
diagram
|
|
||||||
.append('circle')
|
|
||||||
.attr('cx', p.x)
|
|
||||||
.attr('cy', p.y)
|
|
||||||
.attr('r', conf.dotRadius)
|
|
||||||
.attr('fill', color);
|
|
||||||
}
|
|
||||||
}; // dot
|
|
||||||
*/
|
|
||||||
/**
|
/**
|
||||||
* Draw en E-R diagram in the tag with id: id based on the text definition of the graph
|
* Draw en E-R diagram in the tag with id: id based on the text definition of the diagram
|
||||||
* @param text
|
* @param text the text of the diagram
|
||||||
* @param id
|
* @param id the unique id of the DOM node that contains the diagram
|
||||||
*/
|
*/
|
||||||
export const draw = function(text, id) {
|
export const draw = function(text, id) {
|
||||||
logger.info('Drawing ER diagram');
|
logger.info('Drawing ER diagram');
|
||||||
@@ -543,25 +282,26 @@ export const draw = function(text, id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get a reference to the diagram node
|
// Get a reference to the diagram node
|
||||||
const diagram = d3.select(`[id='${id}']`);
|
const svg = d3.select(`[id='${id}']`);
|
||||||
|
|
||||||
// Add cardinality 'marker' definitions to the svg
|
// Add cardinality marker definitions to the svg
|
||||||
erMarkers.insertMarkers(diagram, conf);
|
erMarkers.insertMarkers(svg, conf);
|
||||||
|
|
||||||
// Create the graph
|
// Create the graph
|
||||||
let g;
|
let g;
|
||||||
|
|
||||||
// TODO: Explore directed vs undirected graphs, and how the layout is affected
|
// TODO: Explore directed vs undirected graphs, and how the layout is affected
|
||||||
// An E-R diagram could be said to be undirected, but there is merit in setting
|
// An E-R diagram could be said to be undirected, but there is merit in setting
|
||||||
// the direction from parent to child (1 to many) as this influences graphlib to
|
// the direction from parent to child in a one-to-many as this influences graphlib to
|
||||||
// put the parent above the child, which is intuitive
|
// put the parent above the child (does it?), which is intuitive. Most relationships
|
||||||
|
// in ER diagrams are one-to-many.
|
||||||
g = new graphlib.Graph({
|
g = new graphlib.Graph({
|
||||||
multigraph: true,
|
multigraph: true,
|
||||||
directed: true,
|
directed: true,
|
||||||
compound: false
|
compound: false
|
||||||
})
|
})
|
||||||
.setGraph({
|
.setGraph({
|
||||||
rankdir: 'TB',
|
rankdir: 'LR',
|
||||||
marginx: 20,
|
marginx: 20,
|
||||||
marginy: 20,
|
marginy: 20,
|
||||||
nodesep: 100,
|
nodesep: 100,
|
||||||
@@ -571,31 +311,18 @@ export const draw = function(text, id) {
|
|||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch the entities (which will become vertices)
|
// Add the entities and relationships to the graph
|
||||||
const entities = erDb.getEntities();
|
const entities = addEntities(erDb.getEntities(), g);
|
||||||
|
const relationships = addRelationships(erDb.getRelationships(), g);
|
||||||
// Add all the entities to the graph
|
|
||||||
addEntities(entities, g);
|
|
||||||
|
|
||||||
const relationships = erDb.getRelationships();
|
|
||||||
// Add all the relationships as edges on the graph
|
|
||||||
addRelationships(relationships, g);
|
|
||||||
|
|
||||||
// Set up an SVG group so that we can translate the final graph.
|
|
||||||
// TODO: This is redundant -just use diagram from above
|
|
||||||
const svg = d3.select(`[id="${id}"]`);
|
|
||||||
|
|
||||||
dagre.layout(g); // Node and edge positions will be updated
|
dagre.layout(g); // Node and edge positions will be updated
|
||||||
|
|
||||||
// Run the renderer. This is what draws the final graph.
|
// Draw the relationships first because their markers need to be
|
||||||
//const element = d3.select('#' + id + ' g');
|
// clipped by the entity boxes
|
||||||
//render(element, g);
|
drawRelationships(svg, relationships, g);
|
||||||
|
drawEntities(svg, entities, g, id);
|
||||||
|
|
||||||
//drawFeet(diagram, relationships, g);
|
const padding = 8; // TODO: move this to config
|
||||||
drawRelationships(diagram, relationships, g);
|
|
||||||
drawEntities(diagram, entities, g, id);
|
|
||||||
|
|
||||||
const padding = 8;
|
|
||||||
|
|
||||||
const svgBounds = svg.node().getBBox();
|
const svgBounds = svg.node().getBBox();
|
||||||
const width = svgBounds.width + padding * 4;
|
const width = svgBounds.width + padding * 4;
|
||||||
|
@@ -369,7 +369,7 @@ const config = {
|
|||||||
/**
|
/**
|
||||||
* Stroke color of box edges and lines
|
* Stroke color of box edges and lines
|
||||||
*/
|
*/
|
||||||
stroke: 'purple',
|
stroke: 'gray',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill color of entity boxes
|
* Fill color of entity boxes
|
||||||
@@ -377,52 +377,11 @@ const config = {
|
|||||||
fill: 'honeydew',
|
fill: 'honeydew',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Distance of the 'ankle' from the intersection point
|
* Opacity of entity boxes - if you want to see how the crows feet
|
||||||
|
* retain their elegant joins to the boxes regardless of the angle of incidence
|
||||||
|
* then override this to something less than 100%
|
||||||
*/
|
*/
|
||||||
ankleDistance: 35,
|
fillOpacity: '100%'
|
||||||
|
|
||||||
/**
|
|
||||||
* Distance of the 'heel' from the intersection point
|
|
||||||
*/
|
|
||||||
heelDistance: 20,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Distance of the side 'toes' perpendicular to the intersection point
|
|
||||||
*/
|
|
||||||
toeDistance: 12,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The style of the toes on the crow's foot: either 'curved' or 'straight'
|
|
||||||
*/
|
|
||||||
toeStyle: 'curved',
|
|
||||||
|
|
||||||
/**
|
|
||||||
* THE REMAINING CONFIG OPTIONS FOR 'er' DIAGRAMS ARE EXPERIMENTAL AND ARE USEFUL
|
|
||||||
* DURING DEVELOPMENT BUT WILL PROBABLY BE REMOVED BEFORE E-R DIAGRAMS ARE PRODUCTIONIZED.
|
|
||||||
* THEY ARE HELPFUL IN DIAGNOSING POSITIONAL AND LAYOUT-RELATED ISSUES; THEY WOULDN'T
|
|
||||||
* LOOK GOOD ON REAL DIAGRAMS
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Opacity of entity boxes - helpful when < 100% to see lines 'behind' the box
|
|
||||||
fillOpacity: '100%',
|
|
||||||
|
|
||||||
// Whether to show dots at important points in the diagram geometry
|
|
||||||
dots: false,
|
|
||||||
|
|
||||||
// Radius of dots
|
|
||||||
dotRadius: 1.5,
|
|
||||||
|
|
||||||
// Color of intersection point dots
|
|
||||||
intersectColor: 'green',
|
|
||||||
|
|
||||||
// Color of 'ankle' dots
|
|
||||||
ankleColor: 'red',
|
|
||||||
|
|
||||||
// Color of 'heel' dots
|
|
||||||
heelColor: 'blue',
|
|
||||||
|
|
||||||
// Color of 'toe' dots
|
|
||||||
toeColor: 'darkorchid'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user