Browse Source

Add arrow types for EndPoints.js (#3839)

* Add arrowhead support

As described in TODO, dotparser.js does not support 'arrowhead'
attribubte of edge.
This update is for adding 'dot' and 'tee'(bar) styles.

* Add example for arrow styles

* Add box arrowhead

To support box arrowhead of graphviz, add Box endpoint class in
EndPoints.js and box attribute in dotparser.js.

* Add diamond arrowhead

To support diamond arrowhead of graphviz, add Diamond endpoint
class in EndPoints.js and diamond attribute in dotparser.js.

* Add crow arrowhead

To support crow arrowhead of graphviz, add Crow endpoint
class in EndPoints.js and crow attribute in dotparser.js.

* Add normal arrowhead

To support normal arrowhead of graphviz, add Triangle endpoint
class in EndPoints.js and normal attribute in dotparser.js.

* Add curve arrowhead

To support curve arrowhead of graphviz, add Curve endpoint
class in EndPoints.js and curve attribute in dotparser.js.

* Add inverted curve arrowhead

To support inverted curve arrowhead of graphviz, add InvertedCurve
endpoint class in EndPoints.js and icurve attribute in
dotparser.js.

* Add vee arrowhead

To support vee arrowhead of graphviz, add Vee endpoint class in
EndPoints.js and vee attribute in dotparser.js.

* Add arrowhead examples

* Fix bug for accessing null attribute

In createEdge(), accessing 'attr' causes an error if the edge has no
attribute and the value is null.
This update fixes bug for accessing null 'attr'.

* Update description for arrows.to.type option

Add followingn options for 'arrows.to.type'.

* box
* crow
* curve
* diamond
* inv_curve
* triangle
* inv_triangle
* vee

* Update edgeStyle example for arrow types

Add arrow types for the example.

'box', 'crow', 'curve', 'inv_curve', 'diamond', 'triangle',
'inv_triangle', 'vee'
develop
geminoa 6 years ago
committed by Yotam Berkowitz
parent
commit
2cf2904220
5 changed files with 385 additions and 45 deletions
  1. +1
    -1
      docs/network/edges.html
  2. +54
    -20
      examples/network/data/dotLanguage/dotEdgeStyles.html
  3. +43
    -22
      examples/network/edgeStyles/arrowTypes.html
  4. +39
    -2
      lib/network/dotparser.js
  5. +248
    -0
      lib/network/modules/components/edges/util/EndPoints.js

+ 1
- 1
docs/network/edges.html View File

@ -232,7 +232,7 @@ network.setOptions(options);
<td class="indent2">arrows.to.type</td> <td class="indent2">arrows.to.type</td>
<td>String</td> <td>String</td>
<td><code>arrow</code></td> <td><code>arrow</code></td>
<td>The type of endpoint. Possible values are: <code>arrow</code>, <code>bar</code>, <code>circle</code>. The default is <code>arrow</code>.
<td>The type of endpoint. Possible values are: <code>arrow</code>, <code>bar</code>, <code>circle</code>, <code>box</code>, <code>crow</code>, <code>curve</code>, <code>diamond</code>, <code>inv_curve</code>, <code>triangle</code>, <code>inv_triangle</code>, <code>vee</code>. The default is <code>arrow</code>.
</tr> </tr>
<tr parent="arrows" class="hidden"> <tr parent="arrows" class="hidden">
<td class="indent">arrows.middle</td> <td class="indent">arrows.middle</td>

+ 54
- 20
examples/network/data/dotLanguage/dotEdgeStyles.html View File

@ -36,8 +36,6 @@
#left, #right { #left, #right {
position: absolute; position: absolute;
width: 50%;
height: 100%;
margin: 0; margin: 0;
padding: 10px; padding: 10px;
box-sizing: border-box; box-sizing: border-box;
@ -45,11 +43,15 @@
} }
#left { #left {
width: 40%;
height: 80%;
top: 0; top: 0;
left: 0; left: 0;
} }
#right { #right {
width: 60%;
height: 100%;
top: 0; top: 0;
right: 0; right: 0;
} }
@ -82,7 +84,7 @@
} }
</style> </style>
</head> </head>
<body> <body>
@ -91,27 +93,43 @@
<div> <div>
<p> <p>
Example of edge styles support
Example of edge styles support.
</p> </p>
<ul>
<li> label: text displayed on the edge</li>
<li> color: edge color</li>
<li> style: edge style is solid(default), dashed or dotted</li>
</ul>
<table border=1>
<tr>
<th>Attributes</th><th>Desriptions</th>
</tr>
<tr>
<td align="center">label</td><td>Text displayed on the edge</td>
</tr>
<tr>
<td align="center">color</td><td>Edge color</td>
</tr>
<tr>
<td align="center">style</td>
<td>Edge style (solid, dashed, dotted)</td>
</tr>
<tr>
<td align="center">arrowhead</td>
<td>Arrow style (dot, box, crow, curve, icurve, normal, inv, diamond, tee, vee)</td>
</tr>
</table>
</div> </div>
<div>
<button id="draw" title="Draw the DOT graph (Ctrl+Enter)">Draw</button>
<button id="reset" title="Reset the DOT graph">Reset</button>
<span id="error"></span>
</div>
</div> </div>
<div id="contents"> <div id="contents">
<div id="left"> <div id="left">
<textarea id="data"> <textarea id="data">
</textarea> </textarea>
<div>
<button id="draw" title="Draw the DOT graph (Ctrl+Enter)">Draw</button>
<button id="reset" title="Reset the DOT graph">Reset</button>
</div>
<div>
<span id="error"></span>
</div>
</div> </div>
<div id="right"> <div id="right">
<div id="mynetwork"></div> <div id="mynetwork"></div>
@ -119,12 +137,28 @@
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
var dotDefault = "digraph {\n" +
" Parent -> C1[label=\"default\"]; // Default style is solid \n" +
" Parent -> C2[label=\"solid pink\", color=\"pink\"];\n" +
" Parent -> C3[label=\"dashed green\", style=\"dashed\", color=\"green\"];\n" +
" Parent -> C4[label=\"dotted purple\", style=\"dotted\", color=\"purple\"];\n" +
"}";
var dotDefault = 'digraph {\n' +
' // Parent nodes\n' +
' lines[label="LINES"]; \n' +
' ahs[label="ARROW HEADS"]; \n' +
'\n' +
' // Line styles\n' +
' lines -- solid[label="solid pink", color="pink"]; \n' +
' lines -- dashed[label="dashed green", style="dashed", color="green"]; \n' +
' lines -- dotted[label="dotted purple", style="dotted", color="purple"]; \n' +
'\n' +
' // Arrowhead styles\n' +
' ahs -> dot[label="dot", arrowhead=dot]; \n' +
' ahs -> box[label="box", arrowhead=box]; \n' +
' ahs -> crow[label="crow", arrowhead=crow]; \n' +
' ahs -> curve[label="curve", arrowhead=curve]; \n' +
' ahs -> icurve[label="icurve", arrowhead=icurve]; \n' +
' ahs -> normal[label="normal", arrowhead=normal]; \n' +
' ahs -> inv[label="inv", arrowhead=inv]; \n' +
' ahs -> diamond[label="diamond", arrowhead=diamond]; \n' +
' ahs -> tee[label="tee", arrowhead=tee]; \n' +
' ahs -> vee[label="vee", arrowhead=vee]; \n' +
'}';
// create a network // create a network
var container = document.getElementById('mynetwork'); var container = document.getElementById('mynetwork');

