From 0e558bf7f5bcf992c341d2ccc50a811fc65ee85b Mon Sep 17 00:00:00 2001 From: Katya Malyavina Date: Wed, 22 Jan 2020 12:54:04 -0500 Subject: [PATCH 1/7] Label position initial draft --- examples/network/labels/labelAlignment.html | 33 ++++++++------ lib/network/modules/components/Node.js | 2 +- .../modules/components/shared/Label.js | 44 +++++++++++++++++-- 3 files changed, 62 insertions(+), 17 deletions(-) diff --git a/examples/network/labels/labelAlignment.html b/examples/network/labels/labelAlignment.html index 92dd888c36..3877802fc0 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 eeea637048..e5c2c86739 100644 --- a/lib/network/modules/components/Node.js +++ b/lib/network/modules/components/Node.js @@ -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..d701210a7b 100644 --- a/lib/network/modules/components/nodes/util/ShapeBase.js +++ b/lib/network/modules/components/nodes/util/ShapeBase.js @@ -68,9 +68,33 @@ 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 = 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 + } + + this.labelModule.draw(ctx, labelX, labelY, selected, hover, labelBaseline); } this.updateBoundingBox(x,y); @@ -82,17 +106,19 @@ class ShapeBase extends NodeBase { * @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.max(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 083dc3c885..9de1d1c34b 100644 --- a/lib/network/modules/components/shared/Label.js +++ b/lib/network/modules/components/shared/Label.js @@ -1,4 +1,3 @@ -/* eslint-disable no-prototype-builtins */ import * as util from 'vis-util'; import ComponentUtil from './ComponentUtil'; import LabelSplitter from './LabelSplitter'; @@ -29,6 +28,7 @@ class Label { this.setOptions(options); this.size = {top: 0, left: 0, width: 0, height: 0, yLine: 0}; this.position = undefined; + this.distance = 12; this.isEdgeLabel = edgelabel; } @@ -39,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 { @@ -48,11 +46,7 @@ class Label { options.label = undefined } - if(options.labelPosition !== undefined && options.labelPosition !== null) { - if(typeof options.labelPosition === 'string'){ - this.position = options.labelPosition; - } - } + this.initFontOptions(options.font); if (options.font !== undefined && options.font !== null) { // font options can be deleted at various levels if (typeof options.font === 'string') { @@ -66,6 +60,12 @@ class Label { } } } + + if(options.labelDistance !== undefined && options.labelDistance !== null) { + if(typeof options.labelDistance === 'number'){ + this.distance = options.labelDistance; + } + } } @@ -466,12 +466,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; } } @@ -535,39 +535,8 @@ class Label { ctx.textBaseline = 'middle'; } } - // check for node label alignment else { ctx.textBaseline = baseline; - - // this.size = {top, left, width, height, yLine}; - // what is yLine? - // how does it currently get y value to position the label below node? - // suggestion: alignment offsets to be based on center/origin of node rather bottom - // suggestion: would 'dimensions' be a better name than 'size'? - - let nodeSize = 30; - let distance = 5; - - switch(this.position){ - case 'inside': - y -= (this.size.height / 2) + nodeSize + distance; // shift up to middle of node - break; - case 'left': - this.fontOptions.align = 'right'; // set font alignment to right - y -= (this.size.height / 2) + nodeSize + distance; // shift up to middle of node - x -= (nodeSize + distance) * 2; // shift left - break; - case 'right': - this.fontOptions.align = 'left'; // set font alignment to left - y -= (this.size.height / 2) + nodeSize + distance; // shift up to middle of node - x += (nodeSize + distance) * 2; // shift right - break; - case 'top': - y -= this.size.height + (nodeSize + distance) * 2; // shift up to above node - break; - default: - // default: bottom - } } return [x,y]; } @@ -665,9 +634,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 } } @@ -772,7 +739,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; } @@ -808,4 +775,4 @@ class Label { } } -export default Label; +export default Label; \ No newline at end of file From 50162ad2c15f9567dfea210bb1fab0e5761b7d57 Mon Sep 17 00:00:00 2001 From: Katya Malyavina Date: Wed, 29 Jan 2020 13:47:35 -0500 Subject: [PATCH 4/7] Erased a little too much --- lib/network/modules/components/shared/Label.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/network/modules/components/shared/Label.js b/lib/network/modules/components/shared/Label.js index 9de1d1c34b..557553dc8a 100644 --- a/lib/network/modules/components/shared/Label.js +++ b/lib/network/modules/components/shared/Label.js @@ -61,6 +61,12 @@ 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; From 3df7a87fc620df1b507b1dfa4448c4cefb2224a9 Mon Sep 17 00:00:00 2001 From: Katya Malyavina Date: Wed, 29 Jan 2020 15:37:16 -0500 Subject: [PATCH 5/7] Change max to min for top bound --- lib/network/modules/components/nodes/util/ShapeBase.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/network/modules/components/nodes/util/ShapeBase.js b/lib/network/modules/components/nodes/util/ShapeBase.js index d701210a7b..787d3cbcb8 100644 --- a/lib/network/modules/components/nodes/util/ShapeBase.js +++ b/lib/network/modules/components/nodes/util/ShapeBase.js @@ -113,7 +113,7 @@ class ShapeBase extends NodeBase { this.boundingBox.bottom = y + this.options.size; if (this.options.label !== undefined && this.labelModule.size.width > 0) { - this.boundingBox.top = Math.max(this.boundingBox.top, this.labelModule.size.top); + 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.labelModule.size.top + this.labelModule.size.height); From 7e7dc58cf4e1ec2ca48c241391d8a3b4117ea7f4 Mon Sep 17 00:00:00 2001 From: Katya Malyavina Date: Fri, 7 Feb 2020 07:56:08 -0500 Subject: [PATCH 6/7] Split label position into its own function --- .../components/nodes/util/ShapeBase.js | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/network/modules/components/nodes/util/ShapeBase.js b/lib/network/modules/components/nodes/util/ShapeBase.js index 787d3cbcb8..97ae2820d7 100644 --- a/lib/network/modules/components/nodes/util/ShapeBase.js +++ b/lib/network/modules/components/nodes/util/ShapeBase.js @@ -66,15 +66,30 @@ 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'); + 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; @@ -94,10 +109,8 @@ class ShapeBase extends NodeBase { labelY += (this.labelModule.size.height + this.height + distance) * 0.5; // shift down to below node } - this.labelModule.draw(ctx, labelX, labelY, selected, hover, labelBaseline); - } - - this.updateBoundingBox(x,y); + return { labelX, labelY, labelBaseline } + } /** @@ -107,10 +120,10 @@ class ShapeBase extends NodeBase { */ 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; + this.boundingBox.top = y - this.height; + this.boundingBox.left = x - this.width; + this.boundingBox.right = x + this.width; + this.boundingBox.bottom = y + this.height; if (this.options.label !== undefined && this.labelModule.size.width > 0) { this.boundingBox.top = Math.min(this.boundingBox.top, this.labelModule.size.top); From 4973e734f519436b1ffc590aaa92fafb9983535e Mon Sep 17 00:00:00 2001 From: Katya Malyavina Date: Fri, 7 Feb 2020 08:15:47 -0500 Subject: [PATCH 7/7] options.size in updateBoundingBox --- lib/network/modules/components/nodes/util/ShapeBase.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/network/modules/components/nodes/util/ShapeBase.js b/lib/network/modules/components/nodes/util/ShapeBase.js index 97ae2820d7..9110c15f79 100644 --- a/lib/network/modules/components/nodes/util/ShapeBase.js +++ b/lib/network/modules/components/nodes/util/ShapeBase.js @@ -120,10 +120,10 @@ class ShapeBase extends NodeBase { */ updateBoundingBox(x, y) { // TODO: Same/similar code is at least 4 places!! - this.boundingBox.top = y - this.height; - this.boundingBox.left = x - this.width; - this.boundingBox.right = x + this.width; - this.boundingBox.bottom = y + this.height; + 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);