diff --git a/examples/network/exampleApplications/nodeLegend.html b/examples/network/exampleApplications/nodeLegend.html index f1e8445f84..ae2a826658 100644 --- a/examples/network/exampleApplications/nodeLegend.html +++ b/examples/network/exampleApplications/nodeLegend.html @@ -96,12 +96,12 @@ var mynetwork = document.getElementById('mynetwork'); var x = - mynetwork.clientWidth / 2 + 50; var y = - mynetwork.clientHeight / 2 + 50; - var step = 70; - nodes.push({id: 1000, x: x, y: y, label: 'Internet', group: 'internet', value: 1, fixed: true, physics:false}); - nodes.push({id: 1001, x: x, y: y + step, label: 'Switch', group: 'switch', value: 1, fixed: true, physics:false}); - nodes.push({id: 1002, x: x, y: y + 2 * step, label: 'Server', group: 'server', value: 1, fixed: true, physics:false}); - nodes.push({id: 1003, x: x, y: y + 3 * step, label: 'Computer', group: 'desktop', value: 1, fixed: true, physics:false}); - nodes.push({id: 1004, x: x, y: y + 4 * step, label: 'Smartphone', group: 'mobile', value: 1, fixed: true, physics:false}); + var step = 50; + nodes.push({id: 1000, x: x, y: y, label: 'Internet', group: 'internet', value: 1, labelPosition:'right', fixed: true, physics:false}); + nodes.push({id: 1001, x: x, y: y + step, label: 'Switch', group: 'switch', value: 1, labelPosition:'right', fixed: true, physics:false}); + nodes.push({id: 1002, x: x, y: y + 2 * step, label: 'Server', group: 'server', value: 1, labelPosition:'right', fixed: true, physics:false}); + nodes.push({id: 1003, x: x, y: y + 3 * step, label: 'Computer', group: 'desktop', value: 1, labelPosition:'right', fixed: true, physics:false}); + nodes.push({id: 1004, x: x, y: y + 4 * step, label: 'Smartphone', group: 'mobile', value: 1, labelPosition:'right', fixed: true, physics:false}); // create a network var container = document.getElementById('mynetwork'); @@ -159,4 +159,4 @@ - + \ No newline at end of file diff --git a/examples/network/labels/labelAlignment.html b/examples/network/labels/labelAlignment.html index 92dd888c36..b48a9d0748 100644 --- a/examples/network/labels/labelAlignment.html +++ b/examples/network/labels/labelAlignment.html @@ -20,10 +20,9 @@ -

Labels of edges can be aligned to edges in various ways.

-

Text-alignment within node labels can be 'left' or 'center', other font alignments not implemented.

-

Label alignment (placement of label "box") for nodes (top, bottom, left, right, inside) is -planned but not in vis yet.

+

Labels of edges can be aligned to edges either: horizontal, middle, top, or bottom.

+

Text-alignment within node labels can be aligned: center, left, or right.

+

For dot-like nodes, labels can be positioned at: bottom, inside, top, left, or right.

The click event is captured and displayed to illustrate how the clicking on labels works. You can drag the nodes over each other to see how this influences the click event values.

@@ -34,20 +33,28 @@ - + \ No newline at end of file diff --git a/lib/network/modules/components/Node.js b/lib/network/modules/components/Node.js index f1289944b9..e5c2c86739 100644 --- a/lib/network/modules/components/Node.js +++ b/lib/network/modules/components/Node.js @@ -511,7 +511,7 @@ class Node { /** - * check if this node is selecte + * check if this node is selected * @return {boolean} selected True if node is selected, else false */ isSelected() { @@ -528,16 +528,6 @@ class Node { } - /** - * Get the current dimensions of the label - * - * @return {rect} - */ - getLabelSize() { - return this.labelModule.size(); - } - - /** * Adjust the value range of the node. The node will adjust it's size * based on its value. diff --git a/lib/network/modules/components/nodes/util/ShapeBase.js b/lib/network/modules/components/nodes/util/ShapeBase.js index 881893f8b1..9110c15f79 100644 --- a/lib/network/modules/components/nodes/util/ShapeBase.js +++ b/lib/network/modules/components/nodes/util/ShapeBase.js @@ -66,33 +66,72 @@ class ShapeBase extends NodeBase { } } + if (this.options.label !== undefined) { // Need to call following here in order to ensure value for `this.labelModule.size.height` - this.labelModule.calculateLabelSize(ctx, selected, hover, x, y, 'hanging') - let yLabel = y + 0.5 * this.height + 0.5 * this.labelModule.size.height; - this.labelModule.draw(ctx, x, yLabel, selected, hover, 'hanging'); + this.labelModule.calculateLabelSize(ctx, selected, hover, x, y, 'hanging'); + let {labelX, labelY, labelBaseline} = this.positionLabel(x, y); + this.labelModule.draw(ctx, labelX, labelY, selected, hover, labelBaseline); } + this.updateBoundingBox(x,y); } + /** + * + * @param {number} x + * @param {number} y + * @returns {{number,number,string}} + */ + positionLabel(x, y){ + let labelX = x; + let labelY = y; + let labelBaseline = 'middle'; + const distance = this.labelModule.distance; + + switch(this.labelModule.position){ + case 'inside': + break; + case 'left': + this.labelModule.fontOptions.align = 'right'; + labelX -= (this.labelModule.size.width + this.width) * 0.5 + distance; // shift left + break; + case 'right': + this.labelModule.fontOptions.align = 'left'; + labelX += (this.labelModule.size.width + this.width) * 0.5 + distance; // shift right + break; + case 'top': + labelY -= (this.labelModule.size.height + this.height) * 0.5 + distance; // shift up to above node + break; + default: + labelBaseline = 'top'; + labelY += (this.labelModule.size.height + this.height + distance) * 0.5; // shift down to below node + } + + return { labelX, labelY, labelBaseline } + + } + /** * * @param {number} x * @param {number} y */ updateBoundingBox(x, y) { + // TODO: Same/similar code is at least 4 places!! 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; if (this.options.label !== undefined && this.labelModule.size.width > 0) { + this.boundingBox.top = Math.min(this.boundingBox.top, this.labelModule.size.top); this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.labelModule.size.top + this.labelModule.size.height); } } } -export default ShapeBase; +export default ShapeBase; \ No newline at end of file diff --git a/lib/network/modules/components/shared/Label.js b/lib/network/modules/components/shared/Label.js index 691cf32e49..557553dc8a 100644 --- a/lib/network/modules/components/shared/Label.js +++ b/lib/network/modules/components/shared/Label.js @@ -21,12 +21,14 @@ class Label { * @param {boolean} [edgelabel=false] */ constructor(body, options, edgelabel = false) { - this.body = body; + this.body = body; // suggestion: pass in just scale since we only use this.body.view.scale this.pointToSelf = false; this.baseSize = undefined; this.fontOptions = {}; // instance variable containing the *instance-local* font options this.setOptions(options); this.size = {top: 0, left: 0, width: 0, height: 0, yLine: 0}; + this.position = undefined; + this.distance = 12; this.isEdgeLabel = edgelabel; } @@ -37,8 +39,6 @@ class Label { setOptions(options) { this.elementOptions = options; // Reference to the options of the parent Node-instance - this.initFontOptions(options.font); - if (ComponentUtil.isValidLabel(options.label)) { this.labelDirty = true; } else { @@ -46,6 +46,8 @@ class Label { options.label = undefined } + this.initFontOptions(options.font); + if (options.font !== undefined && options.font !== null) { // font options can be deleted at various levels if (typeof options.font === 'string') { this.baseSize = this.fontOptions.size; @@ -58,6 +60,18 @@ class Label { } } } + + if(options.labelPosition !== undefined && options.labelPosition !== null) { + if(typeof options.labelPosition === 'string'){ + this.position = options.labelPosition; + } + } + + if(options.labelDistance !== undefined && options.labelDistance !== null) { + if(typeof options.labelDistance === 'number'){ + this.distance = options.labelDistance; + } + } } @@ -458,12 +472,12 @@ class Label { ctx.textAlign = 'left'; x = x - this.size.width / 2; // Shift label 1/2-distance to the left - if ((this.fontOptions.valign) && (this.size.height > this.size.labelHeight)) { + if ((this.fontOptions.valign) && (this.size.height > this.size.stateHeight)) { if (this.fontOptions.valign === 'top') { - y -= (this.size.height - this.size.labelHeight) / 2; + y -= (this.size.height - this.size.stateHeight) / 2; } if (this.fontOptions.valign === 'bottom') { - y += (this.size.height - this.size.labelHeight) / 2; + y += (this.size.height - this.size.stateHeight) / 2; } } @@ -509,8 +523,7 @@ class Label { * @private */ _setAlignment(ctx, x, y, baseline) { - // check for label alignment (for edges) - // TODO: make alignment for nodes + // check for edge label alignment if (this.isEdgeLabel && this.fontOptions.align !== 'horizontal' && this.pointToSelf === false) { x = 0; y = 0; @@ -627,9 +640,7 @@ class Label { this.size.top = y - this.size.height * 0.5; this.size.yLine = y + (1 - this.lineCount) * 0.5 * this.fontOptions.size; if (baseline === "hanging") { - this.size.top += 0.5 * this.fontOptions.size; - this.size.top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers - this.size.yLine += 4; // distance from node + this.size.yLine += 4; // used for edge position } } @@ -734,7 +745,7 @@ class Label { state.width = this.fontOptions.minWdt; } - this.size.labelHeight =state.height; + this.size.stateHeight = state.height; if ((this.fontOptions.minHgt > 0) && (state.height < this.fontOptions.minHgt)) { state.height = this.fontOptions.minHgt; } @@ -770,4 +781,4 @@ class Label { } } -export default Label; +export default Label; \ No newline at end of file