|
@ -6,21 +6,26 @@ class Label { |
|
|
|
|
|
|
|
|
this.pointToSelf = false; |
|
|
this.pointToSelf = false; |
|
|
this.baseSize = undefined; |
|
|
this.baseSize = undefined; |
|
|
|
|
|
this.fontOptions = {}; |
|
|
this.setOptions(options); |
|
|
this.setOptions(options); |
|
|
this.size = {top: 0, left: 0, width: 0, height: 0, yLine: 0}; // could be cached
|
|
|
this.size = {top: 0, left: 0, width: 0, height: 0, yLine: 0}; // could be cached
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
setOptions(options, allowDeletion = false) { |
|
|
setOptions(options, allowDeletion = false) { |
|
|
this.options = options; |
|
|
|
|
|
|
|
|
this.nodeOptions = options; |
|
|
|
|
|
|
|
|
|
|
|
// We want to keep the font options seperated from the node options.
|
|
|
|
|
|
// The node options have to mirror the globals when they are not overruled.
|
|
|
|
|
|
this.fontOptions = util.deepExtend({},options.font, true); |
|
|
|
|
|
|
|
|
if (options.label !== undefined) { |
|
|
if (options.label !== undefined) { |
|
|
this.labelDirty = true; |
|
|
this.labelDirty = true; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (options.font !== undefined) { |
|
|
if (options.font !== undefined) { |
|
|
Label.parseOptions(this.options.font, options, allowDeletion); |
|
|
|
|
|
|
|
|
Label.parseOptions(this.fontOptions, options, allowDeletion); |
|
|
if (typeof options.font === 'string') { |
|
|
if (typeof options.font === 'string') { |
|
|
this.baseSize = this.options.font.size; |
|
|
|
|
|
|
|
|
this.baseSize = this.fontOptions.size; |
|
|
} |
|
|
} |
|
|
else if (typeof options.font === 'object') { |
|
|
else if (typeof options.font === 'object') { |
|
|
if (options.font.size !== undefined) { |
|
|
if (options.font.size !== undefined) { |
|
@ -54,12 +59,12 @@ class Label { |
|
|
*/ |
|
|
*/ |
|
|
draw(ctx, x, y, selected, baseline = 'middle') { |
|
|
draw(ctx, x, y, selected, baseline = 'middle') { |
|
|
// if no label, return
|
|
|
// if no label, return
|
|
|
if (this.options.label === undefined) |
|
|
|
|
|
|
|
|
if (this.nodeOptions.label === undefined) |
|
|
return; |
|
|
return; |
|
|
|
|
|
|
|
|
// check if we have to render the label
|
|
|
// check if we have to render the label
|
|
|
let viewFontSize = this.options.font.size * this.body.view.scale; |
|
|
|
|
|
if (this.options.label && viewFontSize < this.options.scaling.label.drawThreshold - 1) |
|
|
|
|
|
|
|
|
let viewFontSize = this.fontOptions.size * this.body.view.scale; |
|
|
|
|
|
if (this.nodeOptions.label && viewFontSize < this.nodeOptions.scaling.label.drawThreshold - 1) |
|
|
return; |
|
|
return; |
|
|
|
|
|
|
|
|
// update the size cache if required
|
|
|
// update the size cache if required
|
|
@ -77,12 +82,12 @@ class Label { |
|
|
* @private |
|
|
* @private |
|
|
*/ |
|
|
*/ |
|
|
_drawBackground(ctx) { |
|
|
_drawBackground(ctx) { |
|
|
if (this.options.font.background !== undefined && this.options.font.background !== "none") { |
|
|
|
|
|
ctx.fillStyle = this.options.font.background; |
|
|
|
|
|
|
|
|
if (this.fontOptions.background !== undefined && this.fontOptions.background !== "none") { |
|
|
|
|
|
ctx.fillStyle = this.fontOptions.background; |
|
|
|
|
|
|
|
|
let lineMargin = 2; |
|
|
let lineMargin = 2; |
|
|
|
|
|
|
|
|
switch (this.options.font.align) { |
|
|
|
|
|
|
|
|
switch (this.fontOptions.align) { |
|
|
case 'middle': |
|
|
case 'middle': |
|
|
ctx.fillRect(-this.size.width * 0.5, -this.size.height * 0.5, this.size.width, this.size.height); |
|
|
ctx.fillRect(-this.size.width * 0.5, -this.size.height * 0.5, this.size.width, this.size.height); |
|
|
break; |
|
|
break; |
|
@ -108,11 +113,11 @@ class Label { |
|
|
* @private |
|
|
* @private |
|
|
*/ |
|
|
*/ |
|
|
_drawText(ctx, selected, x, y, baseline = 'middle') { |
|
|
_drawText(ctx, selected, x, y, baseline = 'middle') { |
|
|
let fontSize = this.options.font.size; |
|
|
|
|
|
|
|
|
let fontSize = this.fontOptions.size; |
|
|
let viewFontSize = fontSize * this.body.view.scale; |
|
|
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)
|
|
|
// this ensures that there will not be HUGE letters on screen by setting an upper limit on the visible text size (regardless of zoomLevel)
|
|
|
if (viewFontSize >= this.options.scaling.label.maxVisible) { |
|
|
|
|
|
fontSize = Number(this.options.scaling.label.maxVisible) / this.body.view.scale; |
|
|
|
|
|
|
|
|
if (viewFontSize >= this.nodeOptions.scaling.label.maxVisible) { |
|
|
|
|
|
fontSize = Number(this.nodeOptions.scaling.label.maxVisible) / this.body.view.scale; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
let yLine = this.size.yLine; |
|
|
let yLine = this.size.yLine; |
|
@ -120,20 +125,20 @@ class Label { |
|
|
[x, yLine] = this._setAlignment(ctx, x, yLine, baseline); |
|
|
[x, yLine] = this._setAlignment(ctx, x, yLine, baseline); |
|
|
|
|
|
|
|
|
// configure context for drawing the text
|
|
|
// configure context for drawing the text
|
|
|
ctx.font = (selected && this.options.labelHighlightBold ? 'bold ' : '') + fontSize + "px " + this.options.font.face; |
|
|
|
|
|
|
|
|
ctx.font = (selected && this.nodeOptions.labelHighlightBold ? 'bold ' : '') + fontSize + "px " + this.fontOptions.face; |
|
|
ctx.fillStyle = fontColor; |
|
|
ctx.fillStyle = fontColor; |
|
|
ctx.textAlign = 'center'; |
|
|
ctx.textAlign = 'center'; |
|
|
|
|
|
|
|
|
// set the strokeWidth
|
|
|
// set the strokeWidth
|
|
|
if (this.options.font.strokeWidth > 0) { |
|
|
|
|
|
ctx.lineWidth = this.options.font.strokeWidth; |
|
|
|
|
|
|
|
|
if (this.fontOptions.strokeWidth > 0) { |
|
|
|
|
|
ctx.lineWidth = this.fontOptions.strokeWidth; |
|
|
ctx.strokeStyle = strokeColor; |
|
|
ctx.strokeStyle = strokeColor; |
|
|
ctx.lineJoin = 'round'; |
|
|
ctx.lineJoin = 'round'; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// draw the text
|
|
|
// draw the text
|
|
|
for (let i = 0; i < this.lineCount; i++) { |
|
|
for (let i = 0; i < this.lineCount; i++) { |
|
|
if (this.options.font.strokeWidth > 0) { |
|
|
|
|
|
|
|
|
if (this.fontOptions.strokeWidth > 0) { |
|
|
ctx.strokeText(this.lines[i], x, yLine); |
|
|
ctx.strokeText(this.lines[i], x, yLine); |
|
|
} |
|
|
} |
|
|
ctx.fillText(this.lines[i], x, yLine); |
|
|
ctx.fillText(this.lines[i], x, yLine); |
|
@ -144,16 +149,16 @@ class Label { |
|
|
_setAlignment(ctx, x, yLine, baseline) { |
|
|
_setAlignment(ctx, x, yLine, baseline) { |
|
|
// check for label alignment (for edges)
|
|
|
// check for label alignment (for edges)
|
|
|
// TODO: make alignment for nodes
|
|
|
// TODO: make alignment for nodes
|
|
|
if (this.options.font.align !== 'horizontal' && this.pointToSelf === false) { |
|
|
|
|
|
|
|
|
if (this.fontOptions.align !== 'horizontal' && this.pointToSelf === false) { |
|
|
x = 0; |
|
|
x = 0; |
|
|
yLine = 0; |
|
|
yLine = 0; |
|
|
|
|
|
|
|
|
let lineMargin = 2; |
|
|
let lineMargin = 2; |
|
|
if (this.options.font.align === 'top') { |
|
|
|
|
|
|
|
|
if (this.fontOptions.align === 'top') { |
|
|
ctx.textBaseline = 'alphabetic'; |
|
|
ctx.textBaseline = 'alphabetic'; |
|
|
yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers
|
|
|
yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers
|
|
|
} |
|
|
} |
|
|
else if (this.options.font.align === 'bottom') { |
|
|
|
|
|
|
|
|
else if (this.fontOptions.align === 'bottom') { |
|
|
ctx.textBaseline = 'hanging'; |
|
|
ctx.textBaseline = 'hanging'; |
|
|
yLine += 2 * lineMargin;// distance from edge, required because we use hanging. Hanging has less difference between browsers
|
|
|
yLine += 2 * lineMargin;// distance from edge, required because we use hanging. Hanging has less difference between browsers
|
|
|
} |
|
|
} |
|
@ -177,10 +182,10 @@ class Label { |
|
|
* @private |
|
|
* @private |
|
|
*/ |
|
|
*/ |
|
|
_getColor(viewFontSize) { |
|
|
_getColor(viewFontSize) { |
|
|
let fontColor = this.options.font.color || '#000000'; |
|
|
|
|
|
let strokeColor = this.options.font.strokeColor || '#ffffff'; |
|
|
|
|
|
if (viewFontSize <= this.options.scaling.label.drawThreshold) { |
|
|
|
|
|
let opacity = Math.max(0, Math.min(1, 1 - (this.options.scaling.label.drawThreshold - viewFontSize))); |
|
|
|
|
|
|
|
|
let fontColor = this.fontOptions.color || '#000000'; |
|
|
|
|
|
let strokeColor = this.fontOptions.strokeColor || '#ffffff'; |
|
|
|
|
|
if (viewFontSize <= this.nodeOptions.scaling.label.drawThreshold) { |
|
|
|
|
|
let opacity = Math.max(0, Math.min(1, 1 - (this.nodeOptions.scaling.label.drawThreshold - viewFontSize))); |
|
|
fontColor = util.overrideOpacity(fontColor, opacity); |
|
|
fontColor = util.overrideOpacity(fontColor, opacity); |
|
|
strokeColor = util.overrideOpacity(strokeColor, opacity); |
|
|
strokeColor = util.overrideOpacity(strokeColor, opacity); |
|
|
} |
|
|
} |
|
@ -197,7 +202,7 @@ class Label { |
|
|
getTextSize(ctx, selected = false) { |
|
|
getTextSize(ctx, selected = false) { |
|
|
let size = { |
|
|
let size = { |
|
|
width: this._processLabel(ctx,selected), |
|
|
width: this._processLabel(ctx,selected), |
|
|
height: this.options.font.size * this.lineCount, |
|
|
|
|
|
|
|
|
height: this.fontOptions.size * this.lineCount, |
|
|
lineCount: this.lineCount |
|
|
lineCount: this.lineCount |
|
|
}; |
|
|
}; |
|
|
return size; |
|
|
return size; |
|
@ -216,12 +221,12 @@ class Label { |
|
|
if (this.labelDirty === true) { |
|
|
if (this.labelDirty === true) { |
|
|
this.size.width = this._processLabel(ctx,selected); |
|
|
this.size.width = this._processLabel(ctx,selected); |
|
|
} |
|
|
} |
|
|
this.size.height = this.options.font.size * this.lineCount; |
|
|
|
|
|
|
|
|
this.size.height = this.fontOptions.size * this.lineCount; |
|
|
this.size.left = x - this.size.width * 0.5; |
|
|
this.size.left = x - this.size.width * 0.5; |
|
|
this.size.top = y - this.size.height * 0.5; |
|
|
this.size.top = y - this.size.height * 0.5; |
|
|
this.size.yLine = y + (1 - this.lineCount) * 0.5 * this.options.font.size; |
|
|
|
|
|
|
|
|
this.size.yLine = y + (1 - this.lineCount) * 0.5 * this.fontOptions.size; |
|
|
if (baseline === "hanging") { |
|
|
if (baseline === "hanging") { |
|
|
this.size.top += 0.5 * this.options.font.size; |
|
|
|
|
|
|
|
|
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.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; // distance from node
|
|
|
} |
|
|
} |
|
@ -241,10 +246,10 @@ class Label { |
|
|
let width = 0; |
|
|
let width = 0; |
|
|
let lines = ['']; |
|
|
let lines = ['']; |
|
|
let lineCount = 0; |
|
|
let lineCount = 0; |
|
|
if (this.options.label !== undefined) { |
|
|
|
|
|
lines = String(this.options.label).split('\n'); |
|
|
|
|
|
|
|
|
if (this.nodeOptions.label !== undefined) { |
|
|
|
|
|
lines = String(this.nodeOptions.label).split('\n'); |
|
|
lineCount = lines.length; |
|
|
lineCount = lines.length; |
|
|
ctx.font = (selected && this.options.labelHighlightBold ? 'bold ' : '') + this.options.font.size + "px " + this.options.font.face; |
|
|
|
|
|
|
|
|
ctx.font = (selected && this.nodeOptions.labelHighlightBold ? 'bold ' : '') + this.fontOptions.size + "px " + this.fontOptions.face; |
|
|
width = ctx.measureText(lines[0]).width; |
|
|
width = ctx.measureText(lines[0]).width; |
|
|
for (let i = 1; i < lineCount; i++) { |
|
|
for (let i = 1; i < lineCount; i++) { |
|
|
let lineWidth = ctx.measureText(lines[i]).width; |
|
|
let lineWidth = ctx.measureText(lines[i]).width; |
|
|