From: Benjamin Auder <benjamin.auder@somewhere>
Date: Wed, 31 Jan 2018 01:30:32 +0000 (+0100)
Subject: Better graph rendering by randomizing appearances and links left/right in dot string
X-Git-Url: https://git.auder.net/%7B%7B%20asset%28%27mixstore/images/assets/current/doc/%7B%7B%20pkg.url%20%7D%7D?a=commitdiff_plain;h=48a55161a22d169781fa3e0805ed2da7dac952cf;p=erdiag.git

Better graph rendering by randomizing appearances and links left/right in dot string
---

diff --git a/README.md b/README.md
index 6352f4d..acf706e 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,9 @@ This parser reads ER diagrams definition files, and produces two types of diagra
 [Graphviz](https://www.graphviz.org/) is used on server side to translate parsed graph descriptions into SVG objects.
 
 *Note:* at the moment only the conceptual graph is implemented, and no comments are allowed in textual descriptions.
-At least the former is planned, and also probably a way to indicate relative identifiers.
+At least the former is planned, and also probably a way to indicate relative identifiers, and maybe links between relationships.
+
+*Note bis:* temporary dependency to [underscore](http://underscorejs.org/); good library but used so far only for its shuffle() method.
 
 -----
 
diff --git a/example.html b/example.html
index 22d2d3a..dcb2fcd 100644
--- a/example.html
+++ b/example.html
@@ -1,5 +1,6 @@
 <div id="test"></div>
 
+<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
 <script src="parser.js"></script>
 <script>
 	let er =
diff --git a/parser.js b/parser.js
index 8a4c524..ba113e5 100644
--- a/parser.js
+++ b/parser.js
@@ -177,6 +177,7 @@ class ErDiags
 	}
 
 	// "Modèle conceptuel des données". TODO: option for graph size
+	// NOTE: randomizing helps to obtain better graphs (sometimes)
 	drawMcd(id, mcdStyle) //mcdStyle: bubble, or compact
 	{
 		let element = document.getElementById(id);
@@ -188,8 +189,11 @@ class ErDiags
 		}
 		// Build dot graph input
 		let mcdDot = 'graph {\n';
+		mcdDot += 'rankdir="LR";\n';
 		// Nodes:
-		Object.keys(this.entities).forEach( name => {
+		if (mcdStyle == "compact")
+			mcdDot += "node [shape=plaintext];\n";
+		_.shuffle(Object.keys(this.entities)).forEach( name => {
 			if (mcdStyle == "bubble")
 			{
 				mcdDot += name + '[shape=rectangle, label="' + name + '"';
@@ -198,16 +202,20 @@ class ErDiags
 				mcdDot += '];\n';
 				if (!!this.entities[name].attributes)
 				{
-					this.entities[name].attributes.forEach( a => {
+					_.shuffle(this.entities[name].attributes).forEach( a => {
 						let label = (a.isKey ? '#' : '') + a.name;
-						mcdDot += name + '_' + a.name + '[shape=ellipse, label="' + label + '"];\n';
-						mcdDot += name + '_' + a.name + ' -- ' + name + ';\n';
+						let attrName = name + '_' + a.name;
+						mcdDot += attrName + '[shape=ellipse, label="' + label + '"];\n';
+						if (Math.random() < 0.5)
+							mcdDot += attrName + ' -- ' + name + ';\n';
+						else
+							mcdDot += name + ' -- ' + attrName + ';\n';
 					});
 				}
 			}
 			else
 			{
-				mcdDot += name + '[shape=plaintext, label=<';
+				mcdDot += name + '[label=<';
 				if (this.entities[name].weak)
 				{
 					mcdDot += '<table port="name" BORDER="1" ALIGN="LEFT" CELLPADDING="0" CELLSPACING="3" CELLBORDER="0">' +
@@ -231,13 +239,17 @@ class ErDiags
 		});
 		// Inheritances:
 		this.inheritances.forEach( i => {
-			i.children.forEach( c => {
-				mcdDot += c + ':name -- ' + i.parent + ':name [len="1.00", dir="forward", arrowhead="vee", style="dashed"];\n';
+			_.shuffle(i.children).forEach( c => {
+				if (Math.random() < 0.5)
+					mcdDot += c + ':name -- ' + i.parent;
+				else
+					mcdDot += i.parent + ':name -- ' + c;
+				mcdDot += ':name [dir="forward", arrowhead="vee", style="dashed"];\n';
 			});
 		});
 		// Relationships:
 		let assoceCounter = 0;
-		this.associations.forEach( a => {
+		_.shuffle(this.associations).forEach( a => {
 			let name = !!a.name && a.name.length > 0
 				? a.name
 				: '_assoce' + assoceCounter++;
@@ -245,20 +257,28 @@ class ErDiags
 			if (a.weak)
 				mcdDot += ', peripheries=2';
 			mcdDot += '];\n';
-			a.entities.forEach( e => {
-				mcdDot += e.name + ':name -- ' + name + '[len="1.00", label="' + ErDiags.CARDINAL[e.card] + '"];\n';
+			_.shuffle(a.entities).forEach( e => {
+				if (Math.random() < 0.5)
+					mcdDot += e.name + ':name -- ' + name;
+				else
+					mcdDot += name + ':name -- ' + e.name;
+				mcdDot += '[label="' + ErDiags.CARDINAL[e.card] + '"];\n';
 			});
 			if (!!a.attributes)
 			{
-				a.attributes.forEach( attr => {
+				_.shuffle(a.attributes).forEach( attr => {
 					let label = (attr.isKey ? '#' : '') + attr.name;
-					mcdDot += name + '_' + attr.name + '[len="1.00", shape=ellipse, label="' + label + '"];\n';
-					mcdDot += name + '_' + attr.name + ' -- ' + name + ';\n';
+					mcdDot += name + '_' + attr.name + '[shape=ellipse, label="' + label + '"];\n';
+					let attrName = name + '_' + attr.name;
+					if (Math.random() < 0.5)
+						mcdDot += attrName + ' -- ' + name + ';\n';
+					else
+						mcdDot += name + ' -- ' + attrName + ';\n';
 				});
 			}
 		});
 		mcdDot += '}';
-		//console.log(mcdDot);
+		console.log(mcdDot);
 		ErDiags.AjaxGet(mcdDot, graphSvg => {
 			this.mcdGraph = graphSvg;
 			element.innerHTML = graphSvg;
@@ -266,6 +286,7 @@ class ErDiags
 	}
 
 	// "Modèle logique des données"
+	// TODO: this one should draw links from foreign keys to keys (port=... in <TD>)
 	drawMld(id)
 	{
 		let element = document.getElementById(id);