Improved layout for relation tree
Change-Id: I6909cf3cf5a39a9153875e40a84cdef2aa87d28b
diff --git a/dev/demo/relations.html b/dev/demo/relations.html
index 6435fb9..d651988 100644
--- a/dev/demo/relations.html
+++ b/dev/demo/relations.html
@@ -5,7 +5,6 @@
<script data-main="relationsdemo.js" src="../js/lib/require.js" async="async"></script>
<link type="text/css" rel="stylesheet" href="../css/kalamar.css" />
<style>
-
tspan, text {
font-size: 11pt;
stroke-width: 0;
@@ -13,17 +12,32 @@
stroke-opacity:0;
padding-right: 3pt
fill: black;
- }
+ }
+ /*
+ svg.relTree g > text > tspan {
+ text-anchor: middle;
+ }
+ */
g.arcs text {
font-size: 9pt;
fill: blue;
- }
+ }
path {
stroke-width: 2;
stroke: black;
fill: none;
}
-
+ path.anchor {
+ stroke: green;
+ z-index: 20;
+ }
+ marker > path {
+ fill-opacity:1;
+ fill: black;
+ }
+ marker {
+ overflow:visible
+ }
</style>
</head>
diff --git a/dev/demo/relationsdemo.js b/dev/demo/relationsdemo.js
index 5c28f9a..17fcfb3 100644
--- a/dev/demo/relationsdemo.js
+++ b/dev/demo/relationsdemo.js
@@ -5,5 +5,32 @@
require(['match/relations'], function (relClass) {
var rel = relClass.create();
document.getElementById("tree").appendChild(rel.element());
+
+ /*
+ * Start and end may be spans, i.e. arrays
+ */
+
+ rel
+ .addToken("Der")
+ .addToken("alte")
+ .addToken("Mann")
+ .addToken("ging")
+ .addToken("über")
+ .addToken("die")
+ .addToken("breite")
+ .addToken("nasse")
+ .addToken("Straße")
+ ;
+
+ rel
+ .addRel({ start: 0, end: 1, label: "a"})
+ .addRel({ start: 0, end: 1, label: "b" })
+ .addRel({ start: 1, end: 2, label: "c", direction: "bi" })
+ .addRel({ start: 0, end: 2, label: "d" })
+ .addRel({ start: [2,4], end: 5, label: "e", direction: "uni" })
+ .addRel({ start: [5,6], end: 7, label: "g" })
+ .addRel({ start: 4, end: [6,8], label: "f", direction: "bi" })
+ ;
+
rel.show();
});
diff --git a/dev/js/src/match.js b/dev/js/src/match.js
index b5f0d5a..5943d88 100644
--- a/dev/js/src/match.js
+++ b/dev/js/src/match.js
@@ -110,7 +110,7 @@
continue;
};
};
-
+
return this;
},
diff --git a/dev/js/src/match/relations.js b/dev/js/src/match/relations.js
index 28a279b..bf80fb3 100644
--- a/dev/js/src/match/relations.js
+++ b/dev/js/src/match/relations.js
@@ -7,15 +7,16 @@
create : function (snippet) {
var obj = Object.create(this)._init(snippet);
obj._tokens = [];
- obj._tokenElements = [];
obj._arcs = []
+ obj._tokenElements = [];
+ obj._y = 0;
// Some configurations
obj.maxArc = 200; // maximum height of the bezier control point
obj.overlapDiff = 20;
obj.arcDiff = 15;
obj.anchorDiff = 6;
- obj.anchorStart = 10;
+ obj.anchorStart = 15;
obj.tokenSep = 30;
return obj;
},
@@ -40,12 +41,16 @@
},
_drawAnchor : function (anchor) {
- var startPos = this._tokenElements[anchor.first].getBoundingClientRect().left;
- var endPos = this._tokenElements[anchor.last].getBoundingClientRect().right;
+ var firstBox = this._tokenElements[anchor.first].getBoundingClientRect();
+ var lastBox = this._tokenElements[anchor.last].getBoundingClientRect();
- var y = (anchor.overlaps * -1 * this.anchorDiff) - this.anchorStart; // - this.arcDiff;
+ var startPos = firstBox.left;
+ var endPos = lastBox.right;
+
+ var y = this._y + (anchor.overlaps * this.anchorDiff) - this.anchorStart;
+
var l = this._c('path');
- l.setAttribute("d", "M " + startPos + " " + y + " L " + endPos + " " + y);
+ l.setAttribute("d", "M " + startPos + "," + y + " L " + endPos + "," + y);
l.setAttribute("class", "anchor");
anchor.element = l;
anchor.y = y;
@@ -57,10 +62,10 @@
_drawArc : function (arc) {
var startPos, endPos;
- var startY = 0, endY = 0;
+ var startY = this._y, endY = this._y;
if (arc.startAnchor !== undefined) {
- startPos = this._tokenPoint(arc.startAnchor.element)
+ startPos = this._tokenPoint(arc.startAnchor.element);
startY = arc.startAnchor.y;
}
else {
@@ -89,15 +94,23 @@
var x = Math.min(startPos, endPos);
- var controlY = (startY + endY - cHeight);
+ //var controlY = (startY + endY - cHeight);
+ var controlY = (endY - cHeight);
- var arcE = "M "+ startPos + " " + startY +
- " C " + startPos + " " + controlY +
- " " + endPos + " " + controlY +
- " " + endPos + " " + endY;
+ var arcE = "M "+ startPos + "," + startY +
+ " C " + startPos + "," + controlY +
+ " " + endPos + "," + controlY +
+ " " + endPos + "," + endY;
p.setAttribute("d", arcE);
+ if (arc.direction !== undefined) {
+ p.setAttribute("marker-end", "url(#arr)");
+ if (arc.direction === 'bi') {
+ p.setAttribute("marker-start", "url(#arr)");
+ };
+ };
+
/*
* Calculate the top point of the arc for labeling using
* de Casteljau's algorithm, see e.g.
@@ -119,15 +132,6 @@
labelE.setAttribute("text-anchor", "middle");
labelE.appendChild(document.createTextNode(arc.label));
};
-
- /*
- var circle = this._c("circle");
- circle.setAttribute("cx", x + middle);
- circle.setAttribute("cy", middleY);
- circle.setAttribute("r", 4);
- circle.setAttribute("fill", "red");
- g.appendChild(circle);
- */
return g;
},
@@ -137,8 +141,24 @@
// Create svg
var svg = this._c("svg");
- svg.setAttribute("width", 700);
- svg.setAttribute("height", 300);
+
+ window.addEventListener("resize", function () {
+ // TODO: Only if text-size changed!
+ this.show();
+ }.bind(this));
+
+ var defs = svg.appendChild(this._c("defs"));
+ var marker = defs.appendChild(this._c("marker"));
+ marker.setAttribute("refX", 9);
+ marker.setAttribute("id", "arr");
+ marker.setAttribute("orient", "auto-start-reverse");
+ marker.setAttribute("markerUnits","userSpaceOnUse");
+
+ var arrow = this._c("path");
+ arrow.setAttribute("transform", "scale(0.8)");
+ arrow.setAttribute("d", "M 0,-5 0,5 10,0 Z");
+ marker.appendChild(arrow);
+
this._element = svg;
return this._element;
},
@@ -186,8 +206,8 @@
var middle = Math.ceil(Math.abs(v.start[1] - v.start[0]) / 2) + v.start[0];
v.startAnchor = {
- "first": v.start[0],
- "last" : v.start[1],
+ "first": v.start[0],
+ "last" : v.start[1],
"length" : v.start[1] - v.start[0]
};
@@ -199,8 +219,8 @@
if (v.end instanceof Array) {
var middle = Math.abs(v.end[0] - v.end[1]) + v.end[0];
v.endAnchor = {
- "first": v.end[0],
- "last" : v.end[1],
+ "first": v.end[0],
+ "last" : v.end[1],
"length" : v.end[1] - v.end[0]
};
@@ -231,46 +251,68 @@
return 1;
});
- this._sortedArcs = lengthSort(sortedArcs, false);
-
+ this._sortedArcs = lengthSort(sortedArcs, false);
this._sortedAnchors = lengthSort(anchors, true);
},
show : function () {
var svg = this._element;
+ var height = this.maxArc;
+
+ /*
+ svg.setAttribute("width", 700);
+ svg.setAttribute("height", 300);
+ */
+
+ // Delete old group
+ if (svg.getElementsByTagName("g")[0] !== undefined) {
+ var group = svg.getElementsByTagName("g")[0];
+ svg.removeChild(group);
+ this._tokenElements = [];
+ };
+
+ var g = svg.appendChild(this._c("g"));
/*
* Generate token list
*/
- var text = svg.appendChild(this._c("text"));
- text.setAttribute("y", 135);
- text.setAttribute("x", 160);
+ var text = g.appendChild(this._c("text"));
+ text.setAttribute("text-anchor", "start");
+ text.setAttribute("y", height);
+ this._y = height - (this.anchorStart);
+
+ var ws = text.appendChild(this._c("tspan"));
+ ws.appendChild(document.createTextNode('\u00A0'));
+ ws.style.textAnchor = "start";
+
var lastRight = 0;
for (var node_i in this._tokens) {
// Append svg
var tspan = text.appendChild(this._c("tspan"));
tspan.appendChild(document.createTextNode(this._tokens[node_i]));
-
+ tspan.setAttribute("text-anchor", "middle");
+
this._tokenElements.push(tspan);
// Add whitespace!
- // var whitespace = text.appendChild(document.createTextNode(" "));
- var ws = text.appendChild(this._c("tspan"));
- ws.appendChild(document.createTextNode(" "));
- ws.setAttribute("class", "rel-ws");
- ws.setAttribute("dx", this.tokenSep);
+ //var ws = text.appendChild(this._c("tspan"));
+ //ws.appendChild(document.createTextNode(" "));
+ // ws.setAttribute("class", "rel-ws");
+ tspan.setAttribute("dx", this.tokenSep);
};
- this.arcs = svg.appendChild(this._c("g"));
- this.arcs.classList.add("arcs");
+ var arcs = g.appendChild(this._c("g"));
+ arcs.classList.add("arcs");
+ /*
var textBox = text.getBoundingClientRect();
- this.arcs.setAttribute(
+ arcs.setAttribute(
"transform",
"translate(0," + textBox.y +")"
);
+ */
/*
* TODO:
@@ -281,16 +323,29 @@
* On the other hand, anchors need to be sorted as well
* in the same way.
*/
- this._sortArcs();
+ if (this._sortedArcs === undefined) {
+ this._sortArcs();
+ };
var i;
for (i in this._sortedAnchors) {
- this.arcs.appendChild(this._drawAnchor(this._sortedAnchors[i]));
+ arcs.appendChild(this._drawAnchor(this._sortedAnchors[i]));
};
for (i in this._sortedArcs) {
- this.arcs.appendChild(this._drawArc(this._sortedArcs[i]));
+ arcs.appendChild(this._drawArc(this._sortedArcs[i]));
};
+
+ var width = text.getBoundingClientRect().width;
+ svg.setAttribute("width", width);
+ svg.setAttribute("height", height);
+ svg.setAttribute("class", "relTree");
+
+ // svg.setAttribute("viewbox", "0 0 500 200");
+ /*
+ console.log(size.width);
+ console.log(size.height);
+ */
}
};