mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-17 06:20:07 +02:00
Change rendering algorithm
This commit is contained in:
@@ -21,6 +21,7 @@ export const setConf = function(cnf) {
|
|||||||
* @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
|
* @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);
|
||||||
|
|
||||||
@@ -39,61 +40,91 @@ const addEntities = function(entities, g) {
|
|||||||
});
|
});
|
||||||
return entities;
|
return entities;
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
/**
|
/**
|
||||||
* Use D3 to construct the svg elements for the entities
|
* Use D3 to construct the svg elements for the entities
|
||||||
* @param diagram the svg node that contains the diagram
|
* @param svgNode the svg node that contains the diagram
|
||||||
* @param entities the entities to be drawn
|
* @param entities the entities to be drawn
|
||||||
* @param g the dagre graph that contains the vertex and edge definitions post-layout
|
* @param g the dagre graph that contains the vertex and edge definitions post-layout
|
||||||
*/
|
*/
|
||||||
const drawEntities = function(diagram, entities, g, svgId) {
|
const drawEntities = function(svgNode, entities, svgId, graph) {
|
||||||
// For each vertex in the graph:
|
const keys = Object.keys(entities);
|
||||||
// - append the text label centred in the right place
|
let firstOne;
|
||||||
// - get it's bounding box and calculate the size of the enclosing rectangle
|
|
||||||
// - insert the enclosing rectangle
|
|
||||||
|
|
||||||
g.nodes().forEach(function(v) {
|
keys.forEach(function(id) {
|
||||||
console.debug('Handling node ', v);
|
// Create a group for each entity
|
||||||
|
const groupNode = svgNode.append('g').attr('id', id);
|
||||||
|
|
||||||
// Get the centre co-ordinate of the node so that we can centre the entity name
|
firstOne = firstOne === undefined ? id : firstOne;
|
||||||
const centre = { x: g.node(v).x, y: g.node(v).y };
|
|
||||||
|
|
||||||
// Label the entity - this is done first so that we can get the bounding box
|
// Label the entity - this is done first so that we can get the bounding box
|
||||||
// which then determines the size of the rectangle
|
// which then determines the size of the rectangle
|
||||||
const textId = 'entity-' + v + '-' + svgId;
|
const textId = 'entity-' + id + '-' + svgId;
|
||||||
const textNode = diagram
|
const textNode = groupNode
|
||||||
.append('text')
|
.append('text')
|
||||||
.attr('id', textId)
|
.attr('id', textId)
|
||||||
.attr('x', centre.x)
|
.attr('x', 0)
|
||||||
.attr('y', centre.y)
|
.attr('y', (conf.fontSize + 2 * conf.entityPadding) / 2)
|
||||||
.attr('dominant-baseline', 'middle')
|
.attr('dominant-baseline', 'middle')
|
||||||
.attr('text-anchor', 'middle')
|
.attr('text-anchor', 'middle')
|
||||||
.attr('style', 'font-family: ' + getConfig().fontFamily)
|
.attr('style', 'font-family: ' + getConfig().fontFamily + '; font-size: ' + conf.fontSize)
|
||||||
.text(v);
|
.text(id);
|
||||||
|
|
||||||
|
// Calculate the width and height of the entity
|
||||||
const textBBox = textNode.node().getBBox();
|
const textBBox = textNode.node().getBBox();
|
||||||
const entityWidth = Math.max(conf.minEntityWidth, textBBox.width + conf.entityPadding * 2);
|
const entityWidth = Math.max(conf.minEntityWidth, textBBox.width + conf.entityPadding * 2);
|
||||||
const entityHeight = Math.max(conf.minEntityHeight, textBBox.height + conf.entityPadding * 2);
|
const entityHeight = Math.max(conf.minEntityHeight, textBBox.height + conf.entityPadding * 2);
|
||||||
|
|
||||||
// Add info to the node so that we can retrieve it later when drawing relationships
|
// Make sure the text gets centred relative to the entity box
|
||||||
g.node(v).width = entityWidth;
|
textNode.attr('transform', 'translate(' + entityWidth / 2 + ',' + entityHeight / 2 + ')');
|
||||||
g.node(v).height = entityHeight;
|
|
||||||
|
|
||||||
// Draw the rectangle - insert it before the text so that the text is not obscured
|
// Draw the rectangle - insert it before the text so that the text is not obscured
|
||||||
const rectX = centre.x - entityWidth / 2;
|
const rectNode = groupNode
|
||||||
const rectY = centre.y - entityHeight / 2;
|
|
||||||
diagram
|
|
||||||
.insert('rect', '#' + textId)
|
.insert('rect', '#' + textId)
|
||||||
.attr('fill', conf.fill)
|
.attr('fill', conf.fill)
|
||||||
.attr('fill-opacity', conf.fillOpacity)
|
.attr('fill-opacity', conf.fillOpacity)
|
||||||
.attr('stroke', conf.stroke)
|
.attr('stroke', conf.stroke)
|
||||||
.attr('x', rectX)
|
.attr('x', 0)
|
||||||
.attr('y', rectY)
|
.attr('y', 0)
|
||||||
.attr('width', entityWidth)
|
.attr('width', entityWidth)
|
||||||
.attr('height', entityHeight);
|
.attr('height', entityHeight);
|
||||||
|
|
||||||
|
const rectBBox = rectNode.node().getBBox();
|
||||||
|
|
||||||
|
// Add the entity to the graph
|
||||||
|
// TODO: revisit this - need to understand properly
|
||||||
|
graph.setNode(id, {
|
||||||
|
labelType: 'svg',
|
||||||
|
width: rectBBox.width,
|
||||||
|
height: rectBBox.height,
|
||||||
|
shape: 'rect',
|
||||||
|
label: document.createElementNS('http://www.w3.org/2000/svg', 'text'),
|
||||||
|
id: id
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
return firstOne;
|
||||||
}; // drawEntities
|
}; // drawEntities
|
||||||
|
|
||||||
|
const adjustEntities = function(svgNode, entities, graph) {
|
||||||
|
graph.nodes().forEach(function(v) {
|
||||||
|
if (typeof v !== 'undefined' && typeof graph.node(v) !== 'undefined') {
|
||||||
|
d3.select('#' + v).attr(
|
||||||
|
'transform',
|
||||||
|
'translate(' +
|
||||||
|
(graph.node(v).x - graph.node(v).width / 2) +
|
||||||
|
',' +
|
||||||
|
(graph.node(v).y - graph.node(v).height / 2) +
|
||||||
|
' )'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getEdgeName = function(rel) {
|
||||||
|
return (rel.entityA + rel.roleA + rel.roleB + rel.entityB).replace(/\s/g, '');
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add each relationship to the graph
|
* Add each relationship to the graph
|
||||||
* @param relationships the relationships to be added
|
* @param relationships the relationships to be added
|
||||||
@@ -102,7 +133,7 @@ const drawEntities = function(diagram, entities, g, svgId) {
|
|||||||
*/
|
*/
|
||||||
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 }, getEdgeName(r));
|
||||||
});
|
});
|
||||||
return relationships;
|
return relationships;
|
||||||
}; // addRelationships
|
}; // addRelationships
|
||||||
@@ -110,9 +141,9 @@ const addRelationships = function(relationships, g) {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
const drawRelationships = function(diagram, relationships, g) {
|
const drawRelationships = function(diagram, relationships, g, insertId) {
|
||||||
relationships.forEach(function(rel) {
|
relationships.forEach(function(rel) {
|
||||||
drawRelationshipFromLayout(diagram, rel, g);
|
drawRelationshipFromLayout(diagram, rel, g, insertId);
|
||||||
});
|
});
|
||||||
}; // drawRelationships
|
}; // drawRelationships
|
||||||
|
|
||||||
@@ -122,9 +153,10 @@ const drawRelationships = function(diagram, relationships, g) {
|
|||||||
* @param rel the relationship to draw in the svg
|
* @param rel the relationship to draw in the svg
|
||||||
* @param g the graph containing the edge information
|
* @param g the graph containing the edge information
|
||||||
*/
|
*/
|
||||||
const drawRelationshipFromLayout = function(diagram, rel, g) {
|
const drawRelationshipFromLayout = function(diagram, rel, g, insert) {
|
||||||
// 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 });
|
||||||
|
const edge = g.edge(rel.entityA, rel.entityB, getEdgeName(rel));
|
||||||
|
|
||||||
// Get a function that will generate the line path
|
// Get a function that will generate the line path
|
||||||
const lineFunction = d3
|
const lineFunction = d3
|
||||||
@@ -137,9 +169,9 @@ const drawRelationshipFromLayout = function(diagram, rel, g) {
|
|||||||
})
|
})
|
||||||
.curve(d3.curveBasis);
|
.curve(d3.curveBasis);
|
||||||
|
|
||||||
// Append the line to the diagram node
|
// Insert the line at the right place
|
||||||
const svgPath = diagram
|
const svgPath = diagram
|
||||||
.append('path')
|
.insert('path', '#' + insert)
|
||||||
.attr('d', lineFunction(edge.points))
|
.attr('d', lineFunction(edge.points))
|
||||||
.attr('stroke', conf.stroke)
|
.attr('stroke', conf.stroke)
|
||||||
.attr('fill', 'none');
|
.attr('fill', 'none');
|
||||||
@@ -281,12 +313,26 @@ export const draw = function(text, id) {
|
|||||||
logger.debug('Parsing failed');
|
logger.debug('Parsing failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a reference to the diagram node
|
// Get a reference to the svg node that contains the text
|
||||||
const svg = 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(svg, conf);
|
erMarkers.insertMarkers(svg, conf);
|
||||||
|
|
||||||
|
// Now we have to construct the diagram in a specific way:
|
||||||
|
// ---
|
||||||
|
// 1. Create all the entities in the svg node at 0,0, but with the correct dimensions (allowing for text content)
|
||||||
|
// 2. Make sure they are all added to the graph
|
||||||
|
// 3. Add all the edges (relationships) to the graph aswell
|
||||||
|
// 4. Let dagre do its magic to layout the graph. This assigns:
|
||||||
|
// - the centre co-ordinates for each node, bearing in mind the dimensions and edge relationships
|
||||||
|
// - the path co-ordinates for each edge
|
||||||
|
// But it has no impact on the svg child nodes - the diagram remains with every entity rooted at 0,0
|
||||||
|
// 5. Now assign a transform to each entity in the svg node so that it gets drawn in the correct place, as determined by
|
||||||
|
// its centre point, which is obtained from the graph, and it's width and height
|
||||||
|
// 6. And finally, create all the edges in the svg node using information from the graph
|
||||||
|
// ---
|
||||||
|
|
||||||
// Create the graph
|
// Create the graph
|
||||||
let g;
|
let g;
|
||||||
|
|
||||||
@@ -311,16 +357,20 @@ export const draw = function(text, id) {
|
|||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add the entities and relationships to the graph
|
const entities = erDb.getEntities();
|
||||||
const entities = addEntities(erDb.getEntities(), g);
|
const firstEntity = drawEntities(svg, entities, id, g);
|
||||||
|
|
||||||
|
//addEntities(erDb.getEntities(), g);
|
||||||
const relationships = addRelationships(erDb.getRelationships(), g);
|
const relationships = addRelationships(erDb.getRelationships(), g);
|
||||||
|
|
||||||
dagre.layout(g); // Node and edge positions will be updated
|
dagre.layout(g); // Node and edge positions will be updated
|
||||||
|
|
||||||
|
adjustEntities(svg, entities, g);
|
||||||
|
|
||||||
// Draw the relationships first because their markers need to be
|
// Draw the relationships first because their markers need to be
|
||||||
// clipped by the entity boxes
|
// clipped by the entity boxes
|
||||||
drawRelationships(svg, relationships, g);
|
drawRelationships(svg, relationships, g, firstEntity);
|
||||||
drawEntities(svg, entities, g, id);
|
//drawEntities(svg, entities, id);
|
||||||
|
|
||||||
const padding = 8; // TODO: move this to config
|
const padding = 8; // TODO: move this to config
|
||||||
|
|
||||||
|
@@ -381,7 +381,12 @@ const config = {
|
|||||||
* retain their elegant joins to the boxes regardless of the angle of incidence
|
* retain their elegant joins to the boxes regardless of the angle of incidence
|
||||||
* then override this to something less than 100%
|
* then override this to something less than 100%
|
||||||
*/
|
*/
|
||||||
fillOpacity: '100%'
|
fillOpacity: '100%',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Font size
|
||||||
|
*/
|
||||||
|
fontSize: '12px'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user