Skip to content

Commit

Permalink
feat(arrow-heads): implement image arrow heads (#157)
Browse files Browse the repository at this point in the history
* feat(arrow-heads): implement image arrow heads

* chore(examples): add image arrow heads example

* style(arrow-heads): reformat

* docs(arrow-heads): add docs

* style(arrow-heads): reformat and reorganize new code

* docs(arrow-heads): replace external image
  • Loading branch information
Thomaash authored Oct 27, 2019
1 parent e35ef67 commit 12b15e1
Show file tree
Hide file tree
Showing 8 changed files with 435 additions and 53 deletions.
47 changes: 43 additions & 4 deletions docs/network/edges.html
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,30 @@ <h3>Options</h3>
var options = {
edges:{
arrows: {
to: {enabled: false, scaleFactor:1, type:'arrow'},
middle: {enabled: false, scaleFactor:1, type:'arrow'},
from: {enabled: false, scaleFactor:1, type:'arrow'}
to: {
enabled: false,
imageHeight: undefined,
imageWidth: undefined,
scaleFactor: 1,
src: undefined,
type: "arrow"
},
middle: {
enabled: false,
imageHeight: 32,
imageWidth: 32,
scaleFactor: 1,
src: "https://visjs.org/images/visjs_logo.png",
type: "image"
},
from: {
enabled: false,
imageHeight: undefined,
imageWidth: undefined,
scaleFactor: 1,
src: undefined,
type: "arrow"
}
},
arrowStrikethrough: true,
chosen: true,
Expand Down Expand Up @@ -255,17 +276,35 @@ <h3>Options</h3>
enabled will be set to true.
</td>
</tr>
<tr parent="arrows" class="hidden">
<td class="indent2">arrows.to.imageHeight</td>
<td>Number</td>
<td><code>undefined</code></td>
<td>The height of the image arrow. The height of the image file is used if this isn't defined.</td>.
</tr>
<tr parent="arrows" class="hidden">
<td class="indent2">arrows.to.imageWidth</td>
<td>Number</td>
<td><code>undefined</code></td>
<td>The width of the image arrow. The width of the image file is used if this isn't defined.</td>.
</tr>
<tr parent="arrows" class="hidden">
<td class="indent2">arrows.to.scaleFactor</td>
<td>Number</td>
<td><code>1</code></td>
<td>The scale factor allows you to change the size of the arrowhead.</td>
</tr>
<tr parent="arrows" class="hidden">
<td class="indent2">arrows.to.src</td>
<td>String</td>
<td><code>undefined</code></td>
<td>The URL for the image arrow type.</td>.
</tr>
<tr parent="arrows" class="hidden">
<td class="indent2">arrows.to.type</td>
<td>String</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> and <code>image</code>. The default is <code>arrow</code></td>.
</tr>
<tr parent="arrows" class="hidden">
<td class="indent">arrows.middle</td>
Expand Down
138 changes: 138 additions & 0 deletions examples/network/edgeStyles/imageArrowHeads.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Vis Network | Edge Styles | Image Arrow Heads</title>

<style type="text/css">
html,
body,
#mynetwork {
margin: 0px;
padding: 0px;
}

#mynetwork {
position: fixed;
left: 0px;
top: 0px;
bottom: 0px;
right: 50%;
min-height: 100vh;
border-right: 1px solid lightgray;
background: white;
}

#text {
position: absolute;
left: 50%;
right: 0%;
padding: 1em;
}

#title {
margin-bottom: 5em;
}

pre {
overflow-x: auto;
}
</style>

<script
type="text/javascript"
src="../../../../dist/vis-network.min.js"
></script>
<link
href="../../../../dist/vis-network.min.css"
rel="stylesheet"
type="text/css"
/>
</head>

<body>
<div id="text">
<div id="title">
<h1>Vis Network</h1>
<h2>Edge Styles</h2>
<h3>Image Arrow Heads</h3>
</div>

<ul>
<li>
All formats supported by canvas are supported here (like SVG, PNG,
JPG).
</li>
<li>Image can be specified using any URL including data URLs.</li>
<li>
Width and height can be set explicitly to scale the image to the
desired size.
</li>
<li>
The arrow head points to the top with the point in the middle of the
top edge of the image.
</li>
</ul>

<pre><code>
const options = {
edges: {
arrows: {
to: {
enabled: true,
type: "image",
imageWidth: 24,
imageHeight: 24,
src:
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='width:24px;height:24px' viewBox='0 0 24 24'%3E%3Cpath fill='%23000000' d='M19,3A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3H9.18C9.6,1.84 10.7,1 12,1C13.3,1 14.4,1.84 14.82,3H19M12,8L7,13H10V17H14V13H17L12,8M12,3A1,1 0 0,0 11,4A1,1 0 0,0 12,5A1,1 0 0,0 13,4A1,1 0 0,0 12,3Z' /%3E%3C/svg%3E"
}
}
}
};
</code></pre>
</div>

<div id="mynetwork"></div>
<script type="text/javascript">
// create an array with nodes
var nodes = new vis.DataSet([
{ id: 1, label: "Node 1" },
{ id: 2, label: "Node 2" },
{ id: 3, label: "Node 3" },
{ id: 4, label: "Node 4" },
{ id: 5, label: "Node 5" }
]);

// create an array with edges
var edges = new vis.DataSet([
{ from: 1, to: 3 },
{ from: 1, to: 2 },
{ from: 2, to: 4 },
{ from: 2, to: 5 },
{ from: 3, to: 3 }
]);

// create a network
var container = document.getElementById("mynetwork");
var data = {
nodes: nodes,
edges: edges
};
var options = {
edges: {
arrows: {
to: {
enabled: true,
type: "image",
imageWidth: 24,
imageHeight: 24,
src:
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='width:24px;height:24px' viewBox='0 0 24 24'%3E%3Cpath fill='%23000000' d='M19,3A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3H9.18C9.6,1.84 10.7,1 12,1C13.3,1 14.4,1.84 14.82,3H19M12,8L7,13H10V17H14V13H17L12,8M12,3A1,1 0 0,0 11,4A1,1 0 0,0 12,5A1,1 0 0,0 13,4A1,1 0 0,0 12,3Z' /%3E%3C/svg%3E"
}
}
}
};
var network = new vis.Network(container, data, options);
</script>
</body>
</html>
8 changes: 7 additions & 1 deletion lib/network/modules/EdgesHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,13 @@ class EdgesHandler {
* @returns {Edge}
*/
create(properties) {
return new Edge(properties, this.body, this.options, this.defaultOptions)
return new Edge(
properties,
this.body,
this.images,
this.options,
this.defaultOptions
);
}

/**
Expand Down
67 changes: 63 additions & 4 deletions lib/network/modules/components/Edge.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ class Edge {
/**
* @param {Object} options values specific to this edge, must contain at least 'from' and 'to'
* @param {Object} body shared state from Network instance
* @param {Network.Images} imagelist A list with images. Only needed when the edge has image arrows.
* @param {Object} globalOptions options from the EdgesHandler instance
* @param {Object} defaultOptions default options from the EdgeHandler instance. Value and reference are constant
*/
constructor(options, body, globalOptions, defaultOptions) {
constructor(options, body, imagelist, globalOptions, defaultOptions) {
if (body === undefined) {
throw new Error("No body provided");
}
Expand All @@ -25,6 +26,7 @@ class Edge {
this.globalOptions = globalOptions;
this.defaultOptions = defaultOptions;
this.body = body;
this.imagelist = imagelist;

// initialize variables
this.id = undefined;
Expand Down Expand Up @@ -263,12 +265,21 @@ class Edge {
toArrow: toArrow,
toArrowScale: this.options.arrows.to.scaleFactor,
toArrowType: this.options.arrows.to.type,
toArrowSrc: this.options.arrows.to.src,
toArrowImageWidth: this.options.arrows.to.imageWidth,
toArrowImageHeight: this.options.arrows.to.imageHeight,
middleArrow: middleArrow,
middleArrowScale: this.options.arrows.middle.scaleFactor,
middleArrowType: this.options.arrows.middle.type,
middleArrowSrc: this.options.arrows.middle.src,
middleArrowImageWidth: this.options.arrows.middle.imageWidth,
middleArrowImageHeight: this.options.arrows.middle.imageHeight,
fromArrow: fromArrow,
fromArrowScale: this.options.arrows.from.scaleFactor,
fromArrowType: this.options.arrows.from.type,
fromArrowSrc: this.options.arrows.from.src,
fromArrowImageWidth: this.options.arrows.from.imageWidth,
fromArrowImageHeight: this.options.arrows.from.imageHeight,
arrowStrikethrough: this.options.arrowStrikethrough,
color: (inheritsColor? undefined : this.options.color.color),
inheritsColor: inheritsColor,
Expand Down Expand Up @@ -535,19 +546,67 @@ class Edge {

// from and to arrows give a different end point for edges. we set them here
if (values.fromArrow) {
arrowData.from = this.edgeType.getArrowData(ctx, 'from', viaNode, this.selected, this.hover, values);
arrowData.from = this.edgeType.getArrowData(
ctx,
"from",
viaNode,
this.selected,
this.hover,
values
);
if (values.arrowStrikethrough === false)
this.edgeType.fromPoint = arrowData.from.core;
if (values.fromArrowSrc) {
arrowData.from.image = this.imagelist.load(values.fromArrowSrc);
}
if (values.fromArrowImageWidth) {
arrowData.from.imageWidth = values.fromArrowImageWidth;
}
if (values.fromArrowImageHeight) {
arrowData.from.imageHeight = values.fromArrowImageHeight;
}
}
if (values.toArrow) {
arrowData.to = this.edgeType.getArrowData(ctx, 'to', viaNode, this.selected, this.hover, values);
arrowData.to = this.edgeType.getArrowData(
ctx,
"to",
viaNode,
this.selected,
this.hover,
values
);
if (values.arrowStrikethrough === false)
this.edgeType.toPoint = arrowData.to.core;
if (values.toArrowSrc) {
arrowData.to.image = this.imagelist.load(values.toArrowSrc);
}
if (values.toArrowImageWidth) {
arrowData.to.imageWidth = values.toArrowImageWidth;
}
if (values.toArrowImageHeight) {
arrowData.to.imageHeight = values.toArrowImageHeight;
}
}

// the middle arrow depends on the line, which can depend on the to and from arrows so we do this one lastly.
if (values.middleArrow) {
arrowData.middle = this.edgeType.getArrowData(ctx,'middle', viaNode, this.selected, this.hover, values);
arrowData.middle = this.edgeType.getArrowData(
ctx,
"middle",
viaNode,
this.selected,
this.hover,
values
);
if (values.middleArrowSrc) {
arrowData.middle.image = this.imagelist.load(values.middleArrowSrc);
}
if (values.middleArrowImageWidth) {
arrowData.middle.imageWidth = values.middleArrowImageWidth;
}
if (values.middleArrowImageHeight) {
arrowData.middle.imageHeight = values.middleArrowImageHeight;
}
}

// draw everything
Expand Down
14 changes: 8 additions & 6 deletions lib/network/modules/components/edges/util/edge-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -798,13 +798,15 @@ export abstract class EdgeBase<Via = undefined> implements EdgeType {
ctx.fillStyle = ctx.strokeStyle;
ctx.lineWidth = values.width;

EndPoints.draw(ctx, arrowData);
const canFill = EndPoints.draw(ctx, arrowData);

// draw shadow if enabled
this.enableShadow(ctx, values);
ctx.fill();
// disable shadows for other elements.
this.disableShadow(ctx, values);
if (canFill) {
// draw shadow if enabled
this.enableShadow(ctx, values);
ctx.fill();
// disable shadows for other elements.
this.disableShadow(ctx, values);
}
}

/**
Expand Down
Loading

0 comments on commit 12b15e1

Please sign in to comment.