mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-11-14 09:44:51 +01:00
Layout algorithm in place
This commit is contained in:
@@ -83,7 +83,6 @@ const detectType = function (text, cnf) {
|
||||
if (cnf && cnf.flowchart && cnf.flowchart.defaultRenderer === 'dagre-wrapper')
|
||||
return 'flowchart-v2';
|
||||
const k = Object.keys(detectors);
|
||||
console.log('here', k);
|
||||
for (let i = 0; i < k.length; i++) {
|
||||
const key = k[i];
|
||||
console.log('Detecting type for', key);
|
||||
|
||||
@@ -85,7 +85,7 @@ describe('when parsing a mindmap ', function () {
|
||||
|
||||
mindmap.parse(str);
|
||||
const mm = mindmap.yy.getMindmap();
|
||||
expect(mm.id).toEqual('root');
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.descr).toEqual('The root');
|
||||
expect(mm.type).toEqual(mindmap.yy.nodeType.RECT);
|
||||
});
|
||||
@@ -100,7 +100,7 @@ describe('when parsing a mindmap ', function () {
|
||||
expect(mm.children.length).toEqual(1);
|
||||
const child = mm.children[0];
|
||||
expect(child.descr).toEqual('child1');
|
||||
expect(child.id).toEqual('theId');
|
||||
expect(child.nodeId).toEqual('theId');
|
||||
expect(child.type).toEqual(mindmap.yy.nodeType.ROUNDED_RECT);
|
||||
});
|
||||
it('should handle an id and type for a node definition', function () {
|
||||
@@ -114,7 +114,7 @@ root
|
||||
expect(mm.children.length).toEqual(1);
|
||||
const child = mm.children[0];
|
||||
expect(child.descr).toEqual('child1');
|
||||
expect(child.id).toEqual('theId');
|
||||
expect(child.nodeId).toEqual('theId');
|
||||
expect(child.type).toEqual(mindmap.yy.nodeType.ROUNDED_RECT);
|
||||
});
|
||||
it('mutiple types (circle)', function () {
|
||||
@@ -139,7 +139,7 @@ root((the root))
|
||||
|
||||
mindmap.parse(str);
|
||||
const mm = mindmap.yy.getMindmap();
|
||||
expect(mm.id).toEqual('root');
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.descr).toEqual('The root');
|
||||
expect(mm.type).toEqual(mindmap.yy.nodeType.RECT);
|
||||
expect(mm.icon).toEqual('bomb');
|
||||
@@ -153,7 +153,7 @@ root((the root))
|
||||
|
||||
mindmap.parse(str);
|
||||
const mm = mindmap.yy.getMindmap();
|
||||
expect(mm.id).toEqual('root');
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.descr).toEqual('The root');
|
||||
expect(mm.type).toEqual(mindmap.yy.nodeType.RECT);
|
||||
expect(mm.class).toEqual('m-4 p-8');
|
||||
@@ -168,7 +168,7 @@ root((the root))
|
||||
|
||||
mindmap.parse(str);
|
||||
const mm = mindmap.yy.getMindmap();
|
||||
expect(mm.id).toEqual('root');
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.descr).toEqual('The root');
|
||||
expect(mm.type).toEqual(mindmap.yy.nodeType.RECT);
|
||||
expect(mm.class).toEqual('m-4 p-8');
|
||||
@@ -182,7 +182,7 @@ root((the root))
|
||||
`;
|
||||
mindmap.parse(str);
|
||||
const mm = mindmap.yy.getMindmap();
|
||||
expect(mm.id).toEqual('root');
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.descr).toEqual('String containing []');
|
||||
});
|
||||
it('should be possible to use node syntax in the descriptions in children', function () {
|
||||
@@ -192,7 +192,7 @@ root((the root))
|
||||
`;
|
||||
mindmap.parse(str);
|
||||
const mm = mindmap.yy.getMindmap();
|
||||
expect(mm.id).toEqual('root');
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.descr).toEqual('String containing []');
|
||||
expect(mm.children.length).toEqual(1);
|
||||
expect(mm.children[0].descr).toEqual('String containing ()');
|
||||
|
||||
@@ -5,9 +5,12 @@ var message = '';
|
||||
var info = false;
|
||||
const root = {};
|
||||
let nodes = [];
|
||||
|
||||
let cnt = 0;
|
||||
let elements = {};
|
||||
export const clear = () => {
|
||||
nodes = [];
|
||||
cnt = 0;
|
||||
elements = {};
|
||||
};
|
||||
|
||||
const getParent = function (level) {
|
||||
@@ -26,7 +29,8 @@ export const getMindmap = () => {
|
||||
};
|
||||
export const addNode = (level, id, descr, type) => {
|
||||
const node = {
|
||||
id: sanitizeText(id),
|
||||
id: cnt++,
|
||||
nodeId: sanitizeText(id),
|
||||
level,
|
||||
descr: sanitizeText(descr),
|
||||
type,
|
||||
@@ -79,6 +83,11 @@ export const getTypeFromStart = (str) => {
|
||||
return nodeType.DEFAULT;
|
||||
}
|
||||
};
|
||||
|
||||
export const setElementForId = (id, element) => {
|
||||
elements[id] = element;
|
||||
};
|
||||
|
||||
export const decorateNode = (decoration) => {
|
||||
console.log('decorateNode', decoration);
|
||||
const node = nodes[nodes.length - 1];
|
||||
@@ -96,5 +105,9 @@ export default {
|
||||
nodeType,
|
||||
getTypeFromStart,
|
||||
decorateNode,
|
||||
setElementForId,
|
||||
getElementById: (id) => elements[id],
|
||||
// getNodeById: (id) => nodes.find((node) => node.id === id),
|
||||
getNodeById: (id) => nodes[id],
|
||||
// parseError
|
||||
};
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
import { select } from 'd3';
|
||||
import { log, getConfig, setupGraphViewbox } from '../../diagram-api/diagramAPI';
|
||||
import svgDraw from './svgDraw';
|
||||
|
||||
import { BoundingBox, Layout, Tree } from 'non-layered-tidy-tree-layout';
|
||||
import clone from 'fast-clone';
|
||||
import db from './mindmapDb';
|
||||
/**
|
||||
* @param {any} svg The svg element to draw the diagram onto
|
||||
* @param {object} mindmap The maindmap data and hierarchy
|
||||
@@ -17,17 +19,181 @@ function drawNodes(svg, mindmap, conf) {
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {any} svg The svg element to draw the diagram onto */
|
||||
function drawEdges() {}
|
||||
|
||||
/**
|
||||
* @param mindmap
|
||||
* @param callback
|
||||
*/
|
||||
function eachNode(mindmap, callback) {
|
||||
callback(mindmap);
|
||||
if (mindmap.children) {
|
||||
mindmap.children.forEach((child) => {
|
||||
eachNode(child, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
/** @param {object} mindmap */
|
||||
function transpose(mindmap) {
|
||||
console.log('transpose', mindmap);
|
||||
eachNode(mindmap, (node) => {
|
||||
// node.y = node.y - (node.y - bb.top) * 2 - node.height;
|
||||
const orgWidth = node.width;
|
||||
const orgX = node.x;
|
||||
node.width = node.height;
|
||||
node.height = orgWidth;
|
||||
node.x = node.y;
|
||||
node.y = orgX;
|
||||
});
|
||||
return mindmap;
|
||||
}
|
||||
/** @param {object} mindmap */
|
||||
function bottomToUp(mindmap) {
|
||||
console.log('bottomToUp', mindmap);
|
||||
eachNode(mindmap.result, (node) => {
|
||||
// node.y = node.y - (node.y - bb.top) * 2 - node.height;
|
||||
node.y = node.y - (node.y - 0) * 2 - node.height;
|
||||
});
|
||||
return mindmap;
|
||||
}
|
||||
/** @param {object} mindmap The mindmap hierarchy */
|
||||
function rightToLeft(mindmap) {
|
||||
console.log('bottomToUp', mindmap);
|
||||
eachNode(mindmap.result, (node) => {
|
||||
// node.y = node.y - (node.y - bb.top) * 2 - node.height;
|
||||
node.x = node.x - (node.x - 0) * 2 - node.width;
|
||||
});
|
||||
return mindmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mindmap
|
||||
* @param dir
|
||||
* @param conf
|
||||
*/
|
||||
function layout(mindmap, dir, conf) {
|
||||
const bb = new BoundingBox(40, 40);
|
||||
|
||||
const layout = new Layout(bb);
|
||||
switch (dir) {
|
||||
case 'TB':
|
||||
return layout.layout(mindmap);
|
||||
case 'BT':
|
||||
return bottomToUp(layout.layout(mindmap));
|
||||
case 'RL': {
|
||||
transpose(mindmap);
|
||||
let newRes = layout.layout(mindmap);
|
||||
transpose(newRes.result);
|
||||
return rightToLeft(newRes);
|
||||
}
|
||||
case 'LR': {
|
||||
transpose(mindmap);
|
||||
let newRes = layout.layout(mindmap);
|
||||
transpose(newRes.result);
|
||||
return newRes;
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
const dirFromIndex = (index) => {
|
||||
const dirNum = index % 4;
|
||||
switch (dirNum) {
|
||||
case 0:
|
||||
return 'LR';
|
||||
case 1:
|
||||
return 'RL';
|
||||
case 2:
|
||||
return 'TB';
|
||||
case 3:
|
||||
return 'BT';
|
||||
default:
|
||||
return 'TB';
|
||||
}
|
||||
};
|
||||
|
||||
const mergeTrees = (node, trees) => {
|
||||
node.x = trees[0].result.x;
|
||||
node.y = trees[0].result.y;
|
||||
trees.forEach((tree) => {
|
||||
tree.result.children.forEach((child) => {
|
||||
const dx = node.x - tree.result.x;
|
||||
const dy = node.y - tree.result.y;
|
||||
eachNode(child, (childNode) => {
|
||||
const orgNode = db.getNodeById(childNode.id);
|
||||
if (orgNode) {
|
||||
orgNode.x = childNode.x + dx;
|
||||
orgNode.y = childNode.y + dy;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
return node;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param node
|
||||
* @param isRoot
|
||||
* @param parent
|
||||
* @param conf
|
||||
*/
|
||||
function layoutMindmap(node, isRoot) {}
|
||||
function layoutMindmap(node, conf) {
|
||||
// BoundingBox(gap, bottomPadding)
|
||||
// const bb = new BoundingBox(10, 10);
|
||||
// const layout = new Layout(bb);
|
||||
// // const layout = new HorizontalLayout(bb);
|
||||
|
||||
const trees = [];
|
||||
// node.children.forEach((child, index) => {
|
||||
// const tree = clone(node);
|
||||
// tree.children = [tree.children[index]];
|
||||
// trees.push(layout(tree, dirFromIndex(index), conf));
|
||||
// });
|
||||
|
||||
let cnt = 0;
|
||||
// For each direction, create a new tree with the same root, and add a ubset of the children to it.
|
||||
for (let i = 0; i < 4; i++) {
|
||||
// Calculate the number of the children of the root node that will be used in this direction
|
||||
const numChildren =
|
||||
Math.floor(node.children.length / 4) + (node.children.length % 4 > i ? 1 : 0);
|
||||
// Copy the original root node
|
||||
const tree = clone(node);
|
||||
// Setup the new copy with the children to be rendered in this direction
|
||||
tree.children = [];
|
||||
for (let j = 0; j < numChildren; j++) {
|
||||
tree.children.push(node.children[cnt]);
|
||||
cnt++;
|
||||
}
|
||||
if (tree.children.length > 0) {
|
||||
trees.push(layout(tree, dirFromIndex(i), conf));
|
||||
}
|
||||
}
|
||||
|
||||
// Merge the trees into a single tree
|
||||
const result = mergeTrees(node, trees);
|
||||
|
||||
// return layout(node, 'BT', conf);
|
||||
// const res = layout(node, 'BT', conf);
|
||||
// res.result.children = [];
|
||||
// trees.forEach((tree) => {
|
||||
// res.result.children.push(tree.result);
|
||||
// });
|
||||
console.log('Trees', trees);
|
||||
return node;
|
||||
}
|
||||
/**
|
||||
* @param node
|
||||
* @param isRoot
|
||||
* @param conf
|
||||
*/
|
||||
function positionNodes(node, isRoot) {}
|
||||
function positionNodes(node, conf) {
|
||||
svgDraw.positionNode(node, conf);
|
||||
if (node.children) {
|
||||
node.children.forEach((child) => {
|
||||
positionNodes(child, conf);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a an info picture in the tag with id: id based on the graph definition in text.
|
||||
@@ -63,16 +229,6 @@ export const draw = (text, id, version, diagObj) => {
|
||||
const g = svg.append('g');
|
||||
const mm = diagObj.db.getMindmap();
|
||||
|
||||
// mm.x = 0;
|
||||
// mm.y = 0;
|
||||
// svgDraw.drawNode(g, mm, getConfig());
|
||||
// mm.children.forEach((child) => {
|
||||
// child.x = 200;
|
||||
// child.y = 200;
|
||||
// child.width = 200;
|
||||
// svgDraw.drawNode(g, child, getConfig());
|
||||
// });
|
||||
|
||||
// Draw the graph and start with drawing the nodes without proper position
|
||||
// this gives us the size of the nodes and we can set the positions later
|
||||
|
||||
@@ -82,12 +238,14 @@ export const draw = (text, id, version, diagObj) => {
|
||||
|
||||
// Next step is to layout the mindmap, giving each node a position
|
||||
|
||||
// layoutMindmap(mm, conf);
|
||||
console.log('Before', mm);
|
||||
const positionedMindmap = layoutMindmap(mm, conf);
|
||||
console.log(positionedMindmap);
|
||||
|
||||
// After this we can draw, first the edges and the then nodes with the correct position
|
||||
// drawEdges(svg, mm, conf);
|
||||
|
||||
// positionNodes(svg, mm, conf);
|
||||
positionNodes(positionedMindmap, conf);
|
||||
|
||||
// Setup the view box and size of the svg element
|
||||
setupGraphViewbox(undefined, svg, conf.mindmap.diagramPadding, conf.mindmap.useMaxWidth);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
const lineBreakRegex = /<br\s*\/?>/gi;
|
||||
import { select } from 'd3';
|
||||
import db from './mindmapDb';
|
||||
|
||||
/**
|
||||
* @param {string} text The text to be wrapped
|
||||
* @param {number} width The max width of the text
|
||||
@@ -88,13 +90,21 @@ export const drawNode = function (elem, node, conf) {
|
||||
.call(wrap, node.width);
|
||||
const bbox = txt.node().getBBox();
|
||||
node.height = bbox.height + conf.fontSize * 1.1 * 0.5;
|
||||
r.attr('height', node.height).attr('y', (-1 * node.height) / 2);
|
||||
r.attr('height', node.height); // .attr('y', (-1 * node.height) / 2);
|
||||
|
||||
txt.attr('transform', 'translate( 0,' + (-1 * node.height) / 2 + ')');
|
||||
// Position the node to its coordinate
|
||||
if (node.x || node.y) {
|
||||
nodeElem.attr('transform', 'translate(' + node.x + ',' + node.y + ')');
|
||||
}
|
||||
db.setElementForId(node.id, nodeElem);
|
||||
return node.height;
|
||||
};
|
||||
export default { drawNode };
|
||||
export const positionNode = function (node, conf) {
|
||||
const nodeElem = db.getElementById(node.id);
|
||||
|
||||
const x = node.x || 0;
|
||||
const y = node.y || 0;
|
||||
// Position the node to its coordinate
|
||||
nodeElem.attr('transform', 'translate(' + x + ',' + y + ')');
|
||||
};
|
||||
export default { drawNode, positionNode };
|
||||
|
||||
Reference in New Issue
Block a user