+ 43
- 22
examples/network/edgeStyles/arrowTypes.html View File

@ -9,7 +9,7 @@
<style type="text/css"> <style type="text/css">
#mynetwork { #mynetwork {
width: 600px; width: 600px;
height: 400px;
height: 540px;
border: 1px solid lightgray; border: 1px solid lightgray;
} }
</style> </style>
@ -17,37 +17,58 @@
<body> <body>
<p> <p>
The types of endpoints are: <code>'arrow' 'circle' 'bar'</code>.
The types of endpoints.
The default is <code>'arrow'</code>. The default is <code>'arrow'</code>.
</p> </p>
<p id="arrow_type_list"></p>
<div id="mynetwork"></div> <div id="mynetwork"></div>
<script type="text/javascript"> <script type="text/javascript">
var arrow_types = [
'arrow', 'bar', 'circle', 'box', 'crow', 'curve', 'inv_curve',
'diamond', 'triangle', 'inv_triangle', 'vee'
];
// update list of arrow types in html body
var nof_types = arrow_types.length;
var list_contents = [];
for(var i = 0; i < nof_types; i++) {
list_contents.push("<code>'" + arrow_types[i] + "'</code>");
}
var mylist = document.getElementById("arrow_type_list");
mylist.innerHTML = list_contents.join(", ");
// create an array with nodes // create an array with nodes
var nodes = new vis.DataSet([
{id: 1, label: 'A'},
{id: 2, label: 'B'},
{id: 3, label: 'C'},
{id: 4, label: 'D'}
]);
var node_attrs = new Array();
var nodes = arrow_types.slice();
nodes.push("end");
console.log(nodes);
var nof_nodes = nodes.length;
for(var i = 0; i < nof_nodes; i++) {
node_attrs[i] = {
id: i+1,
label: nodes[i]
};
}
var nodes = new vis.DataSet(node_attrs);
// create an array with edges // create an array with edges
var edges = new vis.DataSet([
{from: 1, to: 2, arrows:'to'},
{from: 2, to: 3, arrows:{
to: {
enabled: true,
type: 'circle'
}
}},
{from: 3, to: 4, arrows:{
to: {
enabled: true,
type: 'bar'
var edge_attrs = new Array();
var nof_edges = nof_nodes - 1;
for(var i = 0; i < nof_edges; i++) {
edge_attrs[i] = {
from: i+1, to: i+2, arrows: {
to: {
enabled: true,
type: arrow_types[i]
}
} }
}},
]);
}
}
var edges = new vis.DataSet(edge_attrs);
// create a network // create a network
var container = document.getElementById('mynetwork'); var container = document.getElementById('mynetwork');

+ 39
- 2
lib/network/dotparser.js View File

@ -266,6 +266,14 @@ function createEdge(graph, from, to, type, attr) {
} }
edge.attr = merge(edge.attr || {}, attr); // merge attributes edge.attr = merge(edge.attr || {}, attr); // merge attributes
// Move arrows attribute from attr to edge temporally created in
// parseAttributeList().
if (attr != null) {
if (attr.hasOwnProperty('arrows')) {
edge['arrows'] = {to: {enabled: true, type: attr.arrows.type}};
attr['arrows'] = null;
}
}
return edge; return edge;
} }
@ -715,6 +723,29 @@ function parseAttributeList() {
value = edgeStyles[value]; value = edgeStyles[value];
} }
// Define arrow types.
// vis.js currently supports types defined in 'arrowTypes'.
// Details of arrow shapes are described in
// http://www.graphviz.org/content/arrow-shapes
var arrowTypes = {
dot: 'circle',
box: 'box',
crow: 'crow',
curve: 'curve',
icurve: 'inv_curve',
normal: 'triangle',
inv: 'inv_triangle',
diamond: 'diamond',
tee: 'bar',
vee: 'vee'
};
if (name === 'arrowhead') {
var arrowType = arrowTypes[value];
name = 'arrows';
value = {to: {enabled:true, type: arrowType}};
}
setValue(attr, name, value); // name can be a path setValue(attr, name, value); // name can be a path
getToken(); getToken();
@ -883,7 +914,13 @@ function DOTToGraph (data) {
to: dotEdge.to to: dotEdge.to
}; };
merge(graphEdge, convertAttr(dotEdge.attr, EDGE_ATTR_MAPPING)); merge(graphEdge, convertAttr(dotEdge.attr, EDGE_ATTR_MAPPING));
graphEdge.arrows = (dotEdge.type === '->') ? 'to' : undefined;
// Add arrows attribute to default styled arrow.
// The reason why default style is not added in parseAttributeList() is
// because only default is cleared before here.
if (graphEdge.arrows == null && dotEdge.type === '->') {
graphEdge.arrows = 'to';
}
return graphEdge; return graphEdge;
}; };
@ -899,7 +936,7 @@ function DOTToGraph (data) {
} }
} }
// TODO: support for attributes 'dir' and 'arrowhead' (edge arrows)
// TODO: support for attributes 'dir' (edge arrows)
if (dotEdge.to instanceof Object) { if (dotEdge.to instanceof Object) {
to = dotEdge.to.nodes; to = dotEdge.to.nodes;

+ 248
- 0
lib/network/modules/components/edges/util/EndPoints.js View File

@ -113,6 +113,153 @@ class Arrow extends EndPoint {
} }
} }
/**
* Drawing methods for the crow endpoint.
* @extends EndPoint
*/
class Crow {
/**
* Draw this shape at the end of a line.
*
* @param {CanvasRenderingContext2D} ctx
* @param {ArrowData} arrowData
* @static
*/
static draw(ctx, arrowData) {
// Normalized points of closed path, in the order that they should be drawn.
// (0, 0) is the attachment point, and the point around which should be rotated
var points = [
{ x:-1, y: 0},
{ x:0, y: 0.3},
{ x:-0.4, y: 0},
{ x:0, y:-0.3},
];
EndPoint.transform(points, arrowData);
EndPoint.drawPath(ctx, points);
}
}
/**
* Drawing methods for the curve endpoint.
* @extends EndPoint
*/
class Curve {
/**
* Draw this shape at the end of a line.
*
* @param {CanvasRenderingContext2D} ctx
* @param {ArrowData} arrowData
* @static
*/
static draw(ctx, arrowData) {
// Normalized points of closed path, in the order that they should be drawn.
// (0, 0) is the attachment point, and the point around which should be rotated
var point = {x:-0.4, y:0};
EndPoint.transform(point, arrowData);
// Update endpoint style for drawing transparent arc.
ctx.strokeStyle = ctx.fillStyle;
ctx.fillStyle = 'rgba(0, 0, 0, 0)';
// Define curve endpoint as semicircle.
var pi = Math.PI
var start_angle = arrowData.angle - pi/2;
var end_angle = arrowData.angle + pi/2;
ctx.beginPath();
ctx.arc(point.x, point.y, arrowData.length*0.4, start_angle, end_angle, false);
ctx.stroke()
}
}
/**
* Drawing methods for the inverted curve endpoint.
* @extends EndPoint
*/
class InvertedCurve {
/**
* Draw this shape at the end of a line.
*
* @param {CanvasRenderingContext2D} ctx
* @param {ArrowData} arrowData
* @static
*/
static draw(ctx, arrowData) {
// Normalized points of closed path, in the order that they should be drawn.
// (0, 0) is the attachment point, and the point around which should be rotated
var point = {x:-0.3, y:0};
EndPoint.transform(point, arrowData);
// Update endpoint style for drawing transparent arc.
ctx.strokeStyle = ctx.fillStyle;
ctx.fillStyle = 'rgba(0, 0, 0, 0)';
// Define inverted curve endpoint as semicircle.
var pi = Math.PI
var start_angle = arrowData.angle + pi/2;
var end_angle = arrowData.angle + 3*pi/2;
ctx.beginPath();
ctx.arc(point.x, point.y, arrowData.length*0.4, start_angle, end_angle, false);
ctx.stroke()
}
}
/**
* Drawing methods for the trinagle endpoint.
* @extends EndPoint
*/
class Triangle {
/**
* Draw this shape at the end of a line.
*
* @param {CanvasRenderingContext2D} ctx
* @param {ArrowData} arrowData
* @static
*/
static draw(ctx, arrowData) {
// Normalized points of closed path, in the order that they should be drawn.
// (0, 0) is the attachment point, and the point around which should be rotated
var points = [
{ x:0.02, y:0},
{ x:-1, y: 0.3},
{ x:-1, y:-0.3},
];
EndPoint.transform(points, arrowData);
EndPoint.drawPath(ctx, points);
}
}
/**
* Drawing methods for the inverted trinagle endpoint.
* @extends EndPoint
*/
class InvertedTriangle {
/**
* Draw this shape at the end of a line.
*
* @param {CanvasRenderingContext2D} ctx
* @param {ArrowData} arrowData
* @static
*/
static draw(ctx, arrowData) {
// Normalized points of closed path, in the order that they should be drawn.
// (0, 0) is the attachment point, and the point around which should be rotated
var points = [
{ x:0, y:0.3},
{ x:0, y: -0.3},
{ x:-1, y:0},
];
EndPoint.transform(points, arrowData);
EndPoint.drawPath(ctx, points);
}
}
/** /**
* Drawing methods for the circle endpoint. * Drawing methods for the circle endpoint.
@ -173,6 +320,83 @@ class Bar {
} }
} }
/**
* Drawing methods for the box endpoint.
*/
class Box {
/**
* Draw this shape at the end of a line.
*
* @param {CanvasRenderingContext2D} ctx
* @param {ArrowData} arrowData
* @static
*/
static draw(ctx, arrowData) {
var points = [
{x:0, y:0.3},
{x:0, y:-0.3},
{x:-0.6, y:-0.3},
{x:-0.6, y:0.3},
];
EndPoint.transform(points, arrowData);
EndPoint.drawPath(ctx, points);
}
}
/**
* Drawing methods for the diamond endpoint.
*/
class Diamond {
/**
* Draw this shape at the end of a line.
*
* @param {CanvasRenderingContext2D} ctx
* @param {ArrowData} arrowData
* @static
*/
static draw(ctx, arrowData) {
var points = [
{x:0, y:0},
{x:-0.5, y:-0.3},
{x:-1, y:0},
{x:-0.5, y:0.3},
];
EndPoint.transform(points, arrowData);
EndPoint.drawPath(ctx, points);
}
}
/**
* Drawing methods for the vee endpoint.
* @extends EndPoint
*/
class Vee {
/**
* Draw this shape at the end of a line.
*
* @param {CanvasRenderingContext2D} ctx
* @param {ArrowData} arrowData
* @static
*/
static draw(ctx, arrowData) {
// Normalized points of closed path, in the order that they should be drawn.
// (0, 0) is the attachment point, and the point around which should be rotated
var points = [
{ x:-1, y: 0.3},
{ x:-0.5, y: 0},
{ x:-1, y:-0.3},
{ x:0, y: 0},
];
EndPoint.transform(points, arrowData);
EndPoint.drawPath(ctx, points);
}
}
/** /**
* Drawing methods for the endpoints. * Drawing methods for the endpoints.
@ -196,9 +420,33 @@ class EndPoints {
case 'circle': case 'circle':
Circle.draw(ctx, arrowData); Circle.draw(ctx, arrowData);
break; break;
case 'box':
Box.draw(ctx, arrowData);
break;
case 'crow':
Crow.draw(ctx, arrowData);
break;
case 'curve':
Curve.draw(ctx, arrowData);
break;
case 'diamond':
Diamond.draw(ctx, arrowData);
break;
case 'inv_curve':
InvertedCurve.draw(ctx, arrowData);
break;
case 'triangle':
Triangle.draw(ctx, arrowData);
break;
case 'inv_triangle':
InvertedTriangle.draw(ctx, arrowData);
break;
case 'bar': case 'bar':
Bar.draw(ctx, arrowData); Bar.draw(ctx, arrowData);
break; break;
case 'vee':
Vee.draw(ctx, arrowData);
break;
case 'arrow': // fall-through case 'arrow': // fall-through
default: default:
Arrow.draw(ctx, arrowData); Arrow.draw(ctx, arrowData);

Loading…
Cancel
Save