diff --git a/docs/network/edges.html b/docs/network/edges.html index 1926db29..4125341c 100644 --- a/docs/network/edges.html +++ b/docs/network/edges.html @@ -120,6 +120,7 @@ var options = { from: {enabled: false, scaleFactor:1, type:'arrow'} }, arrowStrikethrough: true, + chosen: true, color: { color:'#848484', highlight:'#848484', @@ -298,6 +299,101 @@ network.setOptions(options); true When false, the edge stops at the arrow. This can be useful if you have thick lines and you want the arrow to end in a point. Middle arrows are not affected by this. + + chosen + Object or Boolean + true + + When true, selecting or hovering on an edge will change it and its label's characteristics according the default. + When false, no change to the edge or its label will occur when the edge is chosen. + If an object is supplied, finer-grained adjustment of edge and label characteristics is available when an edge is chosen. + + + + chosen.edge + Function or Boolean + undefined + + When true, selecting or hovering on an edge will change its characteristics according the default. + When false, no change to the edge will occur when the edge is chosen. +

+ If a function is supplied, it is called when the edge is chosen. +

+  function(values, id, selected, hovering) {
+    values.property = chosenValue;
+  }
+

+

+ Any of the incoming arguments may be used to determine characteristic changes. + If a property is not specifically assigned by the supplied function, it will be left unchanged. + A specific function may be assigned to each particular edge in its options, or to all in the network's edges options. +

+

+ The properties define the characteristics that can be changed as follows: +

+ + + + + + + + + + + + + + + + + + + + + + + + +
PropertyEdge Reference
dashessee dashes
toArrowsee arrows.to.enabled
toArrowScalesee arrows.to.scaleFactor
toArrowTypesee arrows.to.type
middleArrowsee arrows.middle.enabled
middleArrowScalesee arrows.middle.scaleFactor
middleArrowTypesee arrows.middle.type
fromArrowsee arrows.from.enabled
fromArrowScalesee arrows.from.scaleFactor
fromArrowTypesee arrows.from.type
arrowStrikethroughsee arrowStrikethrough
colorsee color.color
inheritsColorsee color.inherit
opacitysee color.opacity
hiddensee hidden
lengthsee length
shadowsee shadow.enabled
shadowColorsee shadow.color
shadowSizesee shadow.size
shadowXsee shadow.x
shadowYsee shadow.y
widthsee width
+
+ + + + chosen.label + Function or Boolean + undefined + + When true, selecting or hovering on an edge will change its label's characteristics according the default. + When false, no change to the edge's label will occur when the edge is chosen. +

+ If a function is supplied, it is called when the edge is chosen. +

+  function(values, id, selected, hovering) {
+    values.property = chosenValue;
+  }
+

+

+ Any of the incoming arguments may be used to determine characteristic changes. + If a property is not specifically assigned by the supplied function, it will be left unchanged. + A specific function may be assigned to each particular edge in its options, or to all in the network's edges options. +

+

+ The properties define the characteristics that can be changed as follows: +

+ + + + + + + + + +
PropertyEdge Reference
colorsee font.color
sizesee font.size
facesee font.face
modfont modifier ('bold', 'italic', etc.)
vadjustsee font.vadjust
strokeWidthsee font.strokeWidth
strokeColorsee font.strokeColor
+
+ + color Object or String @@ -311,7 +407,7 @@ network.setOptions(options); color.color String '#848484' - The color of the border of the node when it is not selected or hovered over (assuming hover is + The color of the edge when it is not selected or hovered over (assuming hover is enabled in the interaction module). diff --git a/docs/network/nodes.html b/docs/network/nodes.html index 53cc052e..15a5bcef 100644 --- a/docs/network/nodes.html +++ b/docs/network/nodes.html @@ -110,6 +110,7 @@ var options = { borderWidth: 1, borderWidthSelected: 2, brokenImage:undefined, + chosen: true, color: { border: '#2B7CE9', background: '#97C2FC', @@ -271,6 +272,90 @@ network.setOptions(options); a backup image in case the URL supplied in the image option cannot be resolved. + + chosen + Object or Boolean + true + + When true, selecting or hovering on a node will change it and its label's characteristics according the default. + When false, no change to the node or its label will occur when the node is chosen. + If an object is supplied, finer-grained adjustment of node and label characteristics is available when a node is chosen. + + + + chosen.node + Function or Boolean + undefined + + When true, selecting or hovering on a node will change its characteristics according the default. + When false, no change to the node will occur when the node is chosen. +

+ If a function is supplied, it is called when the node is chosen. +

+  function(values, id, selected, hovering) {
+    values.property = chosenValue;
+  }
+

+

+ Any of the incoming arguments may be used to determine characteristic changes. + If a property is not specifically assigned by the supplied function, it will be left unchanged. + A specific function may be assigned to each particular node in its options, or to all in the network's nodes options. +

+

+ The properties define the characteristics that can be changed as follows: +

+ + + + + + + + + + + + + +
PropertyNode Reference
colorsee color.background
borderWidthsee borderWidth
borderColorsee color.border
sizesee size
borderDashessee shapeProperties.borderDashes
borderRadiussee shapeProperties.borderRadius
shadowsee shadow.enabled
shadowColorsee shadow.color
shadowSizesee shadow.size
shadowXsee shadow.x
shadowYsee shadow.y
+
+ + + + chosen.label + Function or Boolean + undefined + + When true, selecting or hovering on a node will change its label's characteristics according the default. + When false, no change to the node's label will occur when the node is chosen. +

+ If a function is supplied, it is called when the node is chosen. +

+  function(values, id, selected, hovering) {
+    values.property = chosenValue;
+  }
+

+

+ Any of the incoming arguments may be used to determine characteristic changes. + If a property is not specifically assigned by the supplied function, it will be left unchanged. + A specific function may be assigned to each particular node in its options, or to all in the network's nodes options. +

+

+ The properties define the characteristics that can be changed as follows: +

+ + + + + + + + + +
PropertyNode Reference
colorsee font.color
sizesee font.size
facesee font.face
modfont modifier ('bold', 'italic', etc.)
vadjustsee font.vadjust
strokeWidthsee font.strokeWidth
strokeColorsee font.strokeColor
+
+ + color Object or String diff --git a/examples/network/other/chosen.html b/examples/network/other/chosen.html new file mode 100644 index 00000000..15c7638d --- /dev/null +++ b/examples/network/other/chosen.html @@ -0,0 +1,466 @@ + + + + Network | Chosen Elements + + + + + + + + + + +

When a node or edge is selected or hovered its visible characteristics can be changed.

+ +
+ +

In this network, an element (node, edge or label) will change a characteristic when hovered, and it will be locked in when selected. + This is managed by setting up a 'chosen' function that will be called when the element containing the function is chosen. + These functions may be set on nodes, edges and labels, at the individual or group level.

+ +

All states (unselected, hover-over-unselected, selected, and hover-over selected) may be handled as needed by the application using vis, as the select and hover states are passed to the chosen function when called. + Additionally, the id of the element is passed to allow context-specific characteristic adjustment on select or hover as needed.

+ +

It should be noted that the characteristics which might affect the position of elements have been left out on purpose. + While it might be interesting to make them changeable, this is problematic on hovering. + Consider that the user hovers over an object. + If it changed characteristics that moved it outside of the hover-distance, it would then no longer be hovering. + So it would be moved back to its original prosition, within the hover-distance and then again be hovering over the object. + This hysteresis loop is kept from occurring by leaving out the characteristics that could cause it. + Some seemingly innocuous changes (such as resizing a node's label on hover that would in turn cause the node to resize and move out of hover-distance) may still cause hysteresis, but with care they should be avoidable.

+ + + + + + diff --git a/lib/network/modules/CanvasRenderer.js b/lib/network/modules/CanvasRenderer.js index a6c4ae1b..a4064e02 100644 --- a/lib/network/modules/CanvasRenderer.js +++ b/lib/network/modules/CanvasRenderer.js @@ -32,11 +32,9 @@ class CanvasRenderer { } bindEventListeners() { - this.body.emitter.on("dragStart", () => { - this.dragging = true; - }); - this.body.emitter.on("dragEnd", () => this.dragging = false); - this.body.emitter.on("_resizeNodes", () => this._resizeNodes()); + this.body.emitter.on("dragStart", () => { this.dragging = true; }); + this.body.emitter.on("dragEnd", () => { this.dragging = false; }); + this.body.emitter.on("_resizeNodes", () => { this._resizeNodes(); }); this.body.emitter.on("_redraw", () => { if (this.renderingActive === false) { this._redraw(); @@ -327,4 +325,4 @@ class CanvasRenderer { } -export default CanvasRenderer; \ No newline at end of file +export default CanvasRenderer; diff --git a/lib/network/modules/EdgesHandler.js b/lib/network/modules/EdgesHandler.js index 320b7742..549cf598 100644 --- a/lib/network/modules/EdgesHandler.js +++ b/lib/network/modules/EdgesHandler.js @@ -151,7 +151,6 @@ class EdgesHandler { // this is called when options of EXISTING nodes or edges have changed. this.body.emitter.on("_dataUpdated", () => { this.reconnectEdges(); - this.markAllEdgesAsDirty(); }); // refresh the edges. Used when reverting from hierarchical layout @@ -177,11 +176,6 @@ class EdgesHandler { // use the parser from the Edge class to fill in all shorthand notations Edge.parseOptions(this.options, options); - // handle multiple input cases for color - if (options.color !== undefined) { - this.markAllEdgesAsDirty(); - } - // update smooth settings in all edges let dataChanged = false; if (options.smooth !== undefined) { @@ -361,13 +355,6 @@ class EdgesHandler { return new Edge(properties, this.body, this.options, this.defaultOptions, this.edgeOptions) } - - markAllEdgesAsDirty() { - for (var edgeId in this.body.edges) { - this.body.edges[edgeId].edgeType.colorDirty = true; - } - } - /** * Reconnect all edges * @private diff --git a/lib/network/modules/components/Edge.js b/lib/network/modules/components/Edge.js index dc75ab1f..80343f0c 100644 --- a/lib/network/modules/components/Edge.js +++ b/lib/network/modules/components/Edge.js @@ -39,7 +39,6 @@ class Edge { this.selected = false; this.hover = false; this.labelDirty = true; - this.colorDirty = true; this.baseWidth = this.options.width; this.baseFontSize = this.options.font.size; @@ -65,16 +64,26 @@ class Edge { if (!options) { return; } - this.colorDirty = true; Edge.parseOptions(this.options, options, true, this.globalOptions); - if (options.id !== undefined) {this.id = options.id;} - if (options.from !== undefined) {this.fromId = options.from;} - if (options.to !== undefined) {this.toId = options.to;} - if (options.title !== undefined) {this.title = options.title;} - if (options.value !== undefined) {options.value = parseFloat(options.value);} + if (options.id !== undefined) { + this.id = options.id; + } + if (options.from !== undefined) { + this.fromId = options.from; + } + if (options.to !== undefined) { + this.toId = options.to; + } + if (options.title !== undefined) { + this.title = options.title; + } + if (options.value !== undefined) { + options.value = parseFloat(options.value); + } + this.choosify(options); // update label Module this.updateLabelModule(options); @@ -197,6 +206,94 @@ class Edge { } } + choosify(options) { + this.chooser = true; + + let pile = [options, this.options, this.defaultOptions]; + + let chosen = util.topMost(pile, 'chosen'); + if (typeof chosen === 'boolean') { + this.chooser = chosen; + } else if (typeof chosen === 'object') { + let chosenEdge = util.topMost(pile, ['chosen', 'edge']); + if ((typeof chosenEdge === 'boolean') || (typeof chosenEdge === 'function')) { + this.chooser = chosenEdge; + } + } + } + + getFormattingValues() { + let toArrow = (this.options.arrows.to === true) || (this.options.arrows.to.enabled === true) + let fromArrow = (this.options.arrows.from === true) || (this.options.arrows.from.enabled === true) + let middleArrow = (this.options.arrows.middle === true) || (this.options.arrows.middle.enabled === true) + let inheritsColor = this.options.color.inherit; + let values = { + toArrow: toArrow, + toArrowScale: this.options.arrows.to.scaleFactor, + toArrowType: this.options.arrows.to.type, + middleArrow: middleArrow, + middleArrowScale: this.options.arrows.middle.scaleFactor, + middleArrowType: this.options.arrows.middle.type, + fromArrow: fromArrow, + fromArrowScale: this.options.arrows.from.scaleFactor, + fromArrowType: this.options.arrows.from.type, + arrowStrikethrough: this.options.arrowStrikethrough, + color: (inheritsColor? undefined : this.options.color.color), + inheritsColor: inheritsColor, + opacity: this.options.color.opacity, + hidden: this.options.hidden, + length: this.options.length, + shadow: this.options.shadow.enabled, + shadowColor: this.options.shadow.color, + shadowSize: this.options.shadow.size, + shadowX: this.options.shadow.x, + shadowY: this.options.shadow.y, + dashes: this.options.dashes, + width: this.options.width + }; + if (this.selected || this.hover) { + if (this.chooser === true) { + if (this.selected) { + let selectedWidth = this.options.selectionWidth; + if (typeof selectedWidth === 'function') { + values.width = selectedWidth(values.width); + } else if (typeof selectedWidth === 'number') { + values.width += selectedWidth; + } + values.width = Math.max(values.width, 0.3 / this.body.view.scale); + values.color = this.options.color.highlight; + values.shadow = this.options.shadow.enabled; + } else if (this.hover) { + let hoverWidth = this.options.hoverWidth; + if (typeof hoverWidth === 'function') { + values.width = hoverWidth(values.width); + } else if (typeof hoverWidth === 'number') { + values.width += hoverWidth; + } + values.width = Math.max(values.width, 0.3 / this.body.view.scale); + values.color = this.options.color.hover; + values.shadow = this.options.shadow.enabled; + } + } else if (typeof this.chooser === 'function') { + this.chooser(values, this.options.id, this.selected, this.hover); + if (values.color !== undefined) { + values.inheritsColor = false; + } + if (values.shadow === false) { + if ((values.shadowColor !== this.options.shadow.color) || + (values.shadowSize !== this.options.shadow.size) || + (values.shadowX !== this.options.shadow.x) || + (values.shadowY !== this.options.shadow.y)) { + values.shadow = true; + } + } + } + } else { + values.shadow = this.options.shadow.enabled; + values.width = Math.max(values.width, 0.3 / this.body.view.scale); + } + return values; + } /** * update the options in the label module @@ -207,6 +304,7 @@ class Edge { this.baseFontSize = this.labelModule.baseSize; } this.labelModule.constrain(this.edgeOptions, options, this.defaultOptions); + this.labelModule.choosify(this.edgeOptions, options, this.defaultOptions); } /** @@ -214,46 +312,47 @@ class Edge { * @returns {boolean} */ updateEdgeType() { + let smooth = this.options.smooth; let dataChanged = false; let changeInType = true; - let smooth = this.options.smooth; if (this.edgeType !== undefined) { - if (this.edgeType instanceof BezierEdgeDynamic && smooth.enabled === true && smooth.type === 'dynamic') {changeInType = false;} - if (this.edgeType instanceof CubicBezierEdge && smooth.enabled === true && smooth.type === 'cubicBezier') {changeInType = false;} - if (this.edgeType instanceof BezierEdgeStatic && smooth.enabled === true && smooth.type !== 'dynamic' && smooth.type !== 'cubicBezier') {changeInType = false;} - if (this.edgeType instanceof StraightEdge && smooth.enabled === false) {changeInType = false;} - + if ((((this.edgeType instanceof BezierEdgeDynamic) && + (smooth.enabled === true) && + (smooth.type === 'dynamic'))) || + (((this.edgeType instanceof CubicBezierEdge) && + (smooth.enabled === true) && + (smooth.type === 'cubicBezier'))) || + (((this.edgeType instanceof BezierEdgeStatic) && + (smooth.enabled === true) && + (smooth.type !== 'dynamic') && + (smooth.type !== 'cubicBezier'))) || + (((this.edgeType instanceof StraightEdge) && + (smooth.type.enabled === false)))) { + changeInType = false; + } if (changeInType === true) { dataChanged = this.cleanup(); } } - if (changeInType === true) { - if (this.options.smooth.enabled === true) { - if (this.options.smooth.type === 'dynamic') { + if (smooth.enabled === true) { + if (smooth.type === 'dynamic') { dataChanged = true; this.edgeType = new BezierEdgeDynamic(this.options, this.body, this.labelModule); - } - else if (this.options.smooth.type === 'cubicBezier') { + } else if (smooth.type === 'cubicBezier') { this.edgeType = new CubicBezierEdge(this.options, this.body, this.labelModule); - } - else { + } else { this.edgeType = new BezierEdgeStatic(this.options, this.body, this.labelModule); } - } - else { + } else { this.edgeType = new StraightEdge(this.options, this.body, this.labelModule); } - } - else { - // if nothing changes, we just set the options. + } else { // if nothing changes, we just set the options. this.edgeType.setOptions(this.options); } - return dataChanged; } - /** * Connect an edge to its nodes */ @@ -353,21 +452,18 @@ class Edge { this.updateLabelModule(); } - _setInteractionWidths() { - if (typeof this.options.hoverWidth === 'function') { - this.edgeType.hoverWidth = this.options.hoverWidth(this.options.width); - } - else { - this.edgeType.hoverWidth = this.options.hoverWidth + this.options.width; - } - - if (typeof this.options.selectionWidth === 'function') { - this.edgeType.selectionWidth = this.options.selectionWidth(this.options.width); - } - else { - this.edgeType.selectionWidth = this.options.selectionWidth + this.options.width; - } - } + _setInteractionWidths() { + if (typeof this.options.hoverWidth === 'function') { + this.edgeType.hoverWidth = this.options.hoverWidth(this.options.width); + } else { + this.edgeType.hoverWidth = this.options.hoverWidth + this.options.width; + } + if (typeof this.options.selectionWidth === 'function') { + this.edgeType.selectionWidth = this.options.selectionWidth(this.options.width); + } else { + this.edgeType.selectionWidth = this.options.selectionWidth + this.options.width; + } + } /** @@ -377,6 +473,11 @@ class Edge { * @param {CanvasRenderingContext2D} ctx */ draw(ctx) { + let values = this.getFormattingValues(); + if (values.hidden) { + return; + } + // get the via node from the edge type let viaNode = this.edgeType.getViaNode(); let arrowData = {}; @@ -386,33 +487,39 @@ class Edge { this.edgeType.toPoint = this.edgeType.to; // from and to arrows give a different end point for edges. we set them here - if (this.options.arrows.from.enabled === true) { - arrowData.from = this.edgeType.getArrowData(ctx,'from', viaNode, this.selected, this.hover); - if (this.options.arrowStrikethrough === false) + if (values.fromArrow) { + arrowData.from = this.edgeType.getArrowData(ctx, 'from', viaNode, this.selected, this.hover, values); + if (values.arrowStrikethrough === false) this.edgeType.fromPoint = arrowData.from.core; } - if (this.options.arrows.to.enabled === true) { - arrowData.to = this.edgeType.getArrowData(ctx,'to', viaNode, this.selected, this.hover); - if (this.options.arrowStrikethrough === false) + if (values.toArrow) { + arrowData.to = this.edgeType.getArrowData(ctx, 'to', viaNode, this.selected, this.hover, values); + if (values.arrowStrikethrough === false) this.edgeType.toPoint = arrowData.to.core; } // the middle arrow depends on the line, which can depend on the to and from arrows so we do this one lastly. - if (this.options.arrows.middle.enabled === true) { - arrowData.middle = this.edgeType.getArrowData(ctx,'middle', viaNode, this.selected, this.hover); + if (values.middleArrow) { + arrowData.middle = this.edgeType.getArrowData(ctx,'middle', viaNode, this.selected, this.hover, values); } // draw everything - this.edgeType.drawLine(ctx, this.selected, this.hover, viaNode); - this.drawArrows(ctx, arrowData); + this.edgeType.drawLine(ctx, values, this.selected, this.hover, viaNode); + this.drawArrows(ctx, arrowData, values); this.drawLabel (ctx, viaNode); } - drawArrows(ctx, arrowData) { - if (this.options.arrows.from.enabled === true) {this.edgeType.drawArrowHead(ctx, this.selected, this.hover, arrowData.from);} - if (this.options.arrows.middle.enabled === true) {this.edgeType.drawArrowHead(ctx, this.selected, this.hover, arrowData.middle);} - if (this.options.arrows.to.enabled === true) {this.edgeType.drawArrowHead(ctx, this.selected, this.hover, arrowData.to);} + drawArrows(ctx, arrowData, values) { + if (values.fromArrow) { + this.edgeType.drawArrowHead(ctx, values, this.selected, this.hover, arrowData.from); + } + if (values.middleArrow) { + this.edgeType.drawArrowHead(ctx, values, this.selected, this.hover, arrowData.middle); + } + if (values.toArrow) { + this.edgeType.drawArrowHead(ctx, values, this.selected, this.hover, arrowData.to); + } } @@ -429,13 +536,13 @@ class Edge { // if the label has to be rotated: if (this.options.font.align !== "horizontal") { - this.labelModule.calculateLabelSize(ctx,selected,point.x,point.y); + this.labelModule.calculateLabelSize(ctx, selected, this.hover, point.x, point.y); ctx.translate(point.x, this.labelModule.size.yLine); this._rotateForLabelAlignment(ctx); } // draw the label - this.labelModule.draw(ctx, point.x, point.y, selected); + this.labelModule.draw(ctx, point.x, point.y, selected, this.hover); ctx.restore(); } else { @@ -452,7 +559,7 @@ class Edge { y = node1.y - node1.shape.height * 0.5; } point = this._pointOnCircle(x, y, radius, 0.125); - this.labelModule.draw(ctx, point.x, point.y, selected); + this.labelModule.draw(ctx, point.x, point.y, selected, this.hover); } } } diff --git a/lib/network/modules/components/Node.js b/lib/network/modules/components/Node.js index e9c4976c..d2f0879e 100644 --- a/lib/network/modules/components/Node.js +++ b/lib/network/modules/components/Node.js @@ -139,6 +139,8 @@ class Node { // this transforms all shorthands into fully defined options Node.parseOptions(this.options, options, true, this.globalOptions); + this.choosify(options); + // load the images if (this.options.image !== undefined) { if (this.imagelist) { @@ -219,6 +221,66 @@ class Node { } } + choosify(options) { + this.chooser = true; + + let pile = [options, this.options, this.defaultOptions]; + + let chosen = util.topMost(pile, 'chosen'); + if (typeof chosen === 'boolean') { + this.chooser = chosen; + } else if (typeof chosen === 'object') { + let chosenNode = util.topMost(pile, ['chosen', 'node']); + if ((typeof chosenNode === 'boolean') || (typeof chosenNode === 'function')) { + this.chooser = chosenNode; + } + } + } + + getFormattingValues() { + let values = { + color: this.options.color.background, + borderWidth: this.options.borderWidth, + borderColor: this.options.color.border, + size: this.options.size, + borderDashes: this.options.shapeProperties.borderDashes, + borderRadius: this.options.shapeProperties.borderRadius, + shadow: this.options.shadow.enabled, + shadowColor: this.options.shadow.color, + shadowSize: this.options.shadow.size, + shadowX: this.options.shadow.x, + shadowY: this.options.shadow.y + }; + if (this.selected || this.hover) { + if (this.chooser === true) { + if (this.selected) { + values.borderWidth *= 2; + values.color = this.options.color.highlight.background; + values.borderColor = this.options.color.highlight.border; + values.shadow = this.options.shadow.enabled; + } else if (this.hover) { + values.color = this.options.color.hover.background; + values.borderColor = this.options.color.hover.border; + values.shadow = this.options.shadow.enabled; + } + } else if (typeof this.chooser === 'function') { + this.chooser(values, this.options.id, this.selected, this.hover); + if (values.shadow === false) { + if ((values.shadowColor !== this.options.shadow.color) || + (values.shadowSize !== this.options.shadow.size) || + (values.shadowX !== this.options.shadow.x) || + (values.shadowY !== this.options.shadow.y)) { + values.shadow = true; + } + } + } + } else { + values.shadow = this.options.shadow.enabled; + } + return values; + } + + updateLabelModule(options) { if (this.options.label === undefined || this.options.label === null) { this.options.label = ''; @@ -228,6 +290,7 @@ class Node { this.baseFontSize = this.labelModule.baseSize; } this.labelModule.constrain(this.nodeOptions, options, this.defaultOptions); + this.labelModule.choosify(this.nodeOptions, options, this.defaultOptions); } updateShape(currentShape) { @@ -396,7 +459,8 @@ class Node { * @param {CanvasRenderingContext2D} ctx */ draw(ctx) { - this.shape.draw(ctx, this.x, this.y, this.selected, this.hover); + let values = this.getFormattingValues(); + this.shape.draw(ctx, this.x, this.y, this.selected, this.hover, values); } @@ -413,7 +477,8 @@ class Node { * @param {CanvasRenderingContext2D} ctx */ resize(ctx) { - this.shape.resize(ctx, this.selected); + let values = this.getFormattingValues(); + this.shape.resize(ctx, this.selected, this.hover, values); } diff --git a/lib/network/modules/components/edges/BezierEdgeDynamic.js b/lib/network/modules/components/edges/BezierEdgeDynamic.js index c35d641a..a57081c4 100644 --- a/lib/network/modules/components/edges/BezierEdgeDynamic.js +++ b/lib/network/modules/components/edges/BezierEdgeDynamic.js @@ -102,7 +102,7 @@ class BezierEdgeDynamic extends BezierEdgeBase { * @param {CanvasRenderingContext2D} ctx * @private */ - _line(ctx, viaNode) { + _line(ctx, values, viaNode) { // draw a straight line ctx.beginPath(); ctx.moveTo(this.fromPoint.x, this.fromPoint.y); @@ -114,9 +114,9 @@ class BezierEdgeDynamic extends BezierEdgeBase { ctx.quadraticCurveTo(viaNode.x, viaNode.y, this.toPoint.x, this.toPoint.y); } // draw shadow if enabled - this.enableShadow(ctx); + this.enableShadow(ctx, values); ctx.stroke(); - this.disableShadow(ctx); + this.disableShadow(ctx, values); } getViaNode() { diff --git a/lib/network/modules/components/edges/BezierEdgeStatic.js b/lib/network/modules/components/edges/BezierEdgeStatic.js index d70ed7c0..fbfb788c 100644 --- a/lib/network/modules/components/edges/BezierEdgeStatic.js +++ b/lib/network/modules/components/edges/BezierEdgeStatic.js @@ -10,7 +10,7 @@ class BezierEdgeStatic extends BezierEdgeBase { * @param {CanvasRenderingContext2D} ctx * @private */ - _line(ctx, viaNode) { + _line(ctx, values, viaNode) { // draw a straight line ctx.beginPath(); ctx.moveTo(this.fromPoint.x, this.fromPoint.y); @@ -23,9 +23,9 @@ class BezierEdgeStatic extends BezierEdgeBase { ctx.quadraticCurveTo(viaNode.x, viaNode.y, this.toPoint.x, this.toPoint.y); } // draw shadow if enabled - this.enableShadow(ctx); + this.enableShadow(ctx, values); ctx.stroke(); - this.disableShadow(ctx); + this.disableShadow(ctx, values); } getViaNode() { diff --git a/lib/network/modules/components/edges/CubicBezierEdge.js b/lib/network/modules/components/edges/CubicBezierEdge.js index e7b76695..4c9579fb 100644 --- a/lib/network/modules/components/edges/CubicBezierEdge.js +++ b/lib/network/modules/components/edges/CubicBezierEdge.js @@ -10,7 +10,7 @@ class CubicBezierEdge extends CubicBezierEdgeBase { * @param {CanvasRenderingContext2D} ctx * @private */ - _line(ctx, viaNodes) { + _line(ctx, values, viaNodes) { // get the coordinates of the support points. let via1 = viaNodes[0]; let via2 = viaNodes[1]; @@ -27,9 +27,9 @@ class CubicBezierEdge extends CubicBezierEdgeBase { ctx.bezierCurveTo(via1.x, via1.y, via2.x, via2.y, this.toPoint.x, this.toPoint.y); } // draw shadow if enabled - this.enableShadow(ctx); + this.enableShadow(ctx, values); ctx.stroke(); - this.disableShadow(ctx); + this.disableShadow(ctx, values); } _getViaCoordinates() { diff --git a/lib/network/modules/components/edges/StraightEdge.js b/lib/network/modules/components/edges/StraightEdge.js index 6d7bf231..61aecbaf 100644 --- a/lib/network/modules/components/edges/StraightEdge.js +++ b/lib/network/modules/components/edges/StraightEdge.js @@ -10,15 +10,15 @@ class StraightEdge extends EdgeBase { * @param {CanvasRenderingContext2D} ctx * @private */ - _line(ctx) { + _line(ctx, values) { // draw a straight line ctx.beginPath(); ctx.moveTo(this.fromPoint.x, this.fromPoint.y); ctx.lineTo(this.toPoint.x, this.toPoint.y); // draw shadow if enabled - this.enableShadow(ctx); + this.enableShadow(ctx, values); ctx.stroke(); - this.disableShadow(ctx); + this.disableShadow(ctx, values); } getViaNode() { diff --git a/lib/network/modules/components/edges/util/EdgeBase.js b/lib/network/modules/components/edges/util/EdgeBase.js index 1371ab4e..a646cd4f 100644 --- a/lib/network/modules/components/edges/util/EdgeBase.js +++ b/lib/network/modules/components/edges/util/EdgeBase.js @@ -18,7 +18,10 @@ class EdgeBase { this.from = this.body.nodes[this.options.from]; this.to = this.body.nodes[this.options.to]; } - cleanup() {return false} + + cleanup() { + return false; + } setOptions(options) { this.options = options; @@ -34,36 +37,36 @@ class EdgeBase { * @param {CanvasRenderingContext2D} ctx * @private */ - drawLine(ctx, selected, hover, viaNode) { + drawLine(ctx, values, selected, hover, viaNode) { // set style - ctx.strokeStyle = this.getColor(ctx, selected, hover); - ctx.lineWidth = this.getLineWidth(selected, hover); + ctx.strokeStyle = this.getColor(ctx, values, selected, hover); + ctx.lineWidth = values.width; - if (this.options.dashes !== false) { - this._drawDashedLine(ctx, viaNode); + if (values.dashes !== false) { + this._drawDashedLine(ctx, values, viaNode); } else { - this._drawLine(ctx, viaNode); + this._drawLine(ctx, values, viaNode); } } - _drawLine(ctx, viaNode, fromPoint, toPoint) { + _drawLine(ctx, values, viaNode, fromPoint, toPoint) { if (this.from != this.to) { // draw line - this._line(ctx, viaNode, fromPoint, toPoint); + this._line(ctx, values, viaNode, fromPoint, toPoint); } else { - let [x,y,radius] = this._getCircleData(ctx); - this._circle(ctx, x, y, radius); + let [x,y,radius] = this._getCircleData(ctx, values); + this._circle(ctx, values, x, y, radius); } } - _drawDashedLine(ctx, viaNode, fromPoint, toPoint) { + _drawDashedLine(ctx, values, viaNode, fromPoint, toPoint) { ctx.lineCap = 'round'; let pattern = [5,5]; - if (Array.isArray(this.options.dashes) === true) { - pattern = this.options.dashes; + if (Array.isArray(values.dashes) === true) { + pattern = values.dashes; } // only firefox and chrome support this method, else we use the legacy one. @@ -77,11 +80,11 @@ class EdgeBase { // draw the line if (this.from != this.to) { // draw line - this._line(ctx, viaNode); + this._line(ctx, values, viaNode); } else { - let [x,y,radius] = this._getCircleData(ctx); - this._circle(ctx, x, y, radius); + let [x,y,radius] = this._getCircleData(ctx, values); + this._circle(ctx, values, x, y, radius); } // restore the dash settings. @@ -95,16 +98,16 @@ class EdgeBase { ctx.dashedLine(this.from.x, this.from.y, this.to.x, this.to.y, pattern); } else { - let [x,y,radius] = this._getCircleData(ctx); - this._circle(ctx, x, y, radius); + let [x,y,radius] = this._getCircleData(ctx, values); + this._circle(ctx, values, x, y, radius); } // draw shadow if enabled - this.enableShadow(ctx); + this.enableShadow(ctx, values); ctx.stroke(); // disable shadows for other elements. - this.disableShadow(ctx); + this.disableShadow(ctx, values); } } @@ -252,24 +255,23 @@ class EdgeBase { } - getColor(ctx, selected, hover) { - let colorOptions = this.options.color; - if (colorOptions.inherit !== false) { + getColor(ctx, values, selected, hover) { + if (values.inheritsColor !== false) { // when this is a loop edge, just use the 'from' method - if (colorOptions.inherit === 'both' && this.from.id !== this.to.id) { + if ((values.inheritsColor === 'both') && (this.from.id !== this.to.id)) { let grd = ctx.createLinearGradient(this.from.x, this.from.y, this.to.x, this.to.y); let fromColor, toColor; fromColor = this.from.options.color.highlight.border; toColor = this.to.options.color.highlight.border; - if (this.from.selected === false && this.to.selected === false) { - fromColor = util.overrideOpacity(this.from.options.color.border, this.options.color.opacity); - toColor = util.overrideOpacity(this.to.options.color.border, this.options.color.opacity); + if ((this.from.selected === false) && (this.to.selected === false)) { + fromColor = util.overrideOpacity(this.from.options.color.border, values.opacity); + toColor = util.overrideOpacity(this.to.options.color.border, values.opacity); } - else if (this.from.selected === true && this.to.selected === false) { + else if ((this.from.selected === true) && (this.to.selected === false)) { toColor = this.to.options.color.border; } - else if (this.from.selected === false && this.to.selected === true) { + else if ((this.from.selected === false) && (this.to.selected === true)) { fromColor = this.from.options.color.border; } grd.addColorStop(0, fromColor); @@ -279,37 +281,13 @@ class EdgeBase { return grd; } - if (this.colorDirty === true) { - if (colorOptions.inherit === "to") { - this.color.highlight = this.to.options.color.highlight.border; - this.color.hover = this.to.options.color.hover.border; - this.color.color = util.overrideOpacity(this.to.options.color.border, colorOptions.opacity); - } - else { // (this.options.color.inherit.source === "from") { - this.color.highlight = this.from.options.color.highlight.border; - this.color.hover = this.from.options.color.hover.border; - this.color.color = util.overrideOpacity(this.from.options.color.border, colorOptions.opacity); - } + if (values.inheritsColor === "to") { + return util.overrideOpacity(this.to.options.color.border, values.opacity); + } else { // "from" + return util.overrideOpacity(this.from.options.color.border, values.opacity); } - } - else if (this.colorDirty === true) { - this.color.highlight = colorOptions.highlight; - this.color.hover = colorOptions.hover; - this.color.color = util.overrideOpacity(colorOptions.color, colorOptions.opacity); - } - - // if color inherit is on and gradients are used, the function has already returned by now. - this.colorDirty = false; - - - if (selected === true) { - return this.color.highlight; - } - else if (hover === true) { - return this.color.hover; - } - else { - return this.color.color; + } else { + return util.overrideOpacity(values.color, values.opacity); } } @@ -321,9 +299,9 @@ class EdgeBase { * @param {Number} radius * @private */ - _circle(ctx, x, y, radius) { + _circle(ctx, values, x, y, radius) { // draw shadow if enabled - this.enableShadow(ctx); + this.enableShadow(ctx, values); // draw a circle ctx.beginPath(); @@ -331,7 +309,7 @@ class EdgeBase { ctx.stroke(); // disable shadows for other elements. - this.disableShadow(ctx); + this.disableShadow(ctx, values); } @@ -347,13 +325,13 @@ class EdgeBase { * @param {number} y3 * @private */ - getDistanceToEdge(x1, y1, x2, y2, x3, y3, via) { // x3,y3 is the point + getDistanceToEdge(x1, y1, x2, y2, x3, y3, via, values) { // x3,y3 is the point let returnValue = 0; if (this.from != this.to) { returnValue = this._getDistanceToEdge(x1, y1, x2, y2, x3, y3, via) } else { - let [x,y,radius] = this._getCircleData(); + let [x,y,radius] = this._getCircleData(undefined, values); let dx = x - x3; let dy = y - y3; returnValue = Math.abs(Math.sqrt(dx * dx + dy * dy) - radius); @@ -404,7 +382,7 @@ class EdgeBase { * @param position * @param viaNode */ - getArrowData(ctx, position, viaNode, selected, hover) { + getArrowData(ctx, position, viaNode, selected, hover, values) { // set lets let angle; let arrowPoint; @@ -413,27 +391,27 @@ class EdgeBase { let guideOffset; let scaleFactor; let type; - let lineWidth = this.getLineWidth(selected, hover); + let lineWidth = values.width; if (position === 'from') { node1 = this.from; node2 = this.to; guideOffset = 0.1; - scaleFactor = this.options.arrows.from.scaleFactor; - type = this.options.arrows.from.type; + scaleFactor = values.fromArrowScale; + type = values.fromArrowType; } else if (position === 'to') { node1 = this.to; node2 = this.from; guideOffset = -0.1; - scaleFactor = this.options.arrows.to.scaleFactor; - type = this.options.arrows.to.type; + scaleFactor = values.toArrowScale; + type = values.toArrowType; } else { node1 = this.to; node2 = this.from; - scaleFactor = this.options.arrows.middle.scaleFactor; - type = this.options.arrows.middle.type; + scaleFactor = values.middleArrowScale; + type = values.middleArrowType; } // if not connected to itself @@ -441,33 +419,28 @@ class EdgeBase { if (position !== 'middle') { // draw arrow head if (this.options.smooth.enabled === true) { - arrowPoint = this.findBorderPosition(node1, ctx, {via: viaNode}); + arrowPoint = this.findBorderPosition(node1, ctx, { via: viaNode }); let guidePos = this.getPoint(Math.max(0.0, Math.min(1.0, arrowPoint.t + guideOffset)), viaNode); angle = Math.atan2((arrowPoint.y - guidePos.y), (arrowPoint.x - guidePos.x)); - } - else { + } else { angle = Math.atan2((node1.y - node2.y), (node1.x - node2.x)); arrowPoint = this.findBorderPosition(node1, ctx); } - } - else { + } else { angle = Math.atan2((node1.y - node2.y), (node1.x - node2.x)); arrowPoint = this.getPoint(0.5, viaNode); // this is 0.6 to account for the size of the arrow. } - } - else { + } else { // draw circle let [x,y,radius] = this._getCircleData(ctx); if (position === 'from') { - arrowPoint = this.findBorderPosition(this.from, ctx, {x, y, low:0.25, high:0.6, direction:-1}); + arrowPoint = this.findBorderPosition(this.from, ctx, { x, y, low: 0.25, high: 0.6, direction: -1 }); angle = arrowPoint.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; - } - else if (position === 'to') { - arrowPoint = this.findBorderPosition(this.from, ctx, {x, y, low:0.6, high:1.0, direction:1}); + } else if (position === 'to') { + arrowPoint = this.findBorderPosition(this.from, ctx, { x, y, low: 0.6, high: 1.0, direction: 1 }); angle = arrowPoint.t * -2 * Math.PI + 1.5 * Math.PI - 1.1 * Math.PI; - } - else { + } else { arrowPoint = this._pointOnCircle(x, y, radius, 0.175); angle = 3.9269908169872414; // === 0.175 * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; } @@ -477,9 +450,9 @@ class EdgeBase { var xi = arrowPoint.x - length * 0.9 * Math.cos(angle); var yi = arrowPoint.y - length * 0.9 * Math.sin(angle); - let arrowCore = {x: xi, y: yi}; + let arrowCore = { x: xi, y: yi }; - return {point: arrowPoint, core: arrowCore, angle: angle, length: length, type: type}; + return { point: arrowPoint, core: arrowCore, angle: angle, length: length, type: type }; } /** @@ -489,11 +462,11 @@ class EdgeBase { * @param hover * @param arrowData */ - drawArrowHead(ctx, selected, hover, arrowData) { + drawArrowHead(ctx, values, selected, hover, arrowData) { // set style - ctx.strokeStyle = this.getColor(ctx, selected, hover); + ctx.strokeStyle = this.getColor(ctx, values, selected, hover); ctx.fillStyle = ctx.strokeStyle; - ctx.lineWidth = this.getLineWidth(selected, hover); + ctx.lineWidth = values.width; if (arrowData.type && arrowData.type.toLowerCase() === 'circle') { // draw circle at the end of the line @@ -504,24 +477,24 @@ class EdgeBase { } // draw shadow if enabled - this.enableShadow(ctx); + this.enableShadow(ctx, values); ctx.fill(); // disable shadows for other elements. - this.disableShadow(ctx); + this.disableShadow(ctx, values); } - enableShadow(ctx) { - if (this.options.shadow.enabled === true) { - ctx.shadowColor = this.options.shadow.color; - ctx.shadowBlur = this.options.shadow.size; - ctx.shadowOffsetX = this.options.shadow.x; - ctx.shadowOffsetY = this.options.shadow.y; + enableShadow(ctx, values) { + if (values.shadow === true) { + ctx.shadowColor = values.shadowColor; + ctx.shadowBlur = values.shadowSize; + ctx.shadowOffsetX = values.shadowX; + ctx.shadowOffsetY = values.shadowY; } } - disableShadow(ctx) { - if (this.options.shadow.enabled === true) { + disableShadow(ctx, values) { + if (values.shadow === true) { ctx.shadowColor = 'rgba(0,0,0,0)'; ctx.shadowBlur = 0; ctx.shadowOffsetX = 0; diff --git a/lib/network/modules/components/nodes/shapes/Box.js b/lib/network/modules/components/nodes/shapes/Box.js index 1d484be9..7f92b66c 100644 --- a/lib/network/modules/components/nodes/shapes/Box.js +++ b/lib/network/modules/components/nodes/shapes/Box.js @@ -8,59 +8,55 @@ class Box extends NodeBase { this._setMargins(labelModule); } - resize(ctx, selected) { - if (this.width === undefined) { - this.textSize = this.labelModule.getTextSize(ctx,selected); + resize(ctx, selected = this.selected, hover = this.hover) { + if ((this.width === undefined) || this.labelModule.differentState(selected, hover)) { + this.textSize = this.labelModule.getTextSize(ctx, selected, hover); this.width = this.textSize.width + this.margin.right + this.margin.left; this.height = this.textSize.height + this.margin.top + this.margin.bottom; this.radius = this.width / 2; } } - draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected); + draw(ctx, x, y, selected, hover, values) { + this.resize(ctx, selected, hover); this.left = x - this.width / 2; this.top = y - this.height / 2; - let borderWidth = this.options.borderWidth; - let selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - - ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - ctx.lineWidth = (selected ? selectionLineWidth : borderWidth); + ctx.strokeStyle = values.borderColor; + ctx.lineWidth = values.borderWidth; ctx.lineWidth /= this.body.view.scale; ctx.lineWidth = Math.min(this.width, ctx.lineWidth); - ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; + ctx.fillStyle = values.color; - let borderRadius = this.options.shapeProperties.borderRadius; // only effective for box - ctx.roundRect(this.left, this.top, this.width, this.height, borderRadius); + ctx.roundRect(this.left, this.top, this.width, this.height, values.borderRadius); // draw shadow if enabled - this.enableShadow(ctx); + this.enableShadow(ctx, values); // draw the background ctx.fill(); // disable shadows for other elements. - this.disableShadow(ctx); + this.disableShadow(ctx, values); //draw dashed border if enabled, save and restore is required for firefox not to crash on unix. ctx.save(); // if borders are zero width, they will be drawn with width 1 by default. This prevents that - if (borderWidth > 0) { - this.enableBorderDashes(ctx); + if (values.borderWidth > 0) { + this.enableBorderDashes(ctx, values); //draw the border ctx.stroke(); //disable dashed border for other elements - this.disableBorderDashes(ctx); + this.disableBorderDashes(ctx, values); } ctx.restore(); - this.updateBoundingBox(x,y,ctx,selected); + this.updateBoundingBox(x, y, ctx, selected, hover); this.labelModule.draw(ctx, this.left + this.textSize.width / 2 + this.margin.left, - this.top + this.textSize.height / 2 + this.margin.top, selected); + this.top + this.textSize.height / 2 + this.margin.top, selected, hover); } - updateBoundingBox(x,y, ctx, selected) { - this.resize(ctx, selected); + updateBoundingBox(x, y, ctx, selected, hover) { + this.resize(ctx, selected, hover); this.left = x - this.width / 2; this.top = y - this.height / 2; diff --git a/lib/network/modules/components/nodes/shapes/Circle.js b/lib/network/modules/components/nodes/shapes/Circle.js index 7b4aa436..d8a9b2a1 100644 --- a/lib/network/modules/components/nodes/shapes/Circle.js +++ b/lib/network/modules/components/nodes/shapes/Circle.js @@ -8,9 +8,9 @@ class Circle extends CircleImageBase { this._setMargins(labelModule); } - resize(ctx, selected) { - if (this.width === undefined) { - this.textSize = this.labelModule.getTextSize(ctx, selected); + resize(ctx, selected = this.selected, hover = this.hover, values = { size: this.options.size}) { + if ((this.width === undefined) || (this.labelModule.differentState(selected, hover))) { + this.textSize = this.labelModule.getTextSize(ctx, selected, hover); var diameter = Math.max(this.textSize.width + this.margin.right + this.margin.left, this.textSize.height + this.margin.top + this.margin.bottom); this.options.size = diameter / 2; @@ -21,21 +21,21 @@ class Circle extends CircleImageBase { } } - draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected); + draw(ctx, x, y, selected, hover, values) { + this.resize(ctx, selected, hover); this.left = x - this.width / 2; this.top = y - this.height / 2; - this._drawRawCircle(ctx, x, y, selected, hover, this.options.size); + this._drawRawCircle(ctx, x, y, selected, hover, values); - this.boundingBox.top = y - this.options.size; - this.boundingBox.left = x - this.options.size; - this.boundingBox.right = x + this.options.size; - this.boundingBox.bottom = y + this.options.size; + this.boundingBox.top = y - values.size; + this.boundingBox.left = x - values.size; + this.boundingBox.right = x + values.size; + this.boundingBox.bottom = y + values.size; this.updateBoundingBox(x,y); this.labelModule.draw(ctx, this.left + this.textSize.width / 2 + this.margin.left, - this.top + this.textSize.height / 2 + this.margin.top, selected); + this.top + this.textSize.height / 2 + this.margin.top, selected, hover); } updateBoundingBox(x,y) { diff --git a/lib/network/modules/components/nodes/shapes/CircularImage.js b/lib/network/modules/components/nodes/shapes/CircularImage.js index 7d04b3f5..8fa39b7f 100644 --- a/lib/network/modules/components/nodes/shapes/CircularImage.js +++ b/lib/network/modules/components/nodes/shapes/CircularImage.js @@ -10,17 +10,17 @@ class CircularImage extends CircleImageBase { this._swapToImageResizeWhenImageLoaded = true; } - resize() { - if (this.imageObj.src === undefined || this.imageObj.width === undefined || this.imageObj.height === undefined ) { - if (!this.width) { - var diameter = this.options.size * 2; - this.width = diameter; - this.height = diameter; - this._swapToImageResizeWhenImageLoaded = true; - this.radius = 0.5*this.width; - } - } - else { + resize(ctx, selected = this.selected, hover = this.hover) { + if ((this.imageObj.src === undefined) || + (this.imageObj.width === undefined) || + (this.imageObj.height === undefined) || + (this.labelModule.differentState(selected, hover))) { + var diameter = this.options.size * 2; + this.width = diameter; + this.height = diameter; + this._swapToImageResizeWhenImageLoaded = true; + this.radius = 0.5*this.width; + } else { if (this._swapToImageResizeWhenImageLoaded) { this.width = undefined; this.height = undefined; @@ -30,7 +30,7 @@ class CircularImage extends CircleImageBase { } } - draw(ctx, x, y, selected, hover) { + draw(ctx, x, y, selected, hover, values) { this.resize(); this.left = x - this.width / 2; @@ -39,18 +39,18 @@ class CircularImage extends CircleImageBase { let size = Math.min(0.5*this.height, 0.5*this.width); // draw the background circle. IMPORTANT: the stroke in this method is used by the clip method below. - this._drawRawCircle(ctx, x, y, selected, hover, size); + this._drawRawCircle(ctx, x, y, selected, hover, values); // now we draw in the circle, we save so we can revert the clip operation after drawing. ctx.save(); // clip is used to use the stroke in drawRawCircle as an area that we can draw in. ctx.clip(); // draw the image - this._drawImageAtPosition(ctx); + this._drawImageAtPosition(ctx, values); // restore so we can again draw on the full canvas ctx.restore(); - this._drawImageLabel(ctx, x, y, selected); + this._drawImageLabel(ctx, x, y, selected, hover); this.updateBoundingBox(x,y); } @@ -72,4 +72,4 @@ class CircularImage extends CircleImageBase { } } -export default CircularImage; \ No newline at end of file +export default CircularImage; diff --git a/lib/network/modules/components/nodes/shapes/Database.js b/lib/network/modules/components/nodes/shapes/Database.js index 3422cfdd..3c7490a1 100644 --- a/lib/network/modules/components/nodes/shapes/Database.js +++ b/lib/network/modules/components/nodes/shapes/Database.js @@ -8,9 +8,9 @@ class Database extends NodeBase { this._setMargins(labelModule); } - resize(ctx, selected) { - if (this.width === undefined) { - this.textSize = this.labelModule.getTextSize(ctx, selected); + resize(ctx, selected, hover) { + if ((this.width === undefined) || (this.labelModule.differentState(selected, hover))) { + this.textSize = this.labelModule.getTextSize(ctx, selected, hover); var size = this.textSize.width + this.margin.right + this.margin.left; this.width = size; this.height = size; @@ -18,47 +18,45 @@ class Database extends NodeBase { } } - draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected); + draw(ctx, x, y, selected, hover, values) { + this.resize(ctx, selected, hover); this.left = x - this.width / 2; this.top = y - this.height / 2; - var neutralborderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - var borderWidth = (selected ? selectionLineWidth : neutralborderWidth) / this.body.view.scale; + var borderWidth = values.borderWidth / this.body.view.scale; ctx.lineWidth = Math.min(this.width, borderWidth); - ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; + ctx.strokeStyle = values.borderColor; - ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; + ctx.fillStyle = values.color; ctx.database(x - this.width / 2, y - this.height / 2, this.width, this.height); // draw shadow if enabled - this.enableShadow(ctx); + this.enableShadow(ctx, values); // draw the background ctx.fill(); // disable shadows for other elements. - this.disableShadow(ctx); + this.disableShadow(ctx, values); //draw dashed border if enabled, save and restore is required for firefox not to crash on unix. ctx.save(); // if borders are zero width, they will be drawn with width 1 by default. This prevents that if (borderWidth > 0) { - this.enableBorderDashes(ctx); + this.enableBorderDashes(ctx, values); //draw the border ctx.stroke(); //disable dashed border for other elements - this.disableBorderDashes(ctx); + this.disableBorderDashes(ctx, values); } ctx.restore(); - this.updateBoundingBox(x,y,ctx,selected); + this.updateBoundingBox(x, y, ctx, selected, hover); this.labelModule.draw(ctx, this.left + this.textSize.width / 2 + this.margin.left, - this.top + this.textSize.height / 2 + this.margin.top, selected); + this.top + this.textSize.height / 2 + this.margin.top, selected, hover); } - updateBoundingBox(x,y,ctx, selected) { - this.resize(ctx, selected); + updateBoundingBox(x, y, ctx, selected, hover) { + this.resize(ctx, selected, hover); this.left = x - this.width * 0.5; this.top = y - this.height * 0.5; diff --git a/lib/network/modules/components/nodes/shapes/Diamond.js b/lib/network/modules/components/nodes/shapes/Diamond.js index 9a72caaa..182c984d 100644 --- a/lib/network/modules/components/nodes/shapes/Diamond.js +++ b/lib/network/modules/components/nodes/shapes/Diamond.js @@ -7,12 +7,12 @@ class Diamond extends ShapeBase { super(options, body, labelModule) } - resize(ctx) { - this._resizeShape(); + resize(ctx, values, selected = this.selected, hover = this.hover) { + this._resizeShape(selected, hover, values); } - draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'diamond', 4, x, y, selected, hover); + draw(ctx, x, y, selected, hover, values) { + this._drawShape(ctx, 'diamond', 4, x, y, selected, hover, values); } distanceToBorder(ctx, angle) { @@ -20,4 +20,4 @@ class Diamond extends ShapeBase { } } -export default Diamond; \ No newline at end of file +export default Diamond; diff --git a/lib/network/modules/components/nodes/shapes/Dot.js b/lib/network/modules/components/nodes/shapes/Dot.js index 68409139..701600fc 100644 --- a/lib/network/modules/components/nodes/shapes/Dot.js +++ b/lib/network/modules/components/nodes/shapes/Dot.js @@ -7,12 +7,12 @@ class Dot extends ShapeBase { super(options, body, labelModule) } - resize(ctx) { - this._resizeShape(); + resize(ctx, values, selected = this.selected, hover = this.hover) { + this._resizeShape(selected, hover, values); } - draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'circle', 2, x, y, selected, hover); + draw(ctx, x, y, selected, hover, values) { + this._drawShape(ctx, 'circle', 2, x, y, selected, hover, values); } distanceToBorder(ctx, angle) { @@ -21,4 +21,4 @@ class Dot extends ShapeBase { } } -export default Dot; \ No newline at end of file +export default Dot; diff --git a/lib/network/modules/components/nodes/shapes/Ellipse.js b/lib/network/modules/components/nodes/shapes/Ellipse.js index 69a2d6db..9669a1c7 100644 --- a/lib/network/modules/components/nodes/shapes/Ellipse.js +++ b/lib/network/modules/components/nodes/shapes/Ellipse.js @@ -7,9 +7,9 @@ class Ellipse extends NodeBase { super(options, body, labelModule); } - resize(ctx, selected) { - if (this.width === undefined) { - var textSize = this.labelModule.getTextSize(ctx, selected); + resize(ctx, selected = this.selected, hover = this.hover) { + if ((this.width === undefined) || (this.labelModule.differentState(selected, hover))) { + var textSize = this.labelModule.getTextSize(ctx, selected, hover); this.height = textSize.height * 2; this.width = textSize.width + this.height; @@ -17,48 +17,46 @@ class Ellipse extends NodeBase { } } - draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected); + draw(ctx, x, y, selected, hover, values) { + this.resize(ctx, selected, hover); this.left = x - this.width * 0.5; this.top = y - this.height * 0.5; - var neutralborderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - var borderWidth = (selected ? selectionLineWidth : neutralborderWidth) / this.body.view.scale; + var borderWidth = values.borderWidth / this.body.view.scale; ctx.lineWidth = Math.min(this.width, borderWidth); - ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; + ctx.strokeStyle = values.borderColor; - ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; + ctx.fillStyle = values.color; ctx.ellipse(this.left, this.top, this.width, this.height); // draw shadow if enabled - this.enableShadow(ctx); + this.enableShadow(ctx, values); // draw the background ctx.fill(); // disable shadows for other elements. - this.disableShadow(ctx); + this.disableShadow(ctx, values); //draw dashed border if enabled, save and restore is required for firefox not to crash on unix. ctx.save(); // if borders are zero width, they will be drawn with width 1 by default. This prevents that if (borderWidth > 0) { - this.enableBorderDashes(ctx); + this.enableBorderDashes(ctx, values); //draw the border ctx.stroke(); //disable dashed border for other elements - this.disableBorderDashes(ctx); + this.disableBorderDashes(ctx, values); } ctx.restore(); - this.updateBoundingBox(x, y, ctx, selected); - this.labelModule.draw(ctx, x, y, selected); + this.updateBoundingBox(x, y, ctx, selected, hover); + this.labelModule.draw(ctx, x, y, selected, hover); } - updateBoundingBox(x, y, ctx, selected) { - this.resize(ctx, selected); // just in case + updateBoundingBox(x, y, ctx, selected, hover) { + this.resize(ctx, selected, hover); // just in case this.left = x - this.width * 0.5; this.top = y - this.height * 0.5; diff --git a/lib/network/modules/components/nodes/shapes/Icon.js b/lib/network/modules/components/nodes/shapes/Icon.js index 6562abd8..2264fda9 100644 --- a/lib/network/modules/components/nodes/shapes/Icon.js +++ b/lib/network/modules/components/nodes/shapes/Icon.js @@ -8,8 +8,8 @@ class Icon extends NodeBase { this._setMargins(labelModule); } - resize(ctx) { - if (this.width === undefined) { + resize(ctx, selected, hover) { + if ((this.width === undefined) || (this.labelModule.differentState(selected, hover))) { this.iconSize = { width: Number(this.options.icon.size), height: Number(this.options.icon.size) @@ -20,13 +20,13 @@ class Icon extends NodeBase { } } - draw(ctx, x, y, selected, hover) { - this.resize(ctx); + draw(ctx, x, y, selected, hover, values) { + this.resize(ctx, selected, hover); this.options.icon.size = this.options.icon.size || 50; this.left = x - this.width / 2; this.top = y - this.height / 2; - this._icon(ctx, x, y, selected); + this._icon(ctx, x, y, selected, hover, values); if (this.options.label !== undefined) { var iconTextSpacing = 5; @@ -34,10 +34,10 @@ class Icon extends NodeBase { y + this.height / 2 + iconTextSpacing, selected); } - this.updateBoundingBox(x,y) + this.updateBoundingBox(x, y) } - updateBoundingBox(x,y) { + updateBoundingBox(x, y) { this.boundingBox.top = y - this.options.icon.size * 0.5; this.boundingBox.left = x - this.options.icon.size * 0.5; this.boundingBox.right = x + this.options.icon.size * 0.5; @@ -51,7 +51,7 @@ class Icon extends NodeBase { } } - _icon(ctx, x, y, selected) { + _icon(ctx, x, y, selected, hover, values) { let iconSize = Number(this.options.icon.size); if (this.options.icon.code !== undefined) { @@ -63,13 +63,12 @@ class Icon extends NodeBase { ctx.textBaseline = "middle"; // draw shadow if enabled - this.enableShadow(ctx); + this.enableShadow(ctx, values); ctx.fillText(this.options.icon.code, x, y); // disable shadows for other elements. - this.disableShadow(ctx); - } - else { + this.disableShadow(ctx, values); + } else { console.error('When using the icon shape, you need to define the code in the icon options object. This can be done per node or globally.') } diff --git a/lib/network/modules/components/nodes/shapes/Image.js b/lib/network/modules/components/nodes/shapes/Image.js index 1a0e8aec..becbf4ab 100644 --- a/lib/network/modules/components/nodes/shapes/Image.js +++ b/lib/network/modules/components/nodes/shapes/Image.js @@ -12,7 +12,7 @@ class Image extends CircleImageBase { this._resizeImage(); } - draw(ctx, x, y, selected, hover) { + draw(ctx, x, y, selected, hover, values) { this.resize(); this.left = x - this.width / 2; this.top = y - this.height / 2; @@ -53,9 +53,9 @@ class Image extends CircleImageBase { ctx.closePath(); } - this._drawImageAtPosition(ctx); + this._drawImageAtPosition(ctx, values); - this._drawImageLabel(ctx, x, y, selected || hover); + this._drawImageLabel(ctx, x, y, selected, hover); this.updateBoundingBox(x,y); } @@ -82,4 +82,4 @@ class Image extends CircleImageBase { } } -export default Image; \ No newline at end of file +export default Image; diff --git a/lib/network/modules/components/nodes/shapes/Square.js b/lib/network/modules/components/nodes/shapes/Square.js index e2fc6ebb..b7b7e8bf 100644 --- a/lib/network/modules/components/nodes/shapes/Square.js +++ b/lib/network/modules/components/nodes/shapes/Square.js @@ -11,8 +11,8 @@ class Square extends ShapeBase { this._resizeShape(); } - draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'square', 2, x, y, selected, hover); + draw(ctx, x, y, selected, hover, values) { + this._drawShape(ctx, 'square', 2, x, y, selected, hover, values); } distanceToBorder(ctx, angle) { @@ -20,4 +20,4 @@ class Square extends ShapeBase { } } -export default Square; \ No newline at end of file +export default Square; diff --git a/lib/network/modules/components/nodes/shapes/Star.js b/lib/network/modules/components/nodes/shapes/Star.js index 4aae4fae..490de5ac 100644 --- a/lib/network/modules/components/nodes/shapes/Star.js +++ b/lib/network/modules/components/nodes/shapes/Star.js @@ -7,12 +7,12 @@ class Star extends ShapeBase { super(options, body, labelModule) } - resize(ctx) { - this._resizeShape(); + resize(ctx, values, selected, hover) { + this._resizeShape(selected, hover, values); } - draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'star', 4, x, y, selected, hover); + draw(ctx, x, y, selected, hover, values) { + this._drawShape(ctx, 'star', 4, x, y, selected, hover, values); } distanceToBorder(ctx, angle) { @@ -20,4 +20,4 @@ class Star extends ShapeBase { } } -export default Star; \ No newline at end of file +export default Star; diff --git a/lib/network/modules/components/nodes/shapes/Text.js b/lib/network/modules/components/nodes/shapes/Text.js index 7d32c70b..e1b7a44f 100644 --- a/lib/network/modules/components/nodes/shapes/Text.js +++ b/lib/network/modules/components/nodes/shapes/Text.js @@ -8,33 +8,33 @@ class Text extends NodeBase { this._setMargins(labelModule); } - resize(ctx, selected) { - if (this.width === undefined) { - this.textSize = this.labelModule.getTextSize(ctx,selected); + resize(ctx, selected, hover) { + if ((this.width === undefined) || this.labelModule.differentState(selected, hover)) { + this.textSize = this.labelModule.getTextSize(ctx, selected, hover); this.width = this.textSize.width + this.margin.right + this.margin.left; this.height = this.textSize.height + this.margin.top + this.margin.bottom; this.radius = 0.5*this.width; } } - draw(ctx, x, y, selected, hover) { - this.resize(ctx, selected || hover); + draw(ctx, x, y, selected, hover, values) { + this.resize(ctx, selected, hover); this.left = x - this.width / 2; this.top = y - this.height / 2; // draw shadow if enabled - this.enableShadow(ctx); + this.enableShadow(ctx, values); this.labelModule.draw(ctx, this.left + this.textSize.width / 2 + this.margin.left, - this.top + this.textSize.height / 2 + this.margin.top, selected || hover); + this.top + this.textSize.height / 2 + this.margin.top, selected, hover); // disable shadows for other elements. - this.disableShadow(ctx); + this.disableShadow(ctx, values); - this.updateBoundingBox(x, y, ctx, selected); + this.updateBoundingBox(x, y, ctx, selected, hover); } - updateBoundingBox(x, y, ctx, selected) { - this.resize(ctx, selected); + updateBoundingBox(x, y, ctx, selected, hover) { + this.resize(ctx, selected, hover); this.left = x - this.width / 2; this.top = y - this.height / 2; diff --git a/lib/network/modules/components/nodes/shapes/Triangle.js b/lib/network/modules/components/nodes/shapes/Triangle.js index 6c58a4f1..2a75476c 100644 --- a/lib/network/modules/components/nodes/shapes/Triangle.js +++ b/lib/network/modules/components/nodes/shapes/Triangle.js @@ -11,8 +11,8 @@ class Triangle extends ShapeBase { this._resizeShape(); } - draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'triangle', 3, x, y, selected, hover); + draw(ctx, x, y, selected, hover, values) { + this._drawShape(ctx, 'triangle', 3, x, y, selected, hover, values); } distanceToBorder(ctx, angle) { @@ -20,4 +20,4 @@ class Triangle extends ShapeBase { } } -export default Triangle; \ No newline at end of file +export default Triangle; diff --git a/lib/network/modules/components/nodes/shapes/TriangleDown.js b/lib/network/modules/components/nodes/shapes/TriangleDown.js index 044fd7c5..a566a103 100644 --- a/lib/network/modules/components/nodes/shapes/TriangleDown.js +++ b/lib/network/modules/components/nodes/shapes/TriangleDown.js @@ -11,8 +11,8 @@ class TriangleDown extends ShapeBase { this._resizeShape(); } - draw(ctx, x, y, selected, hover) { - this._drawShape(ctx, 'triangleDown', 3, x, y, selected, hover); + draw(ctx, x, y, selected, hover, values) { + this._drawShape(ctx, 'triangleDown', 3, x, y, selected, hover, values); } distanceToBorder(ctx, angle) { @@ -20,4 +20,4 @@ class TriangleDown extends ShapeBase { } } -export default TriangleDown; \ No newline at end of file +export default TriangleDown; diff --git a/lib/network/modules/components/nodes/util/CircleImageBase.js b/lib/network/modules/components/nodes/util/CircleImageBase.js index 0bf68a82..7e5c38db 100644 --- a/lib/network/modules/components/nodes/util/CircleImageBase.js +++ b/lib/network/modules/components/nodes/util/CircleImageBase.js @@ -65,43 +65,41 @@ class CircleImageBase extends NodeBase { } - _drawRawCircle(ctx, x, y, selected, hover, size) { - var neutralborderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - var borderWidth = (selected ? selectionLineWidth : neutralborderWidth) / this.body.view.scale; + _drawRawCircle(ctx, x, y, selected, hover, values) { + var borderWidth = values.borderWidth / this.body.view.scale; ctx.lineWidth = Math.min(this.width, borderWidth); - ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; - ctx.circle(x, y, size); + ctx.strokeStyle = values.borderColor; + ctx.fillStyle = values.color; + ctx.circle(x, y, values.size); // draw shadow if enabled - this.enableShadow(ctx); + this.enableShadow(ctx, values); // draw the background ctx.fill(); // disable shadows for other elements. - this.disableShadow(ctx); + this.disableShadow(ctx, values); //draw dashed border if enabled, save and restore is required for firefox not to crash on unix. ctx.save(); // if borders are zero width, they will be drawn with width 1 by default. This prevents that if (borderWidth > 0) { - this.enableBorderDashes(ctx); + this.enableBorderDashes(ctx, values); //draw the border ctx.stroke(); //disable dashed border for other elements - this.disableBorderDashes(ctx); + this.disableBorderDashes(ctx, values); } ctx.restore(); } - _drawImageAtPosition(ctx) { + _drawImageAtPosition(ctx, values) { if (this.imageObj.width != 0) { // draw the image ctx.globalAlpha = 1.0; // draw shadow if enabled - this.enableShadow(ctx); + this.enableShadow(ctx, values); let factor = (this.imageObj.width / this.width) / this.body.view.scale; if (factor > 2 && this.options.shapeProperties.interpolation === true) { @@ -136,17 +134,17 @@ class CircleImageBase extends NodeBase { // disable shadows for other elements. - this.disableShadow(ctx); + this.disableShadow(ctx, values); } } - _drawImageLabel(ctx, x, y, selected) { + _drawImageLabel(ctx, x, y, selected, hover) { var yLabel; var offset = 0; if (this.height !== undefined) { offset = this.height * 0.5; - var labelDimensions = this.labelModule.getTextSize(ctx); + var labelDimensions = this.labelModule.getTextSize(ctx, selected, hover); if (labelDimensions.lineCount >= 1) { offset += labelDimensions.height / 2; } @@ -157,8 +155,8 @@ class CircleImageBase extends NodeBase { if (this.options.label) { this.labelOffset = offset; } - this.labelModule.draw(ctx, x, yLabel, selected, 'hanging'); + this.labelModule.draw(ctx, x, yLabel, selected, hover, 'hanging'); } } -export default CircleImageBase; \ No newline at end of file +export default CircleImageBase; diff --git a/lib/network/modules/components/nodes/util/NodeBase.js b/lib/network/modules/components/nodes/util/NodeBase.js index 9d60fe4f..d6a6e693 100644 --- a/lib/network/modules/components/nodes/util/NodeBase.js +++ b/lib/network/modules/components/nodes/util/NodeBase.js @@ -42,17 +42,17 @@ class NodeBase { Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; } - enableShadow(ctx) { - if (this.options.shadow.enabled === true) { - ctx.shadowColor = this.options.shadow.color; - ctx.shadowBlur = this.options.shadow.size; - ctx.shadowOffsetX = this.options.shadow.x; - ctx.shadowOffsetY = this.options.shadow.y; + enableShadow(ctx, values) { + if (values.shadow) { + ctx.shadowColor = values.shadowColor; + ctx.shadowBlur = values.shadowSize; + ctx.shadowOffsetX = values.shadowX; + ctx.shadowOffsetY = values.shadowY; } } - disableShadow(ctx) { - if (this.options.shadow.enabled === true) { + disableShadow(ctx, values) { + if (values.shadow) { ctx.shadowColor = 'rgba(0,0,0,0)'; ctx.shadowBlur = 0; ctx.shadowOffsetX = 0; @@ -60,10 +60,10 @@ class NodeBase { } } - enableBorderDashes(ctx) { - if (this.options.shapeProperties.borderDashes !== false) { + enableBorderDashes(ctx, values) { + if (values.borderDashes !== false) { if (ctx.setLineDash !== undefined) { - let dashes = this.options.shapeProperties.borderDashes; + let dashes = values.borderDashes; if (dashes === true) { dashes = [5,15] } @@ -72,18 +72,20 @@ class NodeBase { else { console.warn("setLineDash is not supported in this browser. The dashed borders cannot be used."); this.options.shapeProperties.borderDashes = false; + values.borderDashes = false; } } } - disableBorderDashes(ctx) { - if (this.options.shapeProperties.borderDashes !== false) { + disableBorderDashes(ctx, values) { + if (values.borderDashes !== false) { if (ctx.setLineDash !== undefined) { ctx.setLineDash([0]); } else { console.warn("setLineDash is not supported in this browser. The dashed borders cannot be used."); this.options.shapeProperties.borderDashes = false; + values.borderDashes = false; } } } diff --git a/lib/network/modules/components/nodes/util/ShapeBase.js b/lib/network/modules/components/nodes/util/ShapeBase.js index d9e2cce2..e430e74c 100644 --- a/lib/network/modules/components/nodes/util/ShapeBase.js +++ b/lib/network/modules/components/nodes/util/ShapeBase.js @@ -5,52 +5,50 @@ class ShapeBase extends NodeBase { super(options, body, labelModule) } - _resizeShape() { - if (this.width === undefined) { - var size = 2 * this.options.size; + _resizeShape(selected = this.selected, hover = this.hover, values = { size: this.options.size }) { + if ((this.width === undefined) || (this.labelModule.differentState(selected, hover))) { + var size = 2 * values.size; this.width = size; this.height = size; this.radius = 0.5*this.width; } } - _drawShape(ctx, shape, sizeMultiplier, x, y, selected, hover) { - this._resizeShape(); + _drawShape(ctx, shape, sizeMultiplier, x, y, selected, hover, values) { + this._resizeShape(selected, hover, values); this.left = x - this.width / 2; this.top = y - this.height / 2; - var neutralborderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - var borderWidth = (selected ? selectionLineWidth : neutralborderWidth) / this.body.view.scale; + var borderWidth = values.borderWidth / this.body.view.scale; ctx.lineWidth = Math.min(this.width, borderWidth); - ctx.strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; - ctx.fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; - ctx[shape](x, y, this.options.size); + ctx.strokeStyle = values.borderColor; + ctx.fillStyle = values.color; + ctx[shape](x, y, values.size); // draw shadow if enabled - this.enableShadow(ctx); + this.enableShadow(ctx, values); // draw the background ctx.fill(); // disable shadows for other elements. - this.disableShadow(ctx); + this.disableShadow(ctx, values); //draw dashed border if enabled, save and restore is required for firefox not to crash on unix. ctx.save(); // if borders are zero width, they will be drawn with width 1 by default. This prevents that if (borderWidth > 0) { - this.enableBorderDashes(ctx); + this.enableBorderDashes(ctx, values); //draw the border ctx.stroke(); //disable dashed border for other elements - this.disableBorderDashes(ctx); + this.disableBorderDashes(ctx, values); } ctx.restore(); if (this.options.label !== undefined) { let yLabel = y + 0.5 * this.height + 3; // the + 3 is to offset it a bit below the node. - this.labelModule.draw(ctx, x, yLabel, selected, 'hanging'); + this.labelModule.draw(ctx, x, yLabel, selected, hover, 'hanging'); } this.updateBoundingBox(x,y); @@ -73,4 +71,4 @@ class ShapeBase extends NodeBase { } -export default ShapeBase; \ No newline at end of file +export default ShapeBase; diff --git a/lib/network/modules/components/shared/Label.js b/lib/network/modules/components/shared/Label.js index cc7dcb13..1601e7a3 100644 --- a/lib/network/modules/components/shared/Label.js +++ b/lib/network/modules/components/shared/Label.js @@ -95,6 +95,23 @@ class Label { } } + // set the selected functions based on 'nearest' value + choosify(elementOptions, options, defaultOptions) { + this.fontOptions.chooser = true; + + let pile = [options, elementOptions, defaultOptions]; + + let chosen = util.topMost(pile, 'chosen'); + if (typeof chosen === 'boolean') { + this.fontOptions.chooser = chosen; + } else if (typeof chosen === 'object') { + let chosenLabel = util.topMost(pile, ['chosen', 'label']); + if ((typeof chosenLabel === 'boolean') || (typeof chosenLabel === 'function')) { + this.fontOptions.chooser = chosenLabel; + } + } + } + // When margins are set in an element, adjust sizes is called to remove them // from the width/height constraints. This must be done prior to label sizing. adjustSizes(margins) { @@ -239,7 +256,7 @@ class Label { * @param selected * @param baseline */ - draw(ctx, x, y, selected, baseline = 'middle') { + draw(ctx, x, y, selected, hover, baseline = 'middle') { // if no label, return if (this.elementOptions.label === undefined) return; @@ -250,12 +267,12 @@ class Label { return; // update the size cache if required - this.calculateLabelSize(ctx, selected, x, y, baseline); + this.calculateLabelSize(ctx, selected, hover, x, y, baseline); // create the fontfill background this._drawBackground(ctx); // draw text - this._drawText(ctx, selected, x, y, baseline); + this._drawText(ctx, selected, hover, x, y, baseline); } /** @@ -298,7 +315,7 @@ class Label { * @param baseline * @private */ - _drawText(ctx, selected, x, y, baseline = 'middle') { + _drawText(ctx, selected, hover, x, y, baseline = 'middle') { let fontSize = this.fontOptions.size; let viewFontSize = fontSize * this.body.view.scale; // this ensures that there will not be HUGE letters on screen by setting an upper limit on the visible text size (regardless of zoomLevel) @@ -332,14 +349,17 @@ class Label { for (let j = 0; j < this.lines[i].blocks.length; j++) { let block = this.lines[i].blocks[j]; ctx.font = block.font; - let [fontColor, strokeColor] = this._getColor(block.color, viewFontSize); - if (this.fontOptions.strokeWidth > 0) { - ctx.lineWidth = this.fontOptions.strokeWidth; + let [fontColor, strokeColor] = this._getColor(block.color, viewFontSize, block.strokeColor); + if (block.strokeWidth > 0) { + ctx.lineWidth = block.strokeWidth; ctx.strokeStyle = strokeColor; ctx.lineJoin = 'round'; - ctx.strokeText(block.text, x + width, yLine + block.vadjust); } ctx.fillStyle = fontColor; + + if (block.strokeWidth > 0) { + ctx.strokeText(block.text, x + width, yLine + block.vadjust); + } ctx.fillText(block.text, x + width, yLine + block.vadjust); width += block.width; } @@ -382,9 +402,9 @@ class Label { * @returns {*[]} * @private */ - _getColor(color, viewFontSize) { + _getColor(color, viewFontSize, initialStrokeColor) { let fontColor = color || '#000000'; - let strokeColor = this.fontOptions.strokeColor || '#ffffff'; + let strokeColor = initialStrokeColor || '#ffffff'; if (viewFontSize <= this.elementOptions.scaling.label.drawThreshold) { let opacity = Math.max(0, Math.min(1, 1 - (this.elementOptions.scaling.label.drawThreshold - viewFontSize))); fontColor = util.overrideOpacity(fontColor, opacity); @@ -400,8 +420,8 @@ class Label { * @param selected * @returns {{width: number, height: number}} */ - getTextSize(ctx, selected = false) { - this._processLabel(ctx, selected); + getTextSize(ctx, selected = false, hover = false) { + this._processLabel(ctx, selected, hover); return { width: this.size.width, height: this.size.height, @@ -418,9 +438,9 @@ class Label { * @param y * @param baseline */ - calculateLabelSize(ctx, selected, x = 0, y = 0, baseline = 'middle') { + calculateLabelSize(ctx, selected, hover, x = 0, y = 0, baseline = 'middle') { if (this.labelDirty === true) { - this._processLabel(ctx, selected); + this._processLabel(ctx, selected, hover); } this.size.left = x - this.size.width * 0.5; this.size.top = y - this.size.height * 0.5; @@ -696,29 +716,37 @@ class Label { return blocks; } - setFont(ctx, selected, mod) { - let height - let vadjust - let color - if (mod === 'normal') { - ctx.font = (selected && this.elementOptions.labelHighlightBold ? 'bold ' : '') + - this.fontOptions.size + "px " + this.fontOptions.face; - color = this.fontOptions.color; - height = this.fontOptions.size; - vadjust = this.fontOptions.vadjust; + getFormattingValues(ctx, selected, hover, mod) { + let values = { + color: (mod === "normal") ? this.fontOptions.color : this.fontOptions[mod].color, + size: (mod === "normal") ? this.fontOptions.size : this.fontOptions[mod].size, + face: (mod === "normal") ? this.fontOptions.face : this.fontOptions[mod].face, + mod: (mod === "normal") ? "" : this.fontOptions[mod].mod, + vadjust: (mod === "normal") ? this.fontOptions.vadjust : this.fontOptions[mod].vadjust, + strokeWidth: this.fontOptions.strokeWidth, + strokeColor: this.fontOptions.strokeColor + }; + if (mod === "normal") { + if (selected || hover) { + if ((this.fontOptions.chooser === true) && (this.elementOptions.labelHighlightBold)) { + values.mod = 'bold'; + } else if (typeof this.fontOptions.chooser === 'function') { + this.fontOptions.chooser(ctx, values, this.elementOptions.id, selected, hover); + } + } } else { - ctx.font = this.fontOptions[mod].mod + " " + - this.fontOptions[mod].size + "px " + this.fontOptions[mod].face; - color = this.fontOptions[mod].color; - height = this.fontOptions[mod].size; - vadjust = this.fontOptions[mod].vadjust || 0; - } - return { - font: ctx.font.replace(/"/g, ""), - color: color, - height: height, - vadjust: vadjust + if ((selected || hover) && (typeof this.fontOptions.chooser === 'function')) { + this.fontOptions.chooser(ctx, values, this.elementOptions.id, selected, hover); + } } + ctx.font = (values.mod + " " + values.size + "px " + values.face).replace(/"/g, ""); + values.font = ctx.font; + values.height = values.size; + return values; + } + + differentState(selected, hover) { + return ((selected !== this.fontOptions.selectedState) && (hover !== this.fontOptions.hoverState)); } /** @@ -727,24 +755,24 @@ class Label { * @param selected * @private */ - _processLabel(ctx, selected) { + _processLabel(ctx, selected, hover) { let width = 0; let height = 0; let nlLines = []; let lines = []; let k = 0; - lines.add = function(l, text, font, color, width, height, vadjust) { + lines.add = function(l, text, font, color, width, height, vadjust, mod, strokeWidth, strokeColor) { if (this.length == l) { this[l] = { width: 0, height: 0, blocks: [] }; } - this[l].blocks.push({ text, font, color, width, height, vadjust }); + this[l].blocks.push({ text, font, color, width, height, vadjust, mod, strokeWidth, strokeColor }); } lines.accumulate = function(l, width, height) { this[l].width += width; this[l].height = height > this[l].height ? height : this[l].height; } - lines.addAndAccumulate = function(l, text, font, color, width, height, vadjust) { - this.add(l, text, font, color, width, height, vadjust); + lines.addAndAccumulate = function(l, text, font, color, width, height, vadjust, mod, strokeWidth, strokeColor) { + this.add(l, text, font, color, width, height, vadjust, mod, strokeWidth, strokeColor); this.accumulate(l, width, height); } if (this.elementOptions.label !== undefined) { @@ -757,15 +785,15 @@ class Label { let lineHeight = 0; if (blocks) { if (blocks.length == 0) { - this.setFont(ctx, selected, "normal"); - lines.addAndAccumulate(k, "", ctx.font, "#000000", 0, this.fontOptions.size, this.fontOptions.vadjust); + let values = this.getFormattingValues(ctx, selected, hover, "normal"); + lines.addAndAccumulate(k, "", values.font, values.color, 0, values.size, values.vadjust, "normal", values.strokeWidth, values.strokeColor); height += lines[k].height; k++; continue; } for (let j = 0; j < blocks.length; j++) { if (this.fontOptions.maxWdt > 0) { - let metrics = this.setFont(ctx, selected, blocks[j].mod); + let values = this.getFormattingValues(ctx, selected, hover, blocks[j].mod); let words = blocks[j].text.split(" "); let atStart = true let text = ""; @@ -777,8 +805,8 @@ class Label { lastMeasure = measure; measure = ctx.measureText(text + pre + words[w]); if (lineWidth + measure.width > this.fontOptions.maxWdt) { - lineHeight = (metrics.height > lineHeight) ? metrics.height : lineHeight; - lines.add(k, text, ctx.font, metrics.color, lastMeasure.width, metrics.height, metrics.vadjust); + lineHeight = (values.height > lineHeight) ? values.height : lineHeight; + lines.add(k, text, values.font, values.color, lastMeasure.width, values.height, values.vadjust, blocks[j].mod, values.strokeWidth, values.strokeColor); lines.accumulate(k, lastMeasure.width, lineHeight); text = ""; atStart = true; @@ -789,9 +817,9 @@ class Label { } else { text = text + pre + words[w]; if (w === words.length-1) { - lineHeight = (metrics.height > lineHeight) ? metrics.height : lineHeight; + lineHeight = (values.height > lineHeight) ? values.height : lineHeight; lineWidth += measure.width; - lines.add(k, text, ctx.font, metrics.color, measure.width, metrics.height, metrics.vadjust); + lines.add(k, text, values.font, values.color, measure.width, values.height, values.vadjust, blocks[j].mod, values.strokeWidth, values.strokeColor); lines.accumulate(k, measure.width, lineHeight); if (j === blocks.length-1) { width = lines[k].width > width ? lines[k].width : width; @@ -804,9 +832,9 @@ class Label { } } } else { - let metrics = this.setFont(ctx, selected, blocks[j].mod) + let values = this.getFormattingValues(ctx, selected, hover, blocks[j].mod); let measure = ctx.measureText(blocks[j].text); - lines.addAndAccumulate(k, blocks[j].text, ctx.font, metrics.color, measure.width, metrics.height, metrics.vadjust); + lines.addAndAccumulate(k, blocks[j].text, values.font, values.color, measure.width, values.height, values.vadjust, blocks[j].mod, values.strokeWidth, values.strokeColor); width = lines[k].width > width ? lines[k].width : width; if (blocks.length-1 === j) { height += lines[k].height; @@ -818,7 +846,7 @@ class Label { } } else { for (let i = 0; i < lineCount; i++) { - ctx.font = (selected && this.elementOptions.labelHighlightBold ? 'bold ' : '') + this.fontOptions.size + "px " + this.fontOptions.face; + let values = this.getFormattingValues(ctx, selected, hover, "normal"); if (this.fontOptions.maxWdt > 0) { let words = nlLines[i].split(" "); let text = ""; @@ -830,7 +858,7 @@ class Label { lastMeasure = measure; measure = ctx.measureText(text + pre + words[w]); if (measure.width > this.fontOptions.maxWdt) { - lines.addAndAccumulate(k, text, ctx.font, this.fontOptions.color, lastMeasure.width, this.fontOptions.size, this.fontOptions.vadjust) + lines.addAndAccumulate(k, text, values.font, values.color, lastMeasure.width, values.size, values.vadjust, "normal", values.strokeWidth, values.strokeColor) width = lines[k].width > width ? lines[k].width : width; height += lines[k].height; text = ""; @@ -838,7 +866,7 @@ class Label { } else { text = text + pre + words[w]; if (w === words.length-1) { - lines.addAndAccumulate(k, text, ctx.font, this.fontOptions.color, measure.width, this.fontOptions.size, this.fontOptions.vadjust) + lines.addAndAccumulate(k, text, values.font, values.color, measure.width, values.size, values.vadjust, "normal", values.strokeWidth, values.strokeColor) width = lines[k].width > width ? lines[k].width : width; height += lines[k].height; k++; @@ -849,7 +877,7 @@ class Label { } else { let text = nlLines[i]; let measure = ctx.measureText(text); - lines.addAndAccumulate(k, text, ctx.font, this.fontOptions.color, measure.width, this.fontOptions.size, this.fontOptions.vadjust); + lines.addAndAccumulate(k, text, values.font, values.color, measure.width, values.size, values.vadjust, "normal", values.strokeWidth, values.strokeColor); width = lines[k].width > width ? lines[k].width : width; height += lines[k].height; k++; @@ -868,6 +896,8 @@ class Label { this.lineCount = lines.length; this.size.width = width; this.size.height = height; + this.selectedState = selected; + this.hoverState = hover; } } diff --git a/lib/network/options.js b/lib/network/options.js index ecc7bf32..921c819a 100644 --- a/lib/network/options.js +++ b/lib/network/options.js @@ -30,6 +30,11 @@ let allOptions = { __type__: { string: ['from', 'to', 'middle'], object } }, arrowStrikethrough: { boolean: bool }, + chosen: { + label: { boolean: bool, 'function': 'function' }, + edge: { boolean: bool, 'function': 'function' }, + __type__: { object, boolean: bool } + }, color: { color: { string }, highlight: { string }, @@ -191,6 +196,11 @@ let allOptions = { borderWidth: { number }, borderWidthSelected: { number, 'undefined': 'undefined' }, brokenImage: { string, 'undefined': 'undefined' }, + chosen: { + label: { boolean: bool, 'function': 'function' }, + node: { boolean: bool, 'function': 'function' }, + __type__: { object, boolean: bool } + }, color: { border: { string }, background: { string }, diff --git a/lib/util.js b/lib/util.js index 21997f65..31ca9bb7 100644 --- a/lib/util.js +++ b/lib/util.js @@ -1514,7 +1514,7 @@ exports.topMost = function (pile, accessors) { continue; } } - if (candidate) { + if (typeof candidate != 'undefined') { break; } }