diff --git a/docs/network.html b/docs/network.html
index 4d729591..7054abe4 100644
--- a/docs/network.html
+++ b/docs/network.html
@@ -940,11 +940,11 @@ All options defined per-node override these global settings.
Define the shape for the node.
Choose from
ellipse
(default), circle
, box
,
- database
, image
, label
, dot
,
+ database
, image
, circularImage
, label
, dot
,
star
, triangle
, triangleDown
, and square
.
- In case of image
, a property with name image
must
+ In case of image
and circularImage
, a property with name image
must
be provided, containing image urls.
diff --git a/examples/network/34_circular_images.html b/examples/network/34_circular_images.html
new file mode 100644
index 00000000..9e323d21
--- /dev/null
+++ b/examples/network/34_circular_images.html
@@ -0,0 +1,87 @@
+
+
+
+ Network | Scalable images
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/network/index.html b/examples/network/index.html
index d3d4d6ad..ac8c7bdd 100644
--- a/examples/network/index.html
+++ b/examples/network/index.html
@@ -45,6 +45,7 @@
31_localization.html
32_hierarchicaLayoutMethods.html
33_animation.html
+ 34_circular_images.html
graphviz_gallery.html
diff --git a/lib/network/Images.js b/lib/network/Images.js
index 0097fb16..9e6962d5 100644
--- a/lib/network/Images.js
+++ b/lib/network/Images.js
@@ -23,10 +23,11 @@ Images.prototype.setOnloadCallback = function(callback) {
* @return {Image} img The image object
*/
Images.prototype.load = function(url, brokenUrl) {
- if (this.images[url] == undefined) {
+ var img = this.images[url];
+ if (img === undefined) {
// create the image
var me = this;
- var img = new Image();
+ img = new Image();
img.onload = function () {
// IE11 fix -- thanks dponch!
diff --git a/lib/network/Node.js b/lib/network/Node.js
index ab5c66b7..074ee3e7 100644
--- a/lib/network/Node.js
+++ b/lib/network/Node.js
@@ -221,7 +221,7 @@ Node.prototype.setProperties = function(properties, constants) {
this.radiusFixed = this.radiusFixed || (properties.radius !== undefined);
- if (this.options.shape == 'image') {
+ if (this.options.shape === 'image' || this.options.shape === 'circularImage') {
this.options.radiusMin = constants.nodes.widthMin;
this.options.radiusMax = constants.nodes.widthMax;
}
@@ -234,6 +234,7 @@ Node.prototype.setProperties = function(properties, constants) {
case 'ellipse': this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break;
// TODO: add diamond shape
case 'image': this.draw = this._drawImage; this.resize = this._resizeImage; break;
+ case 'circularImage': this.draw = this._drawCircularImage; this.resize = this._resizeCircularImage; break;
case 'text': this.draw = this._drawText; this.resize = this._resizeText; break;
case 'dot': this.draw = this._drawDot; this.resize = this._resizeShape; break;
case 'square': this.draw = this._drawSquare; this.resize = this._resizeShape; break;
@@ -562,12 +563,7 @@ Node.prototype._resizeImage = function (ctx) {
};
-Node.prototype._drawImage = function (ctx) {
- this._resizeImage(ctx);
- this.left = this.x - this.width / 2;
- this.top = this.y - this.height / 2;
-
- var yLabel;
+Node.prototype._drawImageAtPosition = function (ctx) {
if (this.imageObj.width != 0 ) {
// draw the shade
if (this.clusterSize > 1) {
@@ -582,6 +578,13 @@ Node.prototype._drawImage = function (ctx) {
// draw the image
ctx.globalAlpha = 1.0;
ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height);
+ }
+};
+
+Node.prototype._drawImageLabel = function (ctx) {
+ var yLabel;
+ if (this.imageObj.width != 0 ) {
+
yLabel = this.y + this.height / 2;
}
else {
@@ -589,18 +592,64 @@ Node.prototype._drawImage = function (ctx) {
yLabel = this.y;
}
+ this._label(ctx, this.label, this.x, yLabel, undefined, "top");
+};
+
+Node.prototype._drawImage = function (ctx) {
+ this._resizeImage(ctx);
+ this.left = this.x - this.width / 2;
+ this.top = this.y - this.height / 2;
+
+ this._drawImageAtPosition(ctx);
this.boundingBox.top = this.top;
this.boundingBox.left = this.left;
this.boundingBox.right = this.left + this.width;
this.boundingBox.bottom = this.top + this.height;
- this._label(ctx, this.label, this.x, yLabel, undefined, "top");
+ this._drawImageLabel(ctx);
this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left);
this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width);
this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height);
};
+Node.prototype._resizeCircularImage = function (ctx) {
+ this._resizeImage(ctx);
+};
+
+Node.prototype._drawCircularImage = function (ctx) {
+ this._resizeCircularImage(ctx);
+
+ this.left = this.x - this.width / 2;
+ this.top = this.y - this.height / 2;
+
+ var centerX = this.left + (this.width / 2);
+ var centerY = this.top + (this.height / 2);
+ var radius = Math.abs(this.height / 2);
+
+ this._drawRawCircle(ctx, centerX, centerY, radius);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.arc(centerX, centerY, radius, 0, Math.PI * 2, true);
+ ctx.closePath();
+ ctx.clip();
+
+ this._drawImageAtPosition(ctx);
+
+ ctx.restore();
+
+ this.boundingBox.top = this.y - this.options.radius;
+ this.boundingBox.left = this.x - this.options.radius;
+ this.boundingBox.right = this.x + this.options.radius;
+ this.boundingBox.bottom = this.y + this.options.radius;
+
+ this._drawImageLabel(ctx);
+
+ this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left);
+ this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width);
+ this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height);
+};
Node.prototype._resizeBox = function (ctx) {
if (!this.width) {
@@ -729,15 +778,11 @@ Node.prototype._resizeCircle = function (ctx) {
}
};
-Node.prototype._drawCircle = function (ctx) {
- this._resizeCircle(ctx);
- this.left = this.x - this.width / 2;
- this.top = this.y - this.height / 2;
-
+Node.prototype._drawRawCircle = function (ctx, x, y, radius) {
var clusterLineWidth = 2.5;
var borderWidth = this.options.borderWidth;
var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth;
-
+
ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border;
// draw the outer border
@@ -746,7 +791,7 @@ Node.prototype._drawCircle = function (ctx) {
ctx.lineWidth *= this.networkScaleInv;
ctx.lineWidth = Math.min(this.width,ctx.lineWidth);
- ctx.circle(this.x, this.y, this.options.radius+2*ctx.lineWidth);
+ ctx.circle(x, y, radius+2*ctx.lineWidth);
ctx.stroke();
}
ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0);
@@ -754,9 +799,17 @@ Node.prototype._drawCircle = function (ctx) {
ctx.lineWidth = Math.min(this.width,ctx.lineWidth);
ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background;
- ctx.circle(this.x, this.y, this.options.radius);
+ ctx.circle(this.x, this.y, radius);
ctx.fill();
ctx.stroke();
+};
+
+Node.prototype._drawCircle = function (ctx) {
+ this._resizeCircle(ctx);
+ this.left = this.x - this.width / 2;
+ this.top = this.y - this.height / 2;
+
+ this._drawRawCircle(ctx, this.x, this.y, this.options.radius);
this.boundingBox.top = this.y - this.options.radius;
this.boundingBox.left = this.x - this.options.radius;