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
true
+ 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: +
+Property | Edge Reference |
---|---|
dashes | see dashes |
toArrow | see arrows.to.enabled |
toArrowScale | see arrows.to.scaleFactor |
toArrowType | see arrows.to.type |
middleArrow | see arrows.middle.enabled |
middleArrowScale | see arrows.middle.scaleFactor |
middleArrowType | see arrows.middle.type |
fromArrow | see arrows.from.enabled |
fromArrowScale | see arrows.from.scaleFactor |
fromArrowType | see arrows.from.type |
arrowStrikethrough | see arrowStrikethrough |
color | see color.color |
inheritsColor | see color.inherit |
opacity | see color.opacity |
hidden | see hidden |
length | see length |
shadow | see shadow.enabled |
shadowColor | see shadow.color |
shadowSize | see shadow.size |
shadowX | see shadow.x |
shadowY | see shadow.y |
width | see width |
+ 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: +
+Property | Edge Reference |
---|---|
color | see font.color |
size | see font.size |
face | see font.face |
mod | font modifier ('bold', 'italic', etc.) |
vadjust | see font.vadjust |
strokeWidth | see font.strokeWidth |
strokeColor | see font.strokeColor |
'#848484'
true
+ 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: +
+Property | Node Reference |
---|---|
color | see color.background |
borderWidth | see borderWidth |
borderColor | see color.border |
size | see size |
borderDashes | see shapeProperties.borderDashes |
borderRadius | see shapeProperties.borderRadius |
shadow | see shadow.enabled |
shadowColor | see shadow.color |
shadowSize | see shadow.size |
shadowX | see shadow.x |
shadowY | see shadow.y |
+ 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: +
+Property | Node Reference |
---|---|
color | see font.color |
size | see font.size |
face | see font.face |
mod | font modifier ('bold', 'italic', etc.) |
vadjust | see font.vadjust |
strokeWidth | see font.strokeWidth |
strokeColor | see font.strokeColor |
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; } }