From 53e528466762326ac4fb316f6e13cd80238dde89 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Fri, 18 Nov 2016 17:43:02 -0500 Subject: [PATCH 01/34] Label backgrounds WIP --- Source/Scene/Label.js | 1 + Source/Scene/LabelCollection.js | 121 ++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/Source/Scene/Label.js b/Source/Scene/Label.js index 4eaf305735eb..b07166ac03e1 100644 --- a/Source/Scene/Label.js +++ b/Source/Scene/Label.js @@ -101,6 +101,7 @@ define([ this._labelCollection = labelCollection; this._glyphs = []; + this._backgroundBillboard = undefined; this._rebindAllGlyphs = true; this._repositionAllGlyphs = true; diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index bbaa96c887a8..82de6b1b14c3 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -1,6 +1,8 @@ /*global define*/ define([ + '../Core/BoundingRectangle', '../Core/Cartesian2', + '../Core/Color', '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', @@ -8,7 +10,10 @@ define([ '../Core/DeveloperError', '../Core/Matrix4', '../Core/writeTextToCanvas', + '../Renderer/RenderState', + '../Renderer/WebGLConstants', './BillboardCollection', + './BlendingState', './HeightReference', './HorizontalOrigin', './Label', @@ -16,7 +21,9 @@ define([ './TextureAtlas', './VerticalOrigin' ], function( + BoundingRectangle, Cartesian2, + Color, defaultValue, defined, defineProperties, @@ -24,7 +31,10 @@ define([ DeveloperError, Matrix4, writeTextToCanvas, + RenderState, + WebGLConstants, BillboardCollection, + BlendingState, HeightReference, HorizontalOrigin, Label, @@ -53,6 +63,28 @@ define([ this.dimensions = dimensions; } + var whitePixelCanvasId = 'ID_WHITE_PIXEL'; + var whitePixelSubRegionId = 'ID_WHITE_PIXEL_SUBREGION'; + var whitePixelSize = new Cartesian2(4, 4); + var whitePixelBoundingRegion = new BoundingRectangle(1, 1, 1, 1); + + function addWhitePixelCanvas(textureAtlas, labelCollection) { + var canvas = document.createElement('canvas'); + canvas.width = whitePixelSize.x; + canvas.height = whitePixelSize.y; + + var context2D = canvas.getContext('2d'); + context2D.fillStyle = '#fff'; + context2D.fillRect(0, 0, canvas.width, canvas.height); + + textureAtlas.addImage(whitePixelCanvasId, canvas).then(function(index) { + //glyphTextureInfo.index = index; + labelCollection._whitePixelIndex = index; + textureAtlas.addSubRegion(whitePixelCanvasId, whitePixelBoundingRegion); +console.log('white pixel index ' + index); + }); + } + // reusable object for calling writeTextToCanvas var writeTextToCanvasParameters = {}; function createGlyphCanvas(character, font, fillColor, outlineColor, outlineWidth, style, verticalOrigin) { @@ -105,6 +137,34 @@ define([ var glyphIndex; var textIndex; + var backgroundBillboard = label._backgroundBillboard; + if (!defined(backgroundBillboard)) { + if (labelCollection._spareBackgroundBillboards.length > 0) { + backgroundBillboard = labelCollection._spareBackgroundBillboards.pop(); + } else { + backgroundBillboard = labelCollection._backgroundBillboardCollection.add({ + collection : labelCollection + }); + } + label._backgroundBillboard = backgroundBillboard; + } + + backgroundBillboard.color = new Color(0.5, 0.5, 0.5, 0.8); // TODO: Configure color. + backgroundBillboard.show = label._show; + backgroundBillboard.position = label._position; + backgroundBillboard.eyeOffset = label._eyeOffset; + backgroundBillboard.pixelOffset = label._pixelOffset; + backgroundBillboard.horizontalOrigin = HorizontalOrigin.LEFT; + backgroundBillboard.verticalOrigin = label._verticalOrigin; + backgroundBillboard.heightReference = label._heightReference; + backgroundBillboard.scale = label._scale; + backgroundBillboard.pickPrimitive = label; + backgroundBillboard.id = label._id; + backgroundBillboard.image = whitePixelSubRegionId; + backgroundBillboard.translucencyByDistance = label._translucencyByDistance; + backgroundBillboard.pixelOffsetScaleByDistance = label._pixelOffsetScaleByDistance; + backgroundBillboard.distanceDisplayCondition = label._distanceDisplayCondition; + // if we have more glyphs than needed, unbind the extras. if (textLength < glyphsLength) { for (glyphIndex = textLength; glyphIndex < glyphsLength; ++glyphIndex) { @@ -216,11 +276,14 @@ define([ var glyphPixelOffset = new Cartesian2(); function repositionAllGlyphs(label, resolutionScale) { +console.log('reposition glyphs:', resolutionScale); var glyphs = label._glyphs; var glyph; var dimensions; var totalWidth = 0; var maxHeight = 0; + var maxDescent = 0; + var maxY = 0; var glyphIndex = 0; var glyphLength = glyphs.length; @@ -229,7 +292,12 @@ define([ dimensions = glyph.dimensions; totalWidth += dimensions.computedWidth; maxHeight = Math.max(maxHeight, dimensions.height); + maxY = Math.max(maxHeight, dimensions.height - dimensions.descent); + maxDescent = Math.max(maxDescent, dimensions.descent); +console.log('Glyph ' + glyphIndex + ' width ' + dimensions.computedWidth + ' descent ' + dimensions.descent + ' height ' + dimensions.height); } + var realMaxHeight = maxY + maxDescent; +console.log('totalWidth ' + totalWidth + ' maxheight ' + maxHeight + ' maxdescent ' + maxDescent + ' realMaxHeight ' + realMaxHeight); var scale = label._scale; var horizontalOrigin = label._horizontalOrigin; @@ -264,6 +332,21 @@ define([ glyphPixelOffset.x += dimensions.computedWidth * scale * resolutionScale; } + + var backgroundBillboard = label._backgroundBillboard; + if (defined(backgroundBillboard)) { + glyphPixelOffset.x = widthOffset * resolutionScale; + if (verticalOrigin === VerticalOrigin.BOTTOM) { + glyphPixelOffset.y = -maxDescent * scale; + } else if (verticalOrigin === VerticalOrigin.TOP) { + glyphPixelOffset.y = -(maxHeight - realMaxHeight) * scale - maxDescent * scale; + } else { + glyphPixelOffset.y = -(maxHeight - realMaxHeight) / 2 * scale - maxDescent * scale; + } + backgroundBillboard._setTranslate(glyphPixelOffset); + backgroundBillboard.width = totalWidth; + backgroundBillboard.height = realMaxHeight; + } } function destroyLabel(labelCollection, label) { @@ -271,6 +354,12 @@ define([ for ( var i = 0, len = glyphs.length; i < len; ++i) { unbindGlyph(labelCollection, glyphs[i]); } + if (defined(label._backgroundBillboard)) { + label._backgroundBillboard.show = false; + label._backgroundBillboard.image = undefined; + labelCollection._spareBackgroundBillboards.push(label._backgroundBillboard); + label._backgroundBillboard = undefined; + } label._labelCollection = undefined; if (defined(label._removeCallbackFunc)) { @@ -330,13 +419,29 @@ define([ this._scene = options.scene; this._textureAtlas = undefined; + this.__backgroundTextureAtlas = undefined; + this._whitePixelIndex = undefined; + + this._backgroundBillboardCollection = new BillboardCollection({ + scene : this._scene + }); + this._backgroundBillboardCollection.destroyTextureAtlas = false; this._billboardCollection = new BillboardCollection({ scene : this._scene }); this._billboardCollection.destroyTextureAtlas = false; + this._billboardCollection._rs = RenderState.fromCache({ + depthTest : { + enabled : true, + func : WebGLConstants.LEQUAL // default is LESS + }, + blending : BlendingState.ALPHA_BLEND + }); + this._spareBillboards = []; + this._spareBackgroundBillboards = []; this._glyphTextureCache = {}; this._labels = []; this._labelsToUpdate = []; @@ -572,9 +677,12 @@ define([ */ LabelCollection.prototype.update = function(frameState) { var billboardCollection = this._billboardCollection; + var backgroundBillboardCollection = this._backgroundBillboardCollection; billboardCollection.modelMatrix = this.modelMatrix; billboardCollection.debugShowBoundingVolume = this.debugShowBoundingVolume; + backgroundBillboardCollection.modelMatrix = this.modelMatrix; + backgroundBillboardCollection.debugShowBoundingVolume = this.debugShowBoundingVolume; var context = frameState.context; @@ -583,6 +691,16 @@ define([ context : context }); billboardCollection.textureAtlas = this._textureAtlas; + //addWhitePixelCanvas(this._textureAtlas, this); + } + + if (!defined(this._backgroundTextureAtlas)) { + this._backgroundTextureAtlas = new TextureAtlas({ + context : context, + initialSize : whitePixelSize + }); + backgroundBillboardCollection.textureAtlas = this._backgroundTextureAtlas; + addWhitePixelCanvas(this._backgroundTextureAtlas, this); } var uniformState = context.uniformState; @@ -621,6 +739,7 @@ define([ } this._labelsToUpdate.length = 0; + backgroundBillboardCollection.update(frameState); billboardCollection.update(frameState); }; @@ -660,6 +779,8 @@ define([ this.removeAll(); this._billboardCollection = this._billboardCollection.destroy(); this._textureAtlas = this._textureAtlas && this._textureAtlas.destroy(); + this._backgroundBillboardCollection = this._backgroundBillboardCollection.destroy(); + this._backgroundTextureAtlas = this._backgroundTextureAtlas && this._backgroundTextureAtlas.destroy(); return destroyObject(this); }; From f90a819e61662d9a9e0aaa7cf81d5c4b9fbf9fa1 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Wed, 23 Nov 2016 12:52:16 -0500 Subject: [PATCH 02/34] Fix some issues --- Source/Scene/LabelCollection.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index 829c5be2ba2c..adc99013264a 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -64,9 +64,9 @@ define([ } var whitePixelCanvasId = 'ID_WHITE_PIXEL'; - var whitePixelSubRegionId = 'ID_WHITE_PIXEL_SUBREGION'; - var whitePixelSize = new Cartesian2(4, 4); - var whitePixelBoundingRegion = new BoundingRectangle(1, 1, 1, 1); + //var whitePixelSubRegionId = 'ID_WHITE_PIXEL_SUBREGION'; + var whitePixelSize = new Cartesian2(64, 64); + //var whitePixelBoundingRegion = new BoundingRectangle(1, 1, 1, 1); function addWhitePixelCanvas(textureAtlas, labelCollection) { var canvas = document.createElement('canvas'); @@ -80,7 +80,7 @@ define([ textureAtlas.addImage(whitePixelCanvasId, canvas).then(function(index) { //glyphTextureInfo.index = index; labelCollection._whitePixelIndex = index; - textureAtlas.addSubRegion(whitePixelCanvasId, whitePixelBoundingRegion); + //textureAtlas.addSubRegion(whitePixelCanvasId, whitePixelBoundingRegion); console.log('white pixel index ' + index); }); } @@ -160,7 +160,7 @@ console.log('white pixel index ' + index); backgroundBillboard.scale = label._scale; backgroundBillboard.pickPrimitive = label; backgroundBillboard.id = label._id; - backgroundBillboard.image = whitePixelSubRegionId; + backgroundBillboard.image = whitePixelCanvasId; backgroundBillboard.translucencyByDistance = label._translucencyByDistance; backgroundBillboard.pixelOffsetScaleByDistance = label._pixelOffsetScaleByDistance; backgroundBillboard.distanceDisplayCondition = label._distanceDisplayCondition; @@ -282,7 +282,7 @@ console.log('reposition glyphs:', resolutionScale); var dimensions; var totalWidth = 0; var maxHeight = 0; - var maxDescent = 0; + var maxDescent = Number.NEGATIVE_INFINITY; var maxY = 0; var glyphIndex = 0; @@ -291,7 +291,7 @@ console.log('reposition glyphs:', resolutionScale); glyph = glyphs[glyphIndex]; dimensions = glyph.dimensions; maxHeight = Math.max(maxHeight, dimensions.height); - maxY = Math.max(maxHeight, dimensions.height - dimensions.descent); + maxY = Math.max(maxY, dimensions.height - dimensions.descent); maxDescent = Math.max(maxDescent, dimensions.descent); console.log('Glyph ' + glyphIndex + ' width ' + dimensions.computedWidth + ' descent ' + dimensions.descent + ' height ' + dimensions.height); From 113acae77a256cc246ea7fdaa0d202f148989747 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Wed, 23 Nov 2016 13:13:56 -0500 Subject: [PATCH 03/34] Fix maxHeight. --- Source/Scene/LabelCollection.js | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index adc99013264a..ba2e1225f48d 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -281,7 +281,6 @@ console.log('reposition glyphs:', resolutionScale); var glyph; var dimensions; var totalWidth = 0; - var maxHeight = 0; var maxDescent = Number.NEGATIVE_INFINITY; var maxY = 0; @@ -290,7 +289,6 @@ console.log('reposition glyphs:', resolutionScale); for (glyphIndex = 0; glyphIndex < glyphLength; ++glyphIndex) { glyph = glyphs[glyphIndex]; dimensions = glyph.dimensions; - maxHeight = Math.max(maxHeight, dimensions.height); maxY = Math.max(maxY, dimensions.height - dimensions.descent); maxDescent = Math.max(maxDescent, dimensions.descent); console.log('Glyph ' + glyphIndex + ' width ' + dimensions.computedWidth + ' descent ' + dimensions.descent + ' height ' + dimensions.height); @@ -301,8 +299,8 @@ console.log('Glyph ' + glyphIndex + ' width ' + dimensions.computedWidth + ' des totalWidth += glyphs[glyphIndex + 1].dimensions.bounds.minx; } } - var realMaxHeight = maxY + maxDescent; -console.log('totalWidth ' + totalWidth + ' maxheight ' + maxHeight + ' maxdescent ' + maxDescent + ' realMaxHeight ' + realMaxHeight); + var maxHeight = maxY + maxDescent; +console.log('totalWidth ' + totalWidth + ' maxY ' + maxY + ' maxdescent ' + maxDescent + ' maxHeight ' + maxHeight); var scale = label._scale; var horizontalOrigin = label._horizontalOrigin; @@ -321,12 +319,12 @@ console.log('totalWidth ' + totalWidth + ' maxheight ' + maxHeight + ' maxdescen glyph = glyphs[glyphIndex]; dimensions = glyph.dimensions; - if (verticalOrigin === VerticalOrigin.BOTTOM || dimensions.height === maxHeight) { + if (verticalOrigin === VerticalOrigin.BOTTOM || dimensions.height === maxY) { glyphPixelOffset.y = -dimensions.descent * scale; } else if (verticalOrigin === VerticalOrigin.TOP) { - glyphPixelOffset.y = -(maxHeight - dimensions.height) * scale - dimensions.descent * scale; + glyphPixelOffset.y = -(maxY - dimensions.height) * scale - dimensions.descent * scale; } else if (verticalOrigin === VerticalOrigin.CENTER) { - glyphPixelOffset.y = -(maxHeight - dimensions.height) / 2 * scale - dimensions.descent * scale; + glyphPixelOffset.y = -(maxY - dimensions.height) / 2 * scale - dimensions.descent * scale; } glyphPixelOffset.y *= resolutionScale; @@ -347,16 +345,16 @@ console.log('totalWidth ' + totalWidth + ' maxheight ' + maxHeight + ' maxdescen var backgroundBillboard = label._backgroundBillboard; if (defined(backgroundBillboard)) { glyphPixelOffset.x = widthOffset * resolutionScale; - if (verticalOrigin === VerticalOrigin.BOTTOM) { + if (verticalOrigin === VerticalOrigin.BOTTOM || maxHeight === maxY) { glyphPixelOffset.y = -maxDescent * scale; } else if (verticalOrigin === VerticalOrigin.TOP) { - glyphPixelOffset.y = -(maxHeight - realMaxHeight) * scale - maxDescent * scale; + glyphPixelOffset.y = -(maxY - maxHeight) * scale - maxDescent * scale; } else { - glyphPixelOffset.y = -(maxHeight - realMaxHeight) / 2 * scale - maxDescent * scale; + glyphPixelOffset.y = -(maxY - maxHeight) / 2 * scale - maxDescent * scale; } backgroundBillboard._setTranslate(glyphPixelOffset); backgroundBillboard.width = totalWidth; - backgroundBillboard.height = realMaxHeight; + backgroundBillboard.height = maxHeight; } } From 6babe4f2bed0222ff2847a2512b8029733df43e2 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Wed, 23 Nov 2016 13:26:26 -0500 Subject: [PATCH 04/34] Use an imageSubRegion to avoid the fringes. --- Source/Scene/LabelCollection.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index ba2e1225f48d..3289dc8c0148 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -64,9 +64,8 @@ define([ } var whitePixelCanvasId = 'ID_WHITE_PIXEL'; - //var whitePixelSubRegionId = 'ID_WHITE_PIXEL_SUBREGION'; - var whitePixelSize = new Cartesian2(64, 64); - //var whitePixelBoundingRegion = new BoundingRectangle(1, 1, 1, 1); + var whitePixelSize = new Cartesian2(4, 4); + var whitePixelBoundingRegion = new BoundingRectangle(1, 1, 1, 1); function addWhitePixelCanvas(textureAtlas, labelCollection) { var canvas = document.createElement('canvas'); @@ -143,7 +142,9 @@ console.log('white pixel index ' + index); backgroundBillboard = labelCollection._spareBackgroundBillboards.pop(); } else { backgroundBillboard = labelCollection._backgroundBillboardCollection.add({ - collection : labelCollection + collection : labelCollection, + image : whitePixelCanvasId, + imageSubRegion : whitePixelBoundingRegion }); } label._backgroundBillboard = backgroundBillboard; @@ -160,7 +161,6 @@ console.log('white pixel index ' + index); backgroundBillboard.scale = label._scale; backgroundBillboard.pickPrimitive = label; backgroundBillboard.id = label._id; - backgroundBillboard.image = whitePixelCanvasId; backgroundBillboard.translucencyByDistance = label._translucencyByDistance; backgroundBillboard.pixelOffsetScaleByDistance = label._pixelOffsetScaleByDistance; backgroundBillboard.distanceDisplayCondition = label._distanceDisplayCondition; From 000ac2593889bb7b83395dcdbf1e8ea15994fee0 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Wed, 23 Nov 2016 13:42:10 -0500 Subject: [PATCH 05/34] Remove debugging info. --- Source/Scene/LabelCollection.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index 3289dc8c0148..2aa6d630cf0b 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -77,10 +77,7 @@ define([ context2D.fillRect(0, 0, canvas.width, canvas.height); textureAtlas.addImage(whitePixelCanvasId, canvas).then(function(index) { - //glyphTextureInfo.index = index; labelCollection._whitePixelIndex = index; - //textureAtlas.addSubRegion(whitePixelCanvasId, whitePixelBoundingRegion); -console.log('white pixel index ' + index); }); } @@ -276,7 +273,6 @@ console.log('white pixel index ' + index); var glyphPixelOffset = new Cartesian2(); function repositionAllGlyphs(label, resolutionScale) { -console.log('reposition glyphs:', resolutionScale); var glyphs = label._glyphs; var glyph; var dimensions; @@ -291,7 +287,6 @@ console.log('reposition glyphs:', resolutionScale); dimensions = glyph.dimensions; maxY = Math.max(maxY, dimensions.height - dimensions.descent); maxDescent = Math.max(maxDescent, dimensions.descent); -console.log('Glyph ' + glyphIndex + ' width ' + dimensions.computedWidth + ' descent ' + dimensions.descent + ' height ' + dimensions.height); //Computing the total width must also account for the kering that occurs between letters. totalWidth += dimensions.width - dimensions.bounds.minx; @@ -300,7 +295,6 @@ console.log('Glyph ' + glyphIndex + ' width ' + dimensions.computedWidth + ' des } } var maxHeight = maxY + maxDescent; -console.log('totalWidth ' + totalWidth + ' maxY ' + maxY + ' maxdescent ' + maxDescent + ' maxHeight ' + maxHeight); var scale = label._scale; var horizontalOrigin = label._horizontalOrigin; From bce6efaf87ddcfcc9c4dc3f8a841d897ac0d7236 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Wed, 23 Nov 2016 15:06:15 -0500 Subject: [PATCH 06/34] Started work on label background settings. --- Source/Scene/Label.js | 52 +++++++++++++++++++++++++++++++++ Source/Scene/LabelCollection.js | 24 +++++++-------- 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/Source/Scene/Label.js b/Source/Scene/Label.js index b07166ac03e1..11289858032a 100644 --- a/Source/Scene/Label.js +++ b/Source/Scene/Label.js @@ -86,6 +86,8 @@ define([ this._fillColor = Color.clone(defaultValue(options.fillColor, Color.WHITE)); this._outlineColor = Color.clone(defaultValue(options.outlineColor, Color.BLACK)); this._outlineWidth = defaultValue(options.outlineWidth, 1.0); + this._backgroundColor = defaultValue(options.backgroundColor, Color.TRANSPARENT); + this._backgroundPadding = defaultValue(options.backgroundPadding, 2); this._style = defaultValue(options.style, LabelStyle.FILL); this._verticalOrigin = defaultValue(options.verticalOrigin, VerticalOrigin.BOTTOM); this._horizontalOrigin = defaultValue(options.horizontalOrigin, HorizontalOrigin.LEFT); @@ -337,6 +339,53 @@ define([ } }, + /** + * Gets or sets the background color of this label. TRANSPARENT means no background will be used. + * @memberof Label.prototype + * @type {Color} + */ + backgroundColor : { + get : function() { + return this._backgroundColor; + }, + set : function(value) { + //>>includeStart('debug', pragmas.debug); + if (!defined(value)) { + throw new DeveloperError('value is required.'); + } + //>>includeEnd('debug'); + + var backgroundColor = this._backgroundColor; + if (!Color.equals(backgroundColor, value)) { + Color.clone(value, backgroundColor); + rebindAllGlyphs(this); + } + } + }, + + /** + * Gets or sets the background padding, in pixels, of this label. + * @memberof Label.prototype + * @type {Number} + */ + backgroundPadding : { + get : function() { + return this._backgroundPadding; + }, + set : function(value) { + //>>includeStart('debug', pragmas.debug); + if (!defined(value)) { + throw new DeveloperError('value is required.'); + } + //>>includeEnd('debug'); + + if (this._backgroundPadding !== value) { + this._backgroundPadding = value; + rebindAllGlyphs(this); + } + } + }, + /** * Gets or sets the style of this label. * @memberof Label.prototype @@ -884,6 +933,8 @@ define([ defined(other) && this._show === other._show && this._scale === other._scale && + this._outlineWidth === other._outlineWidth && + this._backgroundPadding === other._backgroundPadding && this._style === other._style && this._verticalOrigin === other._verticalOrigin && this._horizontalOrigin === other._horizontalOrigin && @@ -893,6 +944,7 @@ define([ Cartesian3.equals(this._position, other._position) && Color.equals(this._fillColor, other._fillColor) && Color.equals(this._outlineColor, other._outlineColor) && + Color.equals(this._backgroundColor, other._backgroundColor) && Cartesian2.equals(this._pixelOffset, other._pixelOffset) && Cartesian3.equals(this._eyeOffset, other._eyeOffset) && NearFarScalar.equals(this._translucencyByDistance, other._translucencyByDistance) && diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index 2aa6d630cf0b..bb67eaf7ec35 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -133,6 +133,7 @@ define([ var glyphIndex; var textIndex; + var hasBackground = (label._backgroundColor.alpha >= 0.0039); // approximately 1.0/255.0 var backgroundBillboard = label._backgroundBillboard; if (!defined(backgroundBillboard)) { if (labelCollection._spareBackgroundBillboards.length > 0) { @@ -273,6 +274,7 @@ define([ var glyphPixelOffset = new Cartesian2(); function repositionAllGlyphs(label, resolutionScale) { +//resolutionScale = 4; // TODO: more testing var glyphs = label._glyphs; var glyph; var dimensions; @@ -338,7 +340,8 @@ define([ var backgroundBillboard = label._backgroundBillboard; if (defined(backgroundBillboard)) { - glyphPixelOffset.x = widthOffset * resolutionScale; + var backgroundPadding = label._backgroundPadding; + glyphPixelOffset.x = (widthOffset - backgroundPadding * scale) * resolutionScale; if (verticalOrigin === VerticalOrigin.BOTTOM || maxHeight === maxY) { glyphPixelOffset.y = -maxDescent * scale; } else if (verticalOrigin === VerticalOrigin.TOP) { @@ -346,9 +349,11 @@ define([ } else { glyphPixelOffset.y = -(maxY - maxHeight) / 2 * scale - maxDescent * scale; } + glyphPixelOffset.y += backgroundPadding * scale; + glyphPixelOffset.y *= resolutionScale; backgroundBillboard._setTranslate(glyphPixelOffset); - backgroundBillboard.width = totalWidth; - backgroundBillboard.height = maxHeight; + backgroundBillboard.width = totalWidth + (backgroundPadding * 2); + backgroundBillboard.height = maxHeight + (backgroundPadding * 2); } } @@ -359,7 +364,6 @@ define([ } if (defined(label._backgroundBillboard)) { label._backgroundBillboard.show = false; - label._backgroundBillboard.image = undefined; labelCollection._spareBackgroundBillboards.push(label._backgroundBillboard); label._backgroundBillboard = undefined; } @@ -425,6 +429,7 @@ define([ this.__backgroundTextureAtlas = undefined; this._whitePixelIndex = undefined; + // TODO: Don't allocate this until later. this._backgroundBillboardCollection = new BillboardCollection({ scene : this._scene }); @@ -435,14 +440,6 @@ define([ }); this._billboardCollection.destroyTextureAtlas = false; - this._billboardCollection._rs = RenderState.fromCache({ - depthTest : { - enabled : true, - func : WebGLConstants.LEQUAL // default is LESS - }, - blending : BlendingState.ALPHA_BLEND - }); - this._spareBillboards = []; this._spareBackgroundBillboards = []; this._glyphTextureCache = {}; @@ -534,6 +531,9 @@ define([ * font : '30px sans-serif', * fillColor : Cesium.Color.WHITE, * outlineColor : Cesium.Color.BLACK, + * outlineWidth : 1.0 + * backgroundColor : Cesium.Color.TRANSPARENT, + * backgroundPadding : 2.0 * style : Cesium.LabelStyle.FILL, * pixelOffset : Cesium.Cartesian2.ZERO, * eyeOffset : Cesium.Cartesian3.ZERO, From 83a5e1be96477a6d38cc8c6c9f1872dacdbab54a Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Wed, 23 Nov 2016 15:53:21 -0500 Subject: [PATCH 07/34] Unused requires --- Source/Scene/LabelCollection.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index af2ba4e9bf9c..90ca6c542cb6 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -10,8 +10,6 @@ define([ '../Core/DeveloperError', '../Core/Matrix4', '../Core/writeTextToCanvas', - '../Renderer/RenderState', - '../Renderer/WebGLConstants', './BillboardCollection', './HorizontalOrigin', './Label', @@ -29,8 +27,6 @@ define([ DeveloperError, Matrix4, writeTextToCanvas, - RenderState, - WebGLConstants, BillboardCollection, HorizontalOrigin, Label, From b54ccd5cc9bec50d21e507630c42d6fd217f45f7 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Wed, 23 Nov 2016 16:19:57 -0500 Subject: [PATCH 08/34] Convert backgroundPadding to Cartesian2, add showBackground bool. --- Source/Scene/Label.js | 17 ++++++++++------- Source/Scene/LabelCollection.js | 15 ++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Source/Scene/Label.js b/Source/Scene/Label.js index 11289858032a..6cd79eff5782 100644 --- a/Source/Scene/Label.js +++ b/Source/Scene/Label.js @@ -86,8 +86,9 @@ define([ this._fillColor = Color.clone(defaultValue(options.fillColor, Color.WHITE)); this._outlineColor = Color.clone(defaultValue(options.outlineColor, Color.BLACK)); this._outlineWidth = defaultValue(options.outlineWidth, 1.0); - this._backgroundColor = defaultValue(options.backgroundColor, Color.TRANSPARENT); - this._backgroundPadding = defaultValue(options.backgroundPadding, 2); + this._showBackground = defaultValue(options.showBackground, false); + this._backgroundColor = defaultValue(options.backgroundColor, new Color(0.165, 0.165, 0.165, 0.8)); + this._backgroundPadding = defaultValue(options.backgroundPadding, new Cartesian2(2, 2)); this._style = defaultValue(options.style, LabelStyle.FILL); this._verticalOrigin = defaultValue(options.verticalOrigin, VerticalOrigin.BOTTOM); this._horizontalOrigin = defaultValue(options.horizontalOrigin, HorizontalOrigin.LEFT); @@ -358,7 +359,7 @@ define([ var backgroundColor = this._backgroundColor; if (!Color.equals(backgroundColor, value)) { Color.clone(value, backgroundColor); - rebindAllGlyphs(this); + rebindAllGlyphs(this); // TODO: Can do something cheaper than rebindAllGlyphs? } } }, @@ -379,9 +380,10 @@ define([ } //>>includeEnd('debug'); - if (this._backgroundPadding !== value) { - this._backgroundPadding = value; - rebindAllGlyphs(this); + var backgroundPadding = this._backgroundPadding; + if (!Cartesian2.equals(backgroundPadding, value)) { + Cartesian2.clone(value, backgroundPadding); + rebindAllGlyphs(this); // TODO: Can do something cheaper than rebindAllGlyphs? } } }, @@ -934,7 +936,7 @@ define([ this._show === other._show && this._scale === other._scale && this._outlineWidth === other._outlineWidth && - this._backgroundPadding === other._backgroundPadding && + this._showBackground === other._showBackground && this._style === other._style && this._verticalOrigin === other._verticalOrigin && this._horizontalOrigin === other._horizontalOrigin && @@ -945,6 +947,7 @@ define([ Color.equals(this._fillColor, other._fillColor) && Color.equals(this._outlineColor, other._outlineColor) && Color.equals(this._backgroundColor, other._backgroundColor) && + Cartesian2.equals(this._backgroundPadding, other._backgroundPadding) && Cartesian2.equals(this._pixelOffset, other._pixelOffset) && Cartesian3.equals(this._eyeOffset, other._eyeOffset) && NearFarScalar.equals(this._translucencyByDistance, other._translucencyByDistance) && diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index 90ca6c542cb6..72fe9cac8205 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -140,7 +140,7 @@ define([ label._backgroundBillboard = backgroundBillboard; } - backgroundBillboard.color = new Color(0.5, 0.5, 0.5, 0.8); // TODO: Configure color. + backgroundBillboard.color = label._backgroundColor; // new Color(0.5, 0.5, 0.5, 0.8); // TODO: remove comment backgroundBillboard.show = label._show; backgroundBillboard.position = label._position; backgroundBillboard.eyeOffset = label._eyeOffset; @@ -333,7 +333,7 @@ define([ var backgroundBillboard = label._backgroundBillboard; if (defined(backgroundBillboard)) { var backgroundPadding = label._backgroundPadding; - glyphPixelOffset.x = (widthOffset - backgroundPadding * scale) * resolutionScale; + glyphPixelOffset.x = (widthOffset - backgroundPadding.x * scale) * resolutionScale; if (verticalOrigin === VerticalOrigin.BOTTOM || maxHeight === maxY) { glyphPixelOffset.y = -maxDescent * scale; } else if (verticalOrigin === VerticalOrigin.TOP) { @@ -341,11 +341,11 @@ define([ } else { glyphPixelOffset.y = -(maxY - maxHeight) / 2 * scale - maxDescent * scale; } - glyphPixelOffset.y += backgroundPadding * scale; + glyphPixelOffset.y += backgroundPadding.y * scale; glyphPixelOffset.y *= resolutionScale; backgroundBillboard._setTranslate(glyphPixelOffset); - backgroundBillboard.width = totalWidth + (backgroundPadding * 2); - backgroundBillboard.height = maxHeight + (backgroundPadding * 2); + backgroundBillboard.width = totalWidth + (backgroundPadding.x * 2); + backgroundBillboard.height = maxHeight + (backgroundPadding.y * 2); } } @@ -523,9 +523,10 @@ define([ * font : '30px sans-serif', * fillColor : Cesium.Color.WHITE, * outlineColor : Cesium.Color.BLACK, - * outlineWidth : 1.0 + * outlineWidth : 1.0, + * showBackground : false, * backgroundColor : Cesium.Color.TRANSPARENT, - * backgroundPadding : 2.0 + * backgroundPadding : new Cesium.Cartesian2(2, 2), * style : Cesium.LabelStyle.FILL, * pixelOffset : Cesium.Cartesian2.ZERO, * eyeOffset : Cesium.Cartesian3.ZERO, From 829439d32f03750b129414b8dc086c1cb306294b Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Thu, 24 Nov 2016 09:00:38 -0500 Subject: [PATCH 09/34] Fix vertical position of background with bottom and center alignments. --- Source/Scene/LabelCollection.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index 72fe9cac8205..fdc2eeeb0f84 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -334,14 +334,13 @@ define([ if (defined(backgroundBillboard)) { var backgroundPadding = label._backgroundPadding; glyphPixelOffset.x = (widthOffset - backgroundPadding.x * scale) * resolutionScale; - if (verticalOrigin === VerticalOrigin.BOTTOM || maxHeight === maxY) { - glyphPixelOffset.y = -maxDescent * scale; + if (verticalOrigin === VerticalOrigin.BOTTOM) { + glyphPixelOffset.y = -backgroundPadding.y * scale - maxDescent * scale; } else if (verticalOrigin === VerticalOrigin.TOP) { - glyphPixelOffset.y = -(maxY - maxHeight) * scale - maxDescent * scale; + glyphPixelOffset.y = -(maxY - maxHeight - backgroundPadding.y) * scale - maxDescent * scale; } else { glyphPixelOffset.y = -(maxY - maxHeight) / 2 * scale - maxDescent * scale; } - glyphPixelOffset.y += backgroundPadding.y * scale; glyphPixelOffset.y *= resolutionScale; backgroundBillboard._setTranslate(glyphPixelOffset); backgroundBillboard.width = totalWidth + (backgroundPadding.x * 2); From 31730591b2c7d8f9ebdc97201c2818a63f8a2e47 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Fri, 25 Nov 2016 15:32:50 -0500 Subject: [PATCH 10/34] Hook up showBackground flag. --- Source/Scene/Label.js | 28 +++++++++++++-- Source/Scene/LabelCollection.js | 61 ++++++++++++++++++--------------- 2 files changed, 60 insertions(+), 29 deletions(-) diff --git a/Source/Scene/Label.js b/Source/Scene/Label.js index 6cd79eff5782..162417c9bf76 100644 --- a/Source/Scene/Label.js +++ b/Source/Scene/Label.js @@ -340,6 +340,29 @@ define([ } }, + /** + * Determines if a background behind this label will be shown. + * @memberof Label.prototype + * @type {Boolean} + */ + showBackground : { + get : function() { + return this._showBackground; + }, + set : function(value) { + //>>includeStart('debug', pragmas.debug); + if (!defined(value)) { + throw new DeveloperError('value is required.'); + } + //>>includeEnd('debug'); + + if (this._showBackground !== value) { + this._showBackground = value; + rebindAllGlyphs(this); // TODO: Can do something cheaper than rebindAllGlyphs? + } + } + }, + /** * Gets or sets the background color of this label. TRANSPARENT means no background will be used. * @memberof Label.prototype @@ -365,9 +388,10 @@ define([ }, /** - * Gets or sets the background padding, in pixels, of this label. + * Gets or sets the background padding, in pixels, of this label. The x value + * controls horizontal padding, and the y value controls vertical padding. * @memberof Label.prototype - * @type {Number} + * @type {Cartesian2} */ backgroundPadding : { get : function() { diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index fdc2eeeb0f84..74be2a8a4f0c 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -125,35 +125,43 @@ define([ var glyphIndex; var textIndex; - var hasBackground = (label._backgroundColor.alpha >= 0.0039); // approximately 1.0/255.0 + var showBackground = label._showBackground; var backgroundBillboard = label._backgroundBillboard; - if (!defined(backgroundBillboard)) { - if (labelCollection._spareBackgroundBillboards.length > 0) { - backgroundBillboard = labelCollection._spareBackgroundBillboards.pop(); - } else { - backgroundBillboard = labelCollection._backgroundBillboardCollection.add({ - collection : labelCollection, - image : whitePixelCanvasId, - imageSubRegion : whitePixelBoundingRegion - }); + if (!showBackground) { + if (defined(backgroundBillboard)) { + backgroundBillboard.show = false; + labelCollection._spareBackgroundBillboards.push(backgroundBillboard); + label._backgroundBillboard = backgroundBillboard = undefined; + } + } else { + if (!defined(backgroundBillboard)) { + if (labelCollection._spareBackgroundBillboards.length > 0) { + backgroundBillboard = labelCollection._spareBackgroundBillboards.pop(); + } else { + backgroundBillboard = labelCollection._backgroundBillboardCollection.add({ + collection : labelCollection, + image : whitePixelCanvasId, + imageSubRegion : whitePixelBoundingRegion + }); + } + label._backgroundBillboard = backgroundBillboard; } - label._backgroundBillboard = backgroundBillboard; - } - backgroundBillboard.color = label._backgroundColor; // new Color(0.5, 0.5, 0.5, 0.8); // TODO: remove comment - backgroundBillboard.show = label._show; - backgroundBillboard.position = label._position; - backgroundBillboard.eyeOffset = label._eyeOffset; - backgroundBillboard.pixelOffset = label._pixelOffset; - backgroundBillboard.horizontalOrigin = HorizontalOrigin.LEFT; - backgroundBillboard.verticalOrigin = label._verticalOrigin; - backgroundBillboard.heightReference = label._heightReference; - backgroundBillboard.scale = label._scale; - backgroundBillboard.pickPrimitive = label; - backgroundBillboard.id = label._id; - backgroundBillboard.translucencyByDistance = label._translucencyByDistance; - backgroundBillboard.pixelOffsetScaleByDistance = label._pixelOffsetScaleByDistance; - backgroundBillboard.distanceDisplayCondition = label._distanceDisplayCondition; + backgroundBillboard.color = label._backgroundColor; + backgroundBillboard.show = label._show; + backgroundBillboard.position = label._position; + backgroundBillboard.eyeOffset = label._eyeOffset; + backgroundBillboard.pixelOffset = label._pixelOffset; + backgroundBillboard.horizontalOrigin = HorizontalOrigin.LEFT; + backgroundBillboard.verticalOrigin = label._verticalOrigin; + backgroundBillboard.heightReference = label._heightReference; + backgroundBillboard.scale = label._scale; + backgroundBillboard.pickPrimitive = label; + backgroundBillboard.id = label._id; + backgroundBillboard.translucencyByDistance = label._translucencyByDistance; + backgroundBillboard.pixelOffsetScaleByDistance = label._pixelOffsetScaleByDistance; + backgroundBillboard.distanceDisplayCondition = label._distanceDisplayCondition; + } // if we have more glyphs than needed, unbind the extras. if (textLength < glyphsLength) { @@ -266,7 +274,6 @@ define([ var glyphPixelOffset = new Cartesian2(); function repositionAllGlyphs(label, resolutionScale) { -//resolutionScale = 4; // TODO: more testing var glyphs = label._glyphs; var glyph; var dimensions; From 1e94844cc748cc169397545c703d55b4ba4e4ec9 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Sat, 26 Nov 2016 21:40:56 -0500 Subject: [PATCH 11/34] Add CZML support for label backgrounds. --- Source/DataSources/CzmlDataSource.js | 3 ++ Source/DataSources/LabelGraphics.js | 40 +++++++++++++++++++++++++++ Source/DataSources/LabelVisualizer.js | 8 ++++++ Source/Scene/LabelCollection.js | 4 +-- 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/Source/DataSources/CzmlDataSource.js b/Source/DataSources/CzmlDataSource.js index 46563103ff83..76994aaf2282 100644 --- a/Source/DataSources/CzmlDataSource.js +++ b/Source/DataSources/CzmlDataSource.js @@ -1374,6 +1374,9 @@ define([ processPacketData(String, label, 'font', labelData.font, interval, sourceUri, entityCollection); processPacketData(LabelStyle, label, 'style', labelData.style, interval, sourceUri, entityCollection); processPacketData(Number, label, 'scale', labelData.scale, interval, sourceUri, entityCollection); + processPacketData(Boolean, label, 'showBackground', labelData.showBackground, interval, sourceUri, entityCollection); + processPacketData(Color, label, 'backgroundColor', labelData.backgroundColor, interval, sourceUri, entityCollection); + processPacketData(Cartesian2, label, 'backgroundPadding', labelData.backgroundPadding, interval, sourceUri, entityCollection); processPacketData(Cartesian2, label, 'pixelOffset', labelData.pixelOffset, interval, sourceUri, entityCollection); processPacketData(Cartesian3, label, 'eyeOffset', labelData.eyeOffset, interval, sourceUri, entityCollection); processPacketData(HorizontalOrigin, label, 'horizontalOrigin', labelData.horizontalOrigin, interval, sourceUri, entityCollection); diff --git a/Source/DataSources/LabelGraphics.js b/Source/DataSources/LabelGraphics.js index f16fcb5977a9..2898c820706b 100644 --- a/Source/DataSources/LabelGraphics.js +++ b/Source/DataSources/LabelGraphics.js @@ -35,6 +35,9 @@ define([ * @param {Property} [options.outlineColor=Color.BLACK] A Property specifying the outline {@link Color}. * @param {Property} [options.outlineWidth=1.0] A numeric Property specifying the outline width. * @param {Property} [options.show=true] A boolean Property specifying the visibility of the label. + * @param {Property} [options.showBackground=true] A boolean Property specifying the visibility of the background behind the label. + * @param {Property} [options.backgroundColor=new Color(0.165, 0.165, 0.165, 0.8)] A Property specifying the background {@link Color}. + * @param {Property} [options.backgroundPadding=new Cartesian2(2, 2)] A {@link Cartesian2} Property specifying the horizontal and vertical background padding in pixels. * @param {Property} [options.scale=1.0] A numeric Property specifying the scale to apply to the text. * @param {Property} [options.horizontalOrigin=HorizontalOrigin.CENTER] A Property specifying the {@link HorizontalOrigin}. * @param {Property} [options.verticalOrigin=VerticalOrigin.CENTER] A Property specifying the {@link VerticalOrigin}. @@ -74,6 +77,12 @@ define([ this._scaleSubscription = undefined; this._show = undefined; this._showSubscription = undefined; + this._showBackground = undefined; + this._showBackgroundSubscription = undefined; + this._backgroundColor = undefined; + this._backgroundColorSubscription = undefined; + this._backgroundPadding = undefined; + this._backgroundPaddingSubscription = undefined; this._translucencyByDistance = undefined; this._translucencyByDistanceSubscription = undefined; this._pixelOffsetScaleByDistance = undefined; @@ -232,6 +241,31 @@ define([ */ show : createPropertyDescriptor('show'), + /** + * Gets or sets the boolean Property specifying the visibility of the background behind the label. + * @memberof LabelGraphics.prototype + * @type {Property} + * @default false + */ + showBackground : createPropertyDescriptor('showBackground'), + + /** + * Gets or sets the Property specifying the background {@link Color}. + * @memberof LabelGraphics.prototype + * @type {Property} + * @default new Color(0.165, 0.165, 0.165, 0.8) + */ + backgroundColor : createPropertyDescriptor('backgroundColor'), + + /** + * Gets or sets the {@link Cartesian2} Property specifying the label's horizontal and vertical + * background padding in pixels. + * @memberof LabelGraphics.prototype + * @type {Property} + * @default new Cartesian2(2, 2) + */ + backgroundPadding : createPropertyDescriptor('backgroundPadding'), + /** * Gets or sets {@link NearFarScalar} Property specifying the translucency of the label based on the distance from the camera. * A label's translucency will interpolate between the {@link NearFarScalar#nearValue} and @@ -279,6 +313,9 @@ define([ result.fillColor = this.fillColor; result.outlineColor = this.outlineColor; result.outlineWidth = this.outlineWidth; + result.showBackground = this.showBackground; + result.backgroundColor = this.backgroundColor; + result.backgroundPadding = this.backgroundPadding; result.scale = this.scale; result.horizontalOrigin = this.horizontalOrigin; result.verticalOrigin = this.verticalOrigin; @@ -311,6 +348,9 @@ define([ this.fillColor = defaultValue(this.fillColor, source.fillColor); this.outlineColor = defaultValue(this.outlineColor, source.outlineColor); this.outlineWidth = defaultValue(this.outlineWidth, source.outlineWidth); + this.showBackground = defaultValue(this.showBackground, source.showBackground); + this.backgroundColor = defaultValue(this.backgroundColor, source.backgroundColor); + this.backgroundPadding = defaultValue(this.backgroundPadding, source.backgroundPadding); this.scale = defaultValue(this.scale, source.scale); this.horizontalOrigin = defaultValue(this.horizontalOrigin, source.horizontalOrigin); this.verticalOrigin = defaultValue(this.verticalOrigin, source.verticalOrigin); diff --git a/Source/DataSources/LabelVisualizer.js b/Source/DataSources/LabelVisualizer.js index 7689eeff192b..3d008f5ccd63 100644 --- a/Source/DataSources/LabelVisualizer.js +++ b/Source/DataSources/LabelVisualizer.js @@ -41,6 +41,9 @@ define([ var defaultFillColor = Color.WHITE; var defaultOutlineColor = Color.BLACK; var defaultOutlineWidth = 1.0; + var defaultShowBackground = false; + var defaultBackgroundColor = new Color(0.165, 0.165, 0.165, 0.8); + var defaultBackgroundPadding = new Cartesian2(2, 2); var defaultPixelOffset = Cartesian2.ZERO; var defaultEyeOffset = Cartesian3.ZERO; var defaultHeightReference = HeightReference.NONE; @@ -50,6 +53,8 @@ define([ var position = new Cartesian3(); var fillColor = new Color(); var outlineColor = new Color(); + var backgroundColor = new Color(); + var backgroundPadding = new Cartesian2(); var eyeOffset = new Cartesian3(); var pixelOffset = new Cartesian2(); var translucencyByDistance = new NearFarScalar(); @@ -146,6 +151,9 @@ define([ label.fillColor = Property.getValueOrDefault(labelGraphics._fillColor, time, defaultFillColor, fillColor); label.outlineColor = Property.getValueOrDefault(labelGraphics._outlineColor, time, defaultOutlineColor, outlineColor); label.outlineWidth = Property.getValueOrDefault(labelGraphics._outlineWidth, time, defaultOutlineWidth); + label.showBackground = Property.getValueOrDefault(labelGraphics._showBackground, time, defaultShowBackground); + label.backgroundColor = Property.getValueOrDefault(labelGraphics._backgroundColor, time, defaultBackgroundColor, backgroundColor); + label.backgroundPadding = Property.getValueOrDefault(labelGraphics._backgroundPadding, time, defaultBackgroundPadding, backgroundPadding); label.pixelOffset = Property.getValueOrDefault(labelGraphics._pixelOffset, time, defaultPixelOffset, pixelOffset); label.eyeOffset = Property.getValueOrDefault(labelGraphics._eyeOffset, time, defaultEyeOffset, eyeOffset); label.heightReference = Property.getValueOrDefault(labelGraphics._heightReference, time, defaultHeightReference); diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index 74be2a8a4f0c..52fd37363f63 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -349,7 +349,7 @@ define([ glyphPixelOffset.y = -(maxY - maxHeight) / 2 * scale - maxDescent * scale; } glyphPixelOffset.y *= resolutionScale; - backgroundBillboard._setTranslate(glyphPixelOffset); + backgroundBillboard._setTranslate(glyphPixelOffset); // TODO: Make this count towards edge-based alignments. backgroundBillboard.width = totalWidth + (backgroundPadding.x * 2); backgroundBillboard.height = maxHeight + (backgroundPadding.y * 2); } @@ -531,7 +531,7 @@ define([ * outlineColor : Cesium.Color.BLACK, * outlineWidth : 1.0, * showBackground : false, - * backgroundColor : Cesium.Color.TRANSPARENT, + * backgroundColor : new Cesium.Color(0.165, 0.165, 0.165, 0.8), * backgroundPadding : new Cesium.Cartesian2(2, 2), * style : Cesium.LabelStyle.FILL, * pixelOffset : Cesium.Cartesian2.ZERO, From fdb3ddf7091d40f8ea0834495a3fa9082f1c92e2 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Sat, 26 Nov 2016 21:57:54 -0500 Subject: [PATCH 12/34] Update Terrain demo to use label backgrounds. --- Apps/Sandcastle/gallery/Terrain.html | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Apps/Sandcastle/gallery/Terrain.html b/Apps/Sandcastle/gallery/Terrain.html index 6346ceb11117..0d78a6bfda0e 100644 --- a/Apps/Sandcastle/gallery/Terrain.html +++ b/Apps/Sandcastle/gallery/Terrain.html @@ -148,11 +148,13 @@ }, label : { text : position.height.toFixed(1), + font : '10pt monospace', horizontalOrigin : Cesium.HorizontalOrigin.CENTER, - scale : 0.3, pixelOffset : new Cesium.Cartesian2(0, -14), - fillColor : Cesium.Color.RED, - outlineColor : Cesium.Color.WHITE + fillColor : Cesium.Color.BLACK, + outlineColor : Cesium.Color.BLACK, + showBackground : true, + backgroundColor : new Cesium.Color(0.9, 0.9, 0.9, 0.7) } }); } From ed5f91113c746f46fda62ad391e82a9e7f6b3803 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Wed, 30 Nov 2016 16:41:11 -0500 Subject: [PATCH 13/34] Remove poor assumptions from label tests. --- Specs/Scene/LabelCollectionSpec.js | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/Specs/Scene/LabelCollectionSpec.js b/Specs/Scene/LabelCollectionSpec.js index b23b9f579169..1771ba9dda32 100644 --- a/Specs/Scene/LabelCollectionSpec.js +++ b/Specs/Scene/LabelCollectionSpec.js @@ -56,6 +56,7 @@ defineSuite([ afterAll(function() { scene.destroyForSpecs(); }); + beforeEach(function() { scene.morphTo3D(0); @@ -1300,10 +1301,8 @@ defineSuite([ label.verticalOrigin = VerticalOrigin.TOP; scene.renderForSpecs(); - // vertical origin TOP should decrease (or equal) Y offset compared to CENTER - expect(getGlyphBillboardVertexTranslate(label, 0).y).toBeLessThanOrEqualTo(offset0.y); - expect(getGlyphBillboardVertexTranslate(label, 1).y).toBeLessThanOrEqualTo(offset1.y); - expect(getGlyphBillboardVertexTranslate(label, 2).y).toBeLessThanOrEqualTo(offset2.y); + // Because changing the label's vertical origin also changes the vertical origin of each + // individual glyph, it is not safe to assume anything about Y offsets being more or less. // X offset should be unchanged expect(getGlyphBillboardVertexTranslate(label, 0).x).toEqual(offset0.x); @@ -1313,11 +1312,6 @@ defineSuite([ label.verticalOrigin = VerticalOrigin.BOTTOM; scene.renderForSpecs(); - // vertical origin BOTTOM should increase (or equal) Y offset compared to CENTER - expect(getGlyphBillboardVertexTranslate(label, 0).y).toBeGreaterThanOrEqualTo(offset0.y); - expect(getGlyphBillboardVertexTranslate(label, 1).y).toBeGreaterThanOrEqualTo(offset1.y); - expect(getGlyphBillboardVertexTranslate(label, 2).y).toBeGreaterThanOrEqualTo(offset2.y); - // X offset should be unchanged expect(getGlyphBillboardVertexTranslate(label, 0).x).toEqual(offset0.x); expect(getGlyphBillboardVertexTranslate(label, 1).x).toEqual(offset1.x); @@ -1394,20 +1388,9 @@ defineSuite([ offset1 = getGlyphBillboardVertexTranslate(label, 1); offset2 = getGlyphBillboardVertexTranslate(label, 2); - // vertical origin TOP should decrease (or equal) Y offset compared to CENTER - expect(getGlyphBillboardVertexTranslate(label, 0).y).toBeLessThanOrEqualTo(offset0.y); - expect(getGlyphBillboardVertexTranslate(label, 1).y).toBeLessThanOrEqualTo(offset1.y); - expect(getGlyphBillboardVertexTranslate(label, 2).y).toBeLessThanOrEqualTo(offset2.y); - - label.verticalOrigin = VerticalOrigin.BOTTOM; - scene.renderForSpecs(); - - // vertical origin BOTTOM should increase (or equal) Y offset compared to CENTER - expect(getGlyphBillboardVertexTranslate(label, 0).y).toBeGreaterThanOrEqualTo(offset0.y); - expect(getGlyphBillboardVertexTranslate(label, 1).y).toBeGreaterThanOrEqualTo(offset1.y); - expect(getGlyphBillboardVertexTranslate(label, 2).y).toBeGreaterThanOrEqualTo(offset2.y); + // Because changing the label's vertical origin also changes the vertical origin of each + // individual glyph, it is not safe to assume anything about Y offsets being more or less. - label.verticalOrigin = VerticalOrigin.CENTER; label.horizontalOrigin = HorizontalOrigin.LEFT; scene.renderForSpecs(); From 39016dca788ed3eccc42f08f5bb4f8300e54764e Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Wed, 30 Nov 2016 20:47:52 -0500 Subject: [PATCH 14/34] Make label.backgroundPadding count towards alignment. --- Source/Scene/LabelCollection.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index 52fd37363f63..4326ae8fd26e 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -270,8 +270,9 @@ define([ label._repositionAllGlyphs = true; } - // reusable Cartesian2 instance + // reusable Cartesian2 instances var glyphPixelOffset = new Cartesian2(); + var scratchBackgroundPadding = new Cartesian2(); function repositionAllGlyphs(label, resolutionScale) { var glyphs = label._glyphs; @@ -281,6 +282,12 @@ define([ var maxDescent = Number.NEGATIVE_INFINITY; var maxY = 0; + var backgroundBillboard = label._backgroundBillboard; + var backgroundPadding = scratchBackgroundPadding; + Cartesian2.clone( + (defined(backgroundBillboard) ? label._backgroundPadding : Cartesian2.ZERO), + backgroundPadding); + var glyphIndex = 0; var glyphLength = glyphs.length; for (glyphIndex = 0; glyphIndex < glyphLength; ++glyphIndex) { @@ -303,7 +310,9 @@ define([ if (horizontalOrigin === HorizontalOrigin.CENTER) { widthOffset -= totalWidth / 2 * scale; } else if (horizontalOrigin === HorizontalOrigin.RIGHT) { - widthOffset -= totalWidth * scale; + widthOffset -= (totalWidth + backgroundPadding.x) * scale; + } else { + widthOffset += backgroundPadding.x * scale; } glyphPixelOffset.x = widthOffset * resolutionScale; @@ -314,10 +323,11 @@ define([ glyph = glyphs[glyphIndex]; dimensions = glyph.dimensions; + // TODO: Add VerticalOrigin.BASELINE as copy of current BOTTOM, then fix BOTTOM. if (verticalOrigin === VerticalOrigin.BOTTOM || dimensions.height === maxY) { glyphPixelOffset.y = -dimensions.descent * scale; } else if (verticalOrigin === VerticalOrigin.TOP) { - glyphPixelOffset.y = -(maxY - dimensions.height) * scale - dimensions.descent * scale; + glyphPixelOffset.y = -(maxY - dimensions.height + dimensions.descent + backgroundPadding.y) * scale; } else if (verticalOrigin === VerticalOrigin.CENTER) { glyphPixelOffset.y = -(maxY - dimensions.height) / 2 * scale - dimensions.descent * scale; } @@ -337,14 +347,13 @@ define([ } } - var backgroundBillboard = label._backgroundBillboard; + // TODO: Figure out why live-updating Entity.LabelGraphics doesn't get the right answer here. if (defined(backgroundBillboard)) { - var backgroundPadding = label._backgroundPadding; glyphPixelOffset.x = (widthOffset - backgroundPadding.x * scale) * resolutionScale; if (verticalOrigin === VerticalOrigin.BOTTOM) { glyphPixelOffset.y = -backgroundPadding.y * scale - maxDescent * scale; } else if (verticalOrigin === VerticalOrigin.TOP) { - glyphPixelOffset.y = -(maxY - maxHeight - backgroundPadding.y) * scale - maxDescent * scale; + glyphPixelOffset.y = -(maxY - maxHeight) * scale - maxDescent * scale; } else { glyphPixelOffset.y = -(maxY - maxHeight) / 2 * scale - maxDescent * scale; } From f0f3a1bf52514412c32f73bc84940b5b43b392f3 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Thu, 1 Dec 2016 11:37:14 -0500 Subject: [PATCH 15/34] Add VerticalOrigin.BASELINE --- Source/Scene/BillboardCollection.js | 5 +++++ Source/Scene/LabelCollection.js | 20 +++++++++++++------- Source/Scene/VerticalOrigin.js | 8 ++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Source/Scene/BillboardCollection.js b/Source/Scene/BillboardCollection.js index 34370f8819de..e269f5a96445 100644 --- a/Source/Scene/BillboardCollection.js +++ b/Source/Scene/BillboardCollection.js @@ -813,6 +813,11 @@ define([ show = false; } + // Raw billboards don't distinguish between BASELINE and BOTTOM, only LabelCollection does that. + if (verticalOrigin === VerticalOrigin.BASELINE) { + verticalOrigin = VerticalOrigin.BOTTOM; + } + billboardCollection._allHorizontalCenter = billboardCollection._allHorizontalCenter && horizontalOrigin === HorizontalOrigin.CENTER; billboardCollection._allVerticalCenter = billboardCollection._allVerticalCenter && verticalOrigin === VerticalOrigin.CENTER; diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index 4326ae8fd26e..fc534b190ff8 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -81,13 +81,13 @@ define([ writeTextToCanvasParameters.strokeColor = outlineColor; writeTextToCanvasParameters.strokeWidth = outlineWidth; - if (verticalOrigin === VerticalOrigin.BOTTOM) { - writeTextToCanvasParameters.textBaseline = 'bottom'; + if (verticalOrigin === VerticalOrigin.CENTER) { + writeTextToCanvasParameters.textBaseline = 'middle'; } else if (verticalOrigin === VerticalOrigin.TOP) { writeTextToCanvasParameters.textBaseline = 'top'; } else { - // VerticalOrigin.CENTER - writeTextToCanvasParameters.textBaseline = 'middle'; + // VerticalOrigin.BOTTOM and VerticalOrigin.BASELINE + writeTextToCanvasParameters.textBaseline = 'bottom'; } writeTextToCanvasParameters.fill = style === LabelStyle.FILL || style === LabelStyle.FILL_AND_OUTLINE; @@ -324,12 +324,15 @@ define([ dimensions = glyph.dimensions; // TODO: Add VerticalOrigin.BASELINE as copy of current BOTTOM, then fix BOTTOM. - if (verticalOrigin === VerticalOrigin.BOTTOM || dimensions.height === maxY) { + if (verticalOrigin === VerticalOrigin.BASELINE) { glyphPixelOffset.y = -dimensions.descent * scale; } else if (verticalOrigin === VerticalOrigin.TOP) { glyphPixelOffset.y = -(maxY - dimensions.height + dimensions.descent + backgroundPadding.y) * scale; } else if (verticalOrigin === VerticalOrigin.CENTER) { glyphPixelOffset.y = -(maxY - dimensions.height) / 2 * scale - dimensions.descent * scale; + } else { + // VerticalOrigin.BOTTOM + glyphPixelOffset.y = (maxDescent - dimensions.descent + backgroundPadding.y) * scale; } glyphPixelOffset.y *= resolutionScale; @@ -350,12 +353,15 @@ define([ // TODO: Figure out why live-updating Entity.LabelGraphics doesn't get the right answer here. if (defined(backgroundBillboard)) { glyphPixelOffset.x = (widthOffset - backgroundPadding.x * scale) * resolutionScale; - if (verticalOrigin === VerticalOrigin.BOTTOM) { + if (verticalOrigin === VerticalOrigin.BASELINE) { glyphPixelOffset.y = -backgroundPadding.y * scale - maxDescent * scale; } else if (verticalOrigin === VerticalOrigin.TOP) { glyphPixelOffset.y = -(maxY - maxHeight) * scale - maxDescent * scale; - } else { + } else if (verticalOrigin === VerticalOrigin.CENTER) { glyphPixelOffset.y = -(maxY - maxHeight) / 2 * scale - maxDescent * scale; + } else { + // VerticalOrigin.BOTTOM + glyphPixelOffset.y = 0; } glyphPixelOffset.y *= resolutionScale; backgroundBillboard._setTranslate(glyphPixelOffset); // TODO: Make this count towards edge-based alignments. diff --git a/Source/Scene/VerticalOrigin.js b/Source/Scene/VerticalOrigin.js index b72103652dc6..b810ddc85c66 100644 --- a/Source/Scene/VerticalOrigin.js +++ b/Source/Scene/VerticalOrigin.js @@ -31,6 +31,14 @@ define([ */ BOTTOM : 1, + /** + * If the object contains text, the origin is at the baseline of the text, else the origin is at the bottom of the object. + * + * @type {Number} + * @constant + */ + BASELINE : 2, + /** * The origin is at the top of the object. * From 3c6a76f319a17ce781cb99120ea4ab4d27c9f17d Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Thu, 1 Dec 2016 16:20:02 -0500 Subject: [PATCH 16/34] Fix various issues with value updates. --- Source/Scene/Label.js | 69 ++++++++++++++++++++++++++++++--- Source/Scene/LabelCollection.js | 4 +- 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/Source/Scene/Label.js b/Source/Scene/Label.js index 162417c9bf76..6124e105e806 100644 --- a/Source/Scene/Label.js +++ b/Source/Scene/Label.js @@ -146,6 +146,10 @@ define([ billboard.show = value; } } + var backgroundBillboard = this._backgroundBillboard; + if (defined(backgroundBillboard)) { + backgroundBillboard.show = value; + } } } }, @@ -177,6 +181,10 @@ define([ billboard.position = value; } } + var backgroundBillboard = this._backgroundBillboard; + if (defined(backgroundBillboard)) { + backgroundBillboard.position = value; + } if (this._heightReference !== HeightReference.NONE) { this._updateClamping(); @@ -211,6 +219,10 @@ define([ billboard.heightReference = value; } } + var backgroundBillboard = this._backgroundBillboard; + if (defined(backgroundBillboard)) { + backgroundBillboard.heightReference = value; + } repositionAllGlyphs(this); @@ -358,7 +370,7 @@ define([ if (this._showBackground !== value) { this._showBackground = value; - rebindAllGlyphs(this); // TODO: Can do something cheaper than rebindAllGlyphs? + rebindAllGlyphs(this); } } }, @@ -382,7 +394,11 @@ define([ var backgroundColor = this._backgroundColor; if (!Color.equals(backgroundColor, value)) { Color.clone(value, backgroundColor); - rebindAllGlyphs(this); // TODO: Can do something cheaper than rebindAllGlyphs? + + var backgroundBillboard = this._backgroundBillboard; + if (defined(backgroundBillboard)) { + backgroundBillboard.color = backgroundColor; + } } } }, @@ -407,7 +423,7 @@ define([ var backgroundPadding = this._backgroundPadding; if (!Cartesian2.equals(backgroundPadding, value)) { Cartesian2.clone(value, backgroundPadding); - rebindAllGlyphs(this); // TODO: Can do something cheaper than rebindAllGlyphs? + repositionAllGlyphs(this); } } }, @@ -473,6 +489,10 @@ define([ glyph.billboard.pixelOffset = value; } } + var backgroundBillboard = this._backgroundBillboard; + if (defined(backgroundBillboard)) { + backgroundBillboard.pixelOffset = value; + } } } }, @@ -521,6 +541,10 @@ define([ glyph.billboard.translucencyByDistance = value; } } + var backgroundBillboard = this._backgroundBillboard; + if (defined(backgroundBillboard)) { + backgroundBillboard.translucencyByDistance = value; + } } } }, @@ -570,6 +594,10 @@ define([ glyph.billboard.pixelOffsetScaleByDistance = value; } } + var backgroundBillboard = this._backgroundBillboard; + if (defined(backgroundBillboard)) { + backgroundBillboard.pixelOffsetScaleByDistance = value; + } } } }, @@ -618,6 +646,10 @@ define([ glyph.billboard.eyeOffset = value; } } + var backgroundBillboard = this._backgroundBillboard; + if (defined(backgroundBillboard)) { + backgroundBillboard.eyeOffset = value; + } } } }, @@ -689,6 +721,10 @@ define([ glyph.billboard.verticalOrigin = value; } } + var backgroundBillboard = this._backgroundBillboard; + if (defined(backgroundBillboard)) { + backgroundBillboard.verticalOrigin = value; + } repositionAllGlyphs(this); } @@ -733,6 +769,10 @@ define([ glyph.billboard.scale = value; } } + var backgroundBillboard = this._backgroundBillboard; + if (defined(backgroundBillboard)) { + backgroundBillboard.scale = value; + } repositionAllGlyphs(this); } @@ -765,6 +805,10 @@ define([ glyph.billboard.distanceDisplayCondition = value; } } + var backgroundBillboard = this._backgroundBillboard; + if (defined(backgroundBillboard)) { + backgroundBillboard.distanceDisplayCondition = value; + } } } }, @@ -789,6 +833,10 @@ define([ glyph.billboard.id = value; } } + var backgroundBillboard = this._backgroundBillboard; + if (defined(backgroundBillboard)) { + backgroundBillboard.id = value; + } } } }, @@ -807,19 +855,23 @@ define([ this._actualClampedPosition = Cartesian3.clone(value, this._actualClampedPosition); var glyphs = this._glyphs; + value = defaultValue(value, this._position); for (var i = 0, len = glyphs.length; i < len; i++) { var glyph = glyphs[i]; if (defined(glyph.billboard)) { // Set all the private values here, because we already clamped to ground // so we don't want to do it again for every glyph - glyph.billboard._clampedPosition = value; - - value = defaultValue(value, this._position); Cartesian3.clone(value, glyph.billboard._position); Cartesian3.clone(value, glyph.billboard._actualPosition); } } + var backgroundBillboard = this._backgroundBillboard; + if (defined(backgroundBillboard)) { + backgroundBillboard._clampedPosition = value; + Cartesian3.clone(value, backgroundBillboard._position); + Cartesian3.clone(value, backgroundBillboard._actualPosition); + } } }, @@ -846,6 +898,10 @@ define([ glyph.billboard.clusterShow = value; } } + var backgroundBillboard = this._backgroundBillboard; + if (defined(backgroundBillboard)) { + backgroundBillboard.cluserShow = value; + } } } } @@ -938,6 +994,7 @@ define([ if (!defined(result)) { result = new BoundingRectangle(); } + // TODO: Add backgroundPadding here. result.x = x; result.y = y; diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index fc534b190ff8..937dd40aa504 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -323,7 +323,6 @@ define([ glyph = glyphs[glyphIndex]; dimensions = glyph.dimensions; - // TODO: Add VerticalOrigin.BASELINE as copy of current BOTTOM, then fix BOTTOM. if (verticalOrigin === VerticalOrigin.BASELINE) { glyphPixelOffset.y = -dimensions.descent * scale; } else if (verticalOrigin === VerticalOrigin.TOP) { @@ -350,7 +349,6 @@ define([ } } - // TODO: Figure out why live-updating Entity.LabelGraphics doesn't get the right answer here. if (defined(backgroundBillboard)) { glyphPixelOffset.x = (widthOffset - backgroundPadding.x * scale) * resolutionScale; if (verticalOrigin === VerticalOrigin.BASELINE) { @@ -364,9 +362,9 @@ define([ glyphPixelOffset.y = 0; } glyphPixelOffset.y *= resolutionScale; - backgroundBillboard._setTranslate(glyphPixelOffset); // TODO: Make this count towards edge-based alignments. backgroundBillboard.width = totalWidth + (backgroundPadding.x * 2); backgroundBillboard.height = maxHeight + (backgroundPadding.y * 2); + backgroundBillboard._setTranslate(glyphPixelOffset); } } From 10045f613230e25b7667c683d03c9b507a5d8ca0 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Thu, 1 Dec 2016 18:08:38 -0500 Subject: [PATCH 17/34] Mostly fix getScreenSpaceBoundingBox. --- Source/Scene/Billboard.js | 2 +- Source/Scene/Label.js | 70 +++++++++++++++++++++------------ Source/Scene/LabelCollection.js | 1 - 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/Source/Scene/Billboard.js b/Source/Scene/Billboard.js index 814aa5fcbd3d..30c08de4bf9e 100644 --- a/Source/Scene/Billboard.js +++ b/Source/Scene/Billboard.js @@ -1227,7 +1227,7 @@ define([ } var y = screenSpacePosition.y; - if (billboard.verticalOrigin === VerticalOrigin.TOP) { + if (billboard.verticalOrigin === VerticalOrigin.TOP) { // TODO: This is likely wrong! y -= height; } else if (billboard.verticalOrigin === VerticalOrigin.CENTER) { y -= height * 0.5; diff --git a/Source/Scene/Label.js b/Source/Scene/Label.js index 6124e105e806..091b1f347dea 100644 --- a/Source/Scene/Label.js +++ b/Source/Scene/Label.js @@ -957,44 +957,62 @@ define([ * @private */ Label.getScreenSpaceBoundingBox = function(label, screenSpacePosition, result) { + var x = 0; + var y = 0; var width = 0; var height = 0; + var scale = label.scale; - var glyphs = label._glyphs; - var length = glyphs.length; - for (var i = 0; i < length; ++i) { - var glyph = glyphs[i]; - var billboard = glyph.billboard; - if (!defined(billboard)) { - continue; + var backgroundBillboard = label._backgroundBillboard; + if (defined(backgroundBillboard)) { + x = screenSpacePosition.x + backgroundBillboard._translate.x; + y = screenSpacePosition.y - backgroundBillboard._translate.y; + width = backgroundBillboard.width * scale; + height = backgroundBillboard.height * scale; + + if (label.verticalOrigin === VerticalOrigin.BOTTOM || label.verticalOrigin === VerticalOrigin.BASELINE) { + y -= height; + } else if (label.verticalOrigin === VerticalOrigin.CENTER) { + y -= height * 0.5; } + } else { + x = Number.POSITIVE_INFINITY; + y = Number.POSITIVE_INFINITY; + var maxX = 0; + var maxY = 0; + var glyphs = label._glyphs; + var length = glyphs.length; + for (var i = 0; i < length; ++i) { + var glyph = glyphs[i]; + var billboard = glyph.billboard; + if (!defined(billboard)) { + continue; + } - width += billboard.width; - height = Math.max(height, billboard.height); - } + var glyphX = screenSpacePosition.x + billboard._translate.x; // TODO: Accomodate resolutionScale in _translate? + var glyphY = screenSpacePosition.y - billboard._translate.y; + var glyphWidth = billboard.width * scale; + var glyphHeight = billboard.height * scale; - var scale = label.scale; - width *= scale; - height *= scale; - - var x = screenSpacePosition.x; - if (label.horizontalOrigin === HorizontalOrigin.RIGHT) { - x -= width; - } else if (label.horizontalOrigin === HorizontalOrigin.CENTER) { - x -= width * 0.5; - } + if (label.verticalOrigin === VerticalOrigin.BOTTOM || label.verticalOrigin === VerticalOrigin.BASELINE) { + glyphY -= glyphHeight; + } else if (label.verticalOrigin === VerticalOrigin.CENTER) { + glyphY -= glyphHeight * 0.5; + } + + x = Math.min(x, glyphX); + y = Math.min(y, glyphY); + maxX = Math.max(maxX, glyphX + glyphWidth); + maxY = Math.max(maxY, glyphY + glyphHeight); + } - var y = screenSpacePosition.y; - if (label.verticalOrigin === VerticalOrigin.TOP) { - y -= height; - } else if (label.verticalOrigin === VerticalOrigin.CENTER) { - y -= height * 0.5; + width = maxX - x; + height = maxY - y; } if (!defined(result)) { result = new BoundingRectangle(); } - // TODO: Add backgroundPadding here. result.x = x; result.y = y; diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index 937dd40aa504..6a79b67bcd7d 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -706,7 +706,6 @@ define([ context : context }); billboardCollection.textureAtlas = this._textureAtlas; - //addWhitePixelCanvas(this._textureAtlas, this); } if (!defined(this._backgroundTextureAtlas)) { From e3010ada419f97a38c4e8fbbf687bf14efd1baaf Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Fri, 2 Dec 2016 16:11:53 -0500 Subject: [PATCH 18/34] More fixes to getScreenSpacePosition. --- Source/DataSources/EntityCluster.js | 4 +- Source/Scene/Billboard.js | 9 +- Source/Scene/Label.js | 12 +- Specs/Scene/BillboardCollectionSpec.js | 4 +- Specs/Scene/LabelCollectionSpec.js | 204 +++++++++++++++++++------ 5 files changed, 174 insertions(+), 59 deletions(-) diff --git a/Source/DataSources/EntityCluster.js b/Source/DataSources/EntityCluster.js index e3d4641b965d..5facb35096a5 100644 --- a/Source/DataSources/EntityCluster.js +++ b/Source/DataSources/EntityCluster.js @@ -106,7 +106,7 @@ define([ function getBoundingBox(item, coord, pixelRange, entityCluster, result) { if (defined(item._labelCollection) && entityCluster._clusterLabels) { - result = Label.getScreenSpaceBoundingBox(item, coord, result); + result = Label.getScreenSpaceBoundingBox(item, coord, entityCluster._scene, result); } else if (defined(item._billboardCollection) && entityCluster._clusterBillboards) { result = Billboard.getScreenSpaceBoundingBox(item, coord, result); } else if (defined(item._pointPrimitiveCollection) && entityCluster._clusterPoints) { @@ -148,7 +148,7 @@ define([ cluster.label.show = true; cluster.label.text = numPoints.toLocaleString(); cluster.billboard.position = cluster.label.position = cluster.point.position = position; - + entityCluster._clusterEvent.raiseEvent(ids, cluster); } diff --git a/Source/Scene/Billboard.js b/Source/Scene/Billboard.js index 30c08de4bf9e..5b50dfe4c7e6 100644 --- a/Source/Scene/Billboard.js +++ b/Source/Scene/Billboard.js @@ -1132,9 +1132,7 @@ define([ return SceneTransforms.computeActualWgs84Position(frameState, tempCartesian3); }; - var scratchCartesian2 = new Cartesian2(); var scratchCartesian3 = new Cartesian3(); - var scratchComputePixelOffset = new Cartesian2(); // This function is basically a stripped-down JavaScript version of BillboardCollectionVS.glsl Billboard._computeScreenSpacePosition = function(modelMatrix, position, eyeOffset, pixelOffset, scene, result) { @@ -1148,10 +1146,7 @@ define([ } // Apply pixel offset - pixelOffset = Cartesian2.clone(pixelOffset, scratchComputePixelOffset); - var po = Cartesian2.multiplyByScalar(pixelOffset, scene.context.uniformState.resolutionScale, scratchCartesian2); - positionWC.x += po.x; - positionWC.y += po.y; + Cartesian2.add(positionWC, pixelOffset, positionWC); return positionWC; }; @@ -1227,7 +1222,7 @@ define([ } var y = screenSpacePosition.y; - if (billboard.verticalOrigin === VerticalOrigin.TOP) { // TODO: This is likely wrong! + if (billboard.verticalOrigin === VerticalOrigin.BOTTOM || billboard.verticalOrigin === VerticalOrigin.BASELINE) { y -= height; } else if (billboard.verticalOrigin === VerticalOrigin.CENTER) { y -= height * 0.5; diff --git a/Source/Scene/Label.js b/Source/Scene/Label.js index 091b1f347dea..3e5f2e64be92 100644 --- a/Source/Scene/Label.js +++ b/Source/Scene/Label.js @@ -951,22 +951,24 @@ define([ * Gets a label's screen space bounding box centered around screenSpacePosition. * @param {Label} label The label to get the screen space bounding box for. * @param {Cartesian2} screenSpacePosition The screen space center of the label. + * @param {Scene} scene The scene the label is in. * @param {BoundingRectangle} [result] The object onto which to store the result. * @returns {BoundingRectangle} The screen space bounding box. * * @private */ - Label.getScreenSpaceBoundingBox = function(label, screenSpacePosition, result) { + Label.getScreenSpaceBoundingBox = function(label, screenSpacePosition, scene, result) { var x = 0; var y = 0; var width = 0; var height = 0; var scale = label.scale; + var resolutionScale = scene.context.uniformState.resolutionScale; var backgroundBillboard = label._backgroundBillboard; if (defined(backgroundBillboard)) { - x = screenSpacePosition.x + backgroundBillboard._translate.x; - y = screenSpacePosition.y - backgroundBillboard._translate.y; + x = screenSpacePosition.x + (backgroundBillboard._translate.x / resolutionScale); + y = screenSpacePosition.y - (backgroundBillboard._translate.y / resolutionScale); width = backgroundBillboard.width * scale; height = backgroundBillboard.height * scale; @@ -989,8 +991,8 @@ define([ continue; } - var glyphX = screenSpacePosition.x + billboard._translate.x; // TODO: Accomodate resolutionScale in _translate? - var glyphY = screenSpacePosition.y - billboard._translate.y; + var glyphX = screenSpacePosition.x + (billboard._translate.x / resolutionScale); + var glyphY = screenSpacePosition.y - (billboard._translate.y / resolutionScale); var glyphWidth = billboard.width * scale; var glyphHeight = billboard.height * scale; diff --git a/Specs/Scene/BillboardCollectionSpec.js b/Specs/Scene/BillboardCollectionSpec.js index bb0e68eb2a11..dc0c9b432c5a 100644 --- a/Specs/Scene/BillboardCollectionSpec.js +++ b/Specs/Scene/BillboardCollectionSpec.js @@ -1154,14 +1154,14 @@ defineSuite([ var bbox = Billboard.getScreenSpaceBoundingBox(b, Cartesian2.ZERO); expect(bbox.x).toEqual(-halfWidth); - expect(bbox.y).toEqual(0); + expect(bbox.y).toEqual(-height); expect(bbox.width).toEqual(width); expect(bbox.height).toEqual(height); b.verticalOrigin = VerticalOrigin.TOP; bbox = Billboard.getScreenSpaceBoundingBox(b, Cartesian2.ZERO); expect(bbox.x).toEqual(-halfWidth); - expect(bbox.y).toEqual(-height); + expect(bbox.y).toEqual(0); expect(bbox.width).toEqual(width); expect(bbox.height).toEqual(height); }); diff --git a/Specs/Scene/LabelCollectionSpec.js b/Specs/Scene/LabelCollectionSpec.js index 1771ba9dda32..65d20bcba2dd 100644 --- a/Specs/Scene/LabelCollectionSpec.js +++ b/Specs/Scene/LabelCollectionSpec.js @@ -864,8 +864,10 @@ defineSuite([ }); scene.renderForSpecs(); - var width = 0; - var height = 0; + var x = Number.POSITIVE_INFINITY; + var y = Number.POSITIVE_INFINITY; + var maxX = 0; + var maxY = 0; var glyphs = label._glyphs; var length = glyphs.length; @@ -876,16 +878,23 @@ defineSuite([ continue; } - width += billboard.width; - height = Math.max(height, billboard.height); + var glyphWidth = billboard.width * scale; + var glyphHeight = billboard.height * scale; + var glyphX = billboard._translate.x; + var glyphY = -(billboard._translate.y + glyphHeight); + + x = Math.min(x, glyphX); + y = Math.min(y, glyphY); + maxX = Math.max(maxX, glyphX + glyphWidth); + maxY = Math.max(maxY, glyphY + glyphHeight); } - width *= scale; - height *= scale; + var width = maxX - x; + var height = maxY - y; - var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); - expect(bbox.x).toEqual(0); - expect(bbox.y).toEqual(0); + var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, scene); + expect(bbox.x).toEqual(x); + expect(bbox.y).toEqual(y); expect(bbox.width).toEqual(width); expect(bbox.height).toEqual(height); }); @@ -899,8 +908,10 @@ defineSuite([ }); scene.renderForSpecs(); - var width = 0; - var height = 0; + var x = Number.POSITIVE_INFINITY; + var y = Number.POSITIVE_INFINITY; + var maxX = 0; + var maxY = 0; var glyphs = label._glyphs; var length = glyphs.length; @@ -911,23 +922,30 @@ defineSuite([ continue; } - width += billboard.width; - height = Math.max(height, billboard.height); + var glyphWidth = billboard.width * scale; + var glyphHeight = billboard.height * scale; + var glyphX = billboard._translate.x; + var glyphY = -(billboard._translate.y + glyphHeight); + + x = Math.min(x, glyphX); + y = Math.min(y, glyphY); + maxX = Math.max(maxX, glyphX + glyphWidth); + maxY = Math.max(maxY, glyphY + glyphHeight); } - width *= scale; - height *= scale; + var width = maxX - x; + var height = maxY - y; var result = new BoundingRectangle(); - var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, result); - expect(bbox.x).toEqual(0); - expect(bbox.y).toEqual(0); + var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, scene, result); + expect(bbox.x).toEqual(x); + expect(bbox.y).toEqual(y); expect(bbox.width).toEqual(width); expect(bbox.height).toEqual(height); expect(bbox).toBe(result); }); - it('computes screen space bounding box with vertical origin', function() { + it('computes screen space bounding box with vertical origin center', function() { var scale = 1.5; var label = labels.add({ @@ -937,8 +955,10 @@ defineSuite([ }); scene.renderForSpecs(); - var width = 0; - var height = 0; + var x = Number.POSITIVE_INFINITY; + var y = Number.POSITIVE_INFINITY; + var maxX = 0; + var maxY = 0; var glyphs = label._glyphs; var length = glyphs.length; @@ -949,25 +969,113 @@ defineSuite([ continue; } - width += billboard.width; - height = Math.max(height, billboard.height); + var glyphWidth = billboard.width * scale; + var glyphHeight = billboard.height * scale; + var glyphX = billboard._translate.x; + var glyphY = -(billboard._translate.y + glyphHeight * 0.5); + + x = Math.min(x, glyphX); + y = Math.min(y, glyphY); + maxX = Math.max(maxX, glyphX + glyphWidth); + maxY = Math.max(maxY, glyphY + glyphHeight); } - width *= scale; - height *= scale; + var width = maxX - x; + var height = maxY - y; + + var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, scene); + expect(bbox.x).toEqual(x); + expect(bbox.y).toEqual(y); + expect(bbox.width).toEqual(width); + expect(bbox.height).toEqual(height); + }); + + it('computes screen space bounding box with vertical origin top', function() { + var scale = 1.5; + + var label = labels.add({ + text : 'abc', + scale : scale, + verticalOrigin : VerticalOrigin.TOP + }); + scene.renderForSpecs(); + + var x = Number.POSITIVE_INFINITY; + var y = Number.POSITIVE_INFINITY; + var maxX = 0; + var maxY = 0; + + var glyphs = label._glyphs; + var length = glyphs.length; + for (var i = 0; i < length; ++i) { + var glyph = glyphs[i]; + var billboard = glyph.billboard; + if (!defined(billboard)) { + continue; + } + + var glyphWidth = billboard.width * scale; + var glyphHeight = billboard.height * scale; + var glyphX = billboard._translate.x; + var glyphY = -billboard._translate.y; + + x = Math.min(x, glyphX); + y = Math.min(y, glyphY); + maxX = Math.max(maxX, glyphX + glyphWidth); + maxY = Math.max(maxY, glyphY + glyphHeight); + } - var halfHeight = height * 0.5; + var width = maxX - x; + var height = maxY - y; - var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); - expect(bbox.x).toEqual(0); - expect(bbox.y).toEqual(-halfHeight); + var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, scene); + expect(bbox.x).toEqual(x); + expect(bbox.y).toEqual(y); expect(bbox.width).toEqual(width); expect(bbox.height).toEqual(height); + }); - label.verticalOrigin = VerticalOrigin.TOP; - bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); - expect(bbox.x).toEqual(0); - expect(bbox.y).toEqual(-height); + it('computes screen space bounding box with vertical origin baseline', function() { + var scale = 1.5; + + var label = labels.add({ + text : 'abc', + scale : scale, + verticalOrigin : VerticalOrigin.BASELINE + }); + scene.renderForSpecs(); + + var x = Number.POSITIVE_INFINITY; + var y = Number.POSITIVE_INFINITY; + var maxX = 0; + var maxY = 0; + + var glyphs = label._glyphs; + var length = glyphs.length; + for (var i = 0; i < length; ++i) { + var glyph = glyphs[i]; + var billboard = glyph.billboard; + if (!defined(billboard)) { + continue; + } + + var glyphWidth = billboard.width * scale; + var glyphHeight = billboard.height * scale; + var glyphX = billboard._translate.x; + var glyphY = -(billboard._translate.y + glyphHeight); + + x = Math.min(x, glyphX); + y = Math.min(y, glyphY); + maxX = Math.max(maxX, glyphX + glyphWidth); + maxY = Math.max(maxY, glyphY + glyphHeight); + } + + var width = maxX - x; + var height = maxY - y; + + var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, scene); + expect(bbox.x).toEqual(x); + expect(bbox.y).toEqual(y); expect(bbox.width).toEqual(width); expect(bbox.height).toEqual(height); }); @@ -982,8 +1090,10 @@ defineSuite([ }); scene.renderForSpecs(); - var width = 0; - var height = 0; + var x = Number.POSITIVE_INFINITY; + var y = Number.POSITIVE_INFINITY; + var maxX = 0; + var maxY = 0; var glyphs = label._glyphs; var length = glyphs.length; @@ -994,25 +1104,33 @@ defineSuite([ continue; } - width += billboard.width; - height = Math.max(height, billboard.height); + var glyphWidth = billboard.width * scale; + var glyphHeight = billboard.height * scale; + var glyphX = billboard._translate.x; + var glyphY = -(billboard._translate.y + glyphHeight); + + x = Math.min(x, glyphX); + y = Math.min(y, glyphY); + maxX = Math.max(maxX, glyphX + glyphWidth); + maxY = Math.max(maxY, glyphY + glyphHeight); } - width *= scale; - height *= scale; + var width = maxX - x; + var height = maxY - y; var halfWidth = width * 0.5; - var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); + var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, scene); expect(bbox.x).toEqual(-halfWidth); - expect(bbox.y).toEqual(0); + expect(bbox.y).toEqual(y); expect(bbox.width).toEqual(width); expect(bbox.height).toEqual(height); label.horizontalOrigin = HorizontalOrigin.RIGHT; - bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); + scene.renderForSpecs(); + bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, scene); expect(bbox.x).toEqual(-width); - expect(bbox.y).toEqual(0); + expect(bbox.y).toEqual(y); expect(bbox.width).toEqual(width); expect(bbox.height).toEqual(height); }); From a48848b3579c72bf51d1f3fd9c36ef2a5604ecc2 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Fri, 2 Dec 2016 17:16:04 -0500 Subject: [PATCH 19/34] Scene parameter not needed, can get resolutionScale from collection. --- Source/DataSources/EntityCluster.js | 2 +- Source/Scene/Label.js | 5 ++--- Specs/Scene/LabelCollectionSpec.js | 14 +++++++------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Source/DataSources/EntityCluster.js b/Source/DataSources/EntityCluster.js index 5facb35096a5..b926038278a5 100644 --- a/Source/DataSources/EntityCluster.js +++ b/Source/DataSources/EntityCluster.js @@ -106,7 +106,7 @@ define([ function getBoundingBox(item, coord, pixelRange, entityCluster, result) { if (defined(item._labelCollection) && entityCluster._clusterLabels) { - result = Label.getScreenSpaceBoundingBox(item, coord, entityCluster._scene, result); + result = Label.getScreenSpaceBoundingBox(item, coord, result); } else if (defined(item._billboardCollection) && entityCluster._clusterBillboards) { result = Billboard.getScreenSpaceBoundingBox(item, coord, result); } else if (defined(item._pointPrimitiveCollection) && entityCluster._clusterPoints) { diff --git a/Source/Scene/Label.js b/Source/Scene/Label.js index 3e5f2e64be92..b50ffaf36029 100644 --- a/Source/Scene/Label.js +++ b/Source/Scene/Label.js @@ -951,19 +951,18 @@ define([ * Gets a label's screen space bounding box centered around screenSpacePosition. * @param {Label} label The label to get the screen space bounding box for. * @param {Cartesian2} screenSpacePosition The screen space center of the label. - * @param {Scene} scene The scene the label is in. * @param {BoundingRectangle} [result] The object onto which to store the result. * @returns {BoundingRectangle} The screen space bounding box. * * @private */ - Label.getScreenSpaceBoundingBox = function(label, screenSpacePosition, scene, result) { + Label.getScreenSpaceBoundingBox = function(label, screenSpacePosition, result) { var x = 0; var y = 0; var width = 0; var height = 0; var scale = label.scale; - var resolutionScale = scene.context.uniformState.resolutionScale; + var resolutionScale = label._labelCollection._resolutionScale; var backgroundBillboard = label._backgroundBillboard; if (defined(backgroundBillboard)) { diff --git a/Specs/Scene/LabelCollectionSpec.js b/Specs/Scene/LabelCollectionSpec.js index 65d20bcba2dd..d36ab7eb1db3 100644 --- a/Specs/Scene/LabelCollectionSpec.js +++ b/Specs/Scene/LabelCollectionSpec.js @@ -892,7 +892,7 @@ defineSuite([ var width = maxX - x; var height = maxY - y; - var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, scene); + var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); expect(bbox.x).toEqual(x); expect(bbox.y).toEqual(y); expect(bbox.width).toEqual(width); @@ -937,7 +937,7 @@ defineSuite([ var height = maxY - y; var result = new BoundingRectangle(); - var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, scene, result); + var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, result); expect(bbox.x).toEqual(x); expect(bbox.y).toEqual(y); expect(bbox.width).toEqual(width); @@ -983,7 +983,7 @@ defineSuite([ var width = maxX - x; var height = maxY - y; - var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, scene); + var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); expect(bbox.x).toEqual(x); expect(bbox.y).toEqual(y); expect(bbox.width).toEqual(width); @@ -1028,7 +1028,7 @@ defineSuite([ var width = maxX - x; var height = maxY - y; - var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, scene); + var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); expect(bbox.x).toEqual(x); expect(bbox.y).toEqual(y); expect(bbox.width).toEqual(width); @@ -1073,7 +1073,7 @@ defineSuite([ var width = maxX - x; var height = maxY - y; - var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, scene); + var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); expect(bbox.x).toEqual(x); expect(bbox.y).toEqual(y); expect(bbox.width).toEqual(width); @@ -1120,7 +1120,7 @@ defineSuite([ var halfWidth = width * 0.5; - var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, scene); + var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); expect(bbox.x).toEqual(-halfWidth); expect(bbox.y).toEqual(y); expect(bbox.width).toEqual(width); @@ -1128,7 +1128,7 @@ defineSuite([ label.horizontalOrigin = HorizontalOrigin.RIGHT; scene.renderForSpecs(); - bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, scene); + bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); expect(bbox.x).toEqual(-width); expect(bbox.y).toEqual(y); expect(bbox.width).toEqual(width); From c21d922055920b22839de9dcf63296754f71e497 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Fri, 2 Dec 2016 20:45:01 -0500 Subject: [PATCH 20/34] Add tests. --- Specs/Scene/LabelCollectionSpec.js | 138 ++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 3 deletions(-) diff --git a/Specs/Scene/LabelCollectionSpec.js b/Specs/Scene/LabelCollectionSpec.js index d36ab7eb1db3..61b1470ab822 100644 --- a/Specs/Scene/LabelCollectionSpec.js +++ b/Specs/Scene/LabelCollectionSpec.js @@ -82,6 +82,9 @@ defineSuite([ expect(label.fillColor).toEqual(Color.WHITE); expect(label.outlineColor).toEqual(Color.BLACK); expect(label.outlineWidth).toEqual(1); + expect(label.showBackground).toEqual(false); + expect(label.backgroundColor).toEqual(new Color(0.165, 0.165, 0.165, 0.8)); + expect(label.backgroundPadding).toEqual(new Cartesian2(2, 2)); expect(label.style).toEqual(LabelStyle.FILL); expect(label.pixelOffset).toEqual(Cartesian2.ZERO); expect(label.eyeOffset).toEqual(Cartesian3.ZERO); @@ -120,6 +123,9 @@ defineSuite([ var horizontalOrigin = HorizontalOrigin.LEFT; var verticalOrigin = VerticalOrigin.BOTTOM; var scale = 2.0; + var showBackground = true; + var backgroundColor = Color.BLUE; + var backgroundPadding = new Cartesian2(11, 12); var translucency = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0); var pixelOffsetScale = new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0); var distanceDisplayCondition = new DistanceDisplayCondition(10.0, 100.0); @@ -132,6 +138,9 @@ defineSuite([ outlineColor : outlineColor, outlineWidth : outlineWidth, style : style, + showBackground : showBackground, + backgroundColor : backgroundColor, + backgroundPadding : backgroundPadding, pixelOffset : pixelOffset, eyeOffset : eyeOffset, horizontalOrigin : horizontalOrigin, @@ -151,6 +160,9 @@ defineSuite([ expect(label.outlineColor).toEqual(outlineColor); expect(label.outlineWidth).toEqual(outlineWidth); expect(label.style).toEqual(style); + expect(label.showBackground).toEqual(showBackground); + expect(label.backgroundColor).toEqual(backgroundColor); + expect(label.backgroundPadding).toEqual(backgroundPadding); expect(label.pixelOffset).toEqual(pixelOffset); expect(label.eyeOffset).toEqual(eyeOffset); expect(label.horizontalOrigin).toEqual(horizontalOrigin); @@ -430,6 +442,22 @@ defineSuite([ expect(scene.renderForSpecs()[0]).toBeGreaterThan(10); }); + it('can render a label background', function() { + var label = labels.add({ + position : Cartesian3.ZERO, + text : '_', + horizontalOrigin : HorizontalOrigin.CENTER, + verticalOrigin : VerticalOrigin.CENTER, + showBackground : true, + backgroundColor : Color.BLUE + }); + + expect(scene.renderForSpecs()).toEqual([0, 0, 255, 255]); + + labels.remove(label); + expect(scene.renderForSpecs()).toEqual([0, 0, 0, 255]); + }); + it('does not render labels with show set to false', function() { var label = labels.add({ position : Cartesian3.ZERO, @@ -447,6 +475,25 @@ defineSuite([ expect(scene.renderForSpecs()[0]).toBeGreaterThan(10); }); + it('does not render label background with show set to false', function() { + var label = labels.add({ + position : Cartesian3.ZERO, + text : '_', + horizontalOrigin : HorizontalOrigin.CENTER, + verticalOrigin : VerticalOrigin.CENTER, + showBackground : true, + backgroundColor : Color.BLUE + }); + + expect(scene.renderForSpecs()).toEqual([0, 0, 255, 255]); + + label.show = false; + expect(scene.renderForSpecs()).toEqual([0, 0, 0, 255]); + + label.show = true; + expect(scene.renderForSpecs()).toEqual([0, 0, 255, 255]); + }); + it('does not render labels that are behind the viewer', function() { var label = labels.add({ position : new Cartesian3(20.0, 0.0, 0.0), // Behind camera @@ -719,14 +766,37 @@ defineSuite([ }); scene.renderForSpecs(); expect(labels._billboardCollection.length).toEqual(3); + expect(labels._spareBillboards.length).toEqual(0); label.text = 'a'; scene.renderForSpecs(); expect(labels._billboardCollection.length).toEqual(3); + expect(labels._spareBillboards.length).toEqual(2); label.text = 'def'; scene.renderForSpecs(); expect(labels._billboardCollection.length).toEqual(3); + expect(labels._spareBillboards.length).toEqual(0); + }); + + it('should reuse background billboards that are not needed any more', function() { + var label = labels.add({ + text : 'abc', + showBackground : true + }); + scene.renderForSpecs(); + expect(labels._backgroundBillboardCollection.length).toEqual(1); + expect(labels._spareBackgroundBillboards.length).toEqual(0); + + label.showBackground = false; + scene.renderForSpecs(); + expect(labels._backgroundBillboardCollection.length).toEqual(1); + expect(labels._spareBackgroundBillboards.length).toEqual(1); + + label.showBackground = true; + scene.renderForSpecs(); + expect(labels._backgroundBillboardCollection.length).toEqual(1); + expect(labels._spareBackgroundBillboards.length).toEqual(0); }); describe('Label', function() { @@ -1135,6 +1205,40 @@ defineSuite([ expect(bbox.height).toEqual(height); }); + it('computes screen space bounding box with padded background', function() { + var scale = 1.5; + + var label = labels.add({ + text : 'abc', + scale : scale, + showBackground : true, + backgroundPadding : new Cartesian2(15, 10) + }); + scene.renderForSpecs(); + + var backgroundBillboard = label._backgroundBillboard; + var width = backgroundBillboard.width * scale; + var height = backgroundBillboard.height * scale; + var x = backgroundBillboard._translate.x; + var y = -(backgroundBillboard._translate.y + height); + + var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); + expect(bbox.x).toEqual(x); + expect(bbox.y).toEqual(y); + expect(bbox.width).toEqual(width); + expect(bbox.height).toEqual(height); + + label.verticalOrigin = VerticalOrigin.CENTER; + scene.renderForSpecs(); + y = -(backgroundBillboard._translate.y + height * 0.5); + + bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); + expect(bbox.x).toEqual(x); + expect(bbox.y).toEqual(y); + expect(bbox.width).toEqual(width); + expect(bbox.height).toEqual(height); + }); + it('can equal another label', function() { var label = labels.add({ position : new Cartesian3(1.0, 2.0, 3.0), @@ -1213,7 +1317,7 @@ defineSuite([ var eyeOffset1 = new Cartesian3(6.0, 7.0, 8.0); var eyeOffset2 = new Cartesian3(16.0, 17.0, 18.0); var verticalOrigin1 = VerticalOrigin.TOP; - var verticalOrigin2 = VerticalOrigin.BOTTOM; + var verticalOrigin2 = VerticalOrigin.BASELINE; var scale1 = 2.0; var scale2 = 3.0; var id1 = 'id1'; @@ -1232,7 +1336,8 @@ defineSuite([ scale : scale1, id : id1, translucencyByDistance : translucency1, - pixelOffsetScaleByDistance : pixelOffsetScale1 + pixelOffsetScaleByDistance : pixelOffsetScale1, + showBackground : true }); scene.renderForSpecs(); @@ -1279,7 +1384,8 @@ defineSuite([ scale : 2.0, id : 'id1', translucencyByDistance : new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0), - pixelOffsetScaleByDistance : new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0) + pixelOffsetScaleByDistance : new NearFarScalar(1.0e4, 1.0, 1.0e6, 0.0), + showBackground : true }); scene.renderForSpecs(); }); @@ -1347,6 +1453,26 @@ defineSuite([ }); }); + it('showBackground', function() { + expect(label.showBackground).toEqual(true); + label.showBackground = false; + expect(label.showBackground).toEqual(false); + }); + + it('backgroundColor', function() { + var newValue = Color.RED; + expect(label.backgroundColor).not.toEqual(newValue); + label.backgroundColor = newValue; + expect(label.backgroundColor).toEqual(newValue); + }); + + it('backgroundPadding', function() { + var newValue = new Cartesian2(8, 5); + expect(label.backgroundPadding).not.toEqual(newValue); + label.backgroundPadding = newValue; + expect(label.backgroundPadding).toEqual(newValue); + }); + it('id', function() { var newValue = 'id2'; expect(label.id).not.toEqual(newValue); @@ -1401,6 +1527,12 @@ defineSuite([ expect(billboard.pixelOffsetScaleByDistance).toEqual(label.pixelOffsetScaleByDistance); }); }); + + it('clusterShow', function() { + expect(label.clusterShow).toEqual(true); + label.clusterShow = false; + expect(label.clusterShow).toEqual(false); + }); }); it('should set vertexTranslate of billboards correctly when vertical origin is changed', function() { From 981138fecd942b61f340133ace8d25a5f9a8fb33 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Sat, 3 Dec 2016 12:02:58 -0500 Subject: [PATCH 21/34] Documentation update --- Source/Scene/HorizontalOrigin.js | 8 +++++--- Source/Scene/Label.js | 6 +++--- Source/Scene/VerticalOrigin.js | 10 ++++++---- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Source/Scene/HorizontalOrigin.js b/Source/Scene/HorizontalOrigin.js index 17199472a51d..805c1db223b2 100644 --- a/Source/Scene/HorizontalOrigin.js +++ b/Source/Scene/HorizontalOrigin.js @@ -6,13 +6,15 @@ define([ 'use strict'; /** - * The horizontal location of an origin relative to an object, e.g., a {@link Billboard}. - * For example, the horizontal origin is used to display a billboard to the left or right (in - * screen space) of the actual position. + * The horizontal location of an origin relative to an object, e.g., a {@link Billboard} + * or {@link Label}. For example, setting the horizontal origin to LEFT + * or RIGHT will display a billboard to the left or right (in screen space) + * of the anchor position. * * @exports HorizontalOrigin * * @see Billboard#horizontalOrigin + * @see Label#horizontalOrigin */ var HorizontalOrigin = { /** diff --git a/Source/Scene/Label.js b/Source/Scene/Label.js index b50ffaf36029..e2bf4f2d4fa1 100644 --- a/Source/Scene/Label.js +++ b/Source/Scene/Label.js @@ -376,7 +376,7 @@ define([ }, /** - * Gets or sets the background color of this label. TRANSPARENT means no background will be used. + * Gets or sets the background color of this label. * @memberof Label.prototype * @type {Color} */ @@ -404,8 +404,8 @@ define([ }, /** - * Gets or sets the background padding, in pixels, of this label. The x value - * controls horizontal padding, and the y value controls vertical padding. + * Gets or sets the background padding, in pixels, of this label. The x value + * controls horizontal padding, and the y value controls vertical padding. * @memberof Label.prototype * @type {Cartesian2} */ diff --git a/Source/Scene/VerticalOrigin.js b/Source/Scene/VerticalOrigin.js index b810ddc85c66..3d6022c6d2d3 100644 --- a/Source/Scene/VerticalOrigin.js +++ b/Source/Scene/VerticalOrigin.js @@ -6,17 +6,19 @@ define([ 'use strict'; /** - * The vertical location of an origin relative to an object, e.g., a {@link Billboard}. - * For example, the vertical origin is used to display a billboard above or below (in - * screen space) of the actual position. + * The vertical location of an origin relative to an object, e.g., a {@link Billboard} + * or {@link Label}. For example, setting the vertical origin to TOP + * or BOTTOM will display a billboard above or below (in screen space) + * of the anchor position. * * @exports VerticalOrigin * * @see Billboard#verticalOrigin + * @see Label#verticalOrigin */ var VerticalOrigin = { /** - * The origin is at the vertical center of the object. + * The origin is at the vertical center between BASELINE and TOP. * * @type {Number} * @constant From ac8e0d21ea4dc381823bf704a58e9626f29202bb Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Sat, 3 Dec 2016 21:24:55 -0500 Subject: [PATCH 22/34] More doc updates, change default label primitive origin to BASELINE. --- .../Images/Billboard.setVerticalOrigin.png | Bin 107923 -> 81239 bytes Source/Scene/Billboard.js | 7 ++++--- Source/Scene/BillboardCollection.js | 13 ++++++++++++- Source/Scene/HorizontalOrigin.js | 4 ++++ Source/Scene/Label.js | 8 ++++---- Source/Scene/LabelCollection.js | 9 ++++++--- Source/Scene/VerticalOrigin.js | 6 +++++- Specs/Scene/LabelCollectionSpec.js | 2 +- 8 files changed, 36 insertions(+), 13 deletions(-) diff --git a/Documentation/Images/Billboard.setVerticalOrigin.png b/Documentation/Images/Billboard.setVerticalOrigin.png index a08e5f8854fc0eba1a03c9286322ee5aab266634..2b49bc06e0f65d2632550b60403457201a7c0af3 100644 GIT binary patch literal 81239 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 LNkvXXu0mjf*tV5> diff --git a/Source/Scene/Billboard.js b/Source/Scene/Billboard.js index 5b50dfe4c7e6..f50cd4eca1ca 100644 --- a/Source/Scene/Billboard.js +++ b/Source/Scene/Billboard.js @@ -194,6 +194,7 @@ define([ * of removing it and re-adding it to the collection. * @memberof Billboard.prototype * @type {Boolean} + * @default true */ show : { get : function() { @@ -468,7 +469,7 @@ define([ /** * Gets or sets the horizontal origin of this billboard, which determines if the billboard is - * to the left, center, or right of its position. + * to the left, center, or right of its anchor position. *

*
*
@@ -500,10 +501,10 @@ define([ /** * Gets or sets the vertical origin of this billboard, which determines if the billboard is - * to the above, below, or at the center of its position. + * to the above, below, or at the center of its anchor position. *

*
- *
+ *
*
* @memberof Billboard.prototype * @type {VerticalOrigin} diff --git a/Source/Scene/BillboardCollection.js b/Source/Scene/BillboardCollection.js index e269f5a96445..523728ffa6ac 100644 --- a/Source/Scene/BillboardCollection.js +++ b/Source/Scene/BillboardCollection.js @@ -406,12 +406,23 @@ define([ * position : Cesium.Cartesian3.ZERO, * pixelOffset : Cesium.Cartesian2.ZERO, * eyeOffset : Cesium.Cartesian3.ZERO, + * heightReference : Cesium.HeightReference.NONE, * horizontalOrigin : Cesium.HorizontalOrigin.CENTER, * verticalOrigin : Cesium.VerticalOrigin.CENTER, * scale : 1.0, * image : 'url/to/image', + * imageSubRegion : undefined, * color : Cesium.Color.WHITE, - * id : undefined + * id : undefined, + * rotation : 0.0, + * alignedAxis : Cesium.Cartesian3.ZERO, + * width : undefined, + * height : undefined, + * scaleByDistance : undefined, + * translucencyByDistance : undefined, + * pixelOffsetScaleByDistance : undefined, + * sizeInMeters : false, + * distanceDisplayCondition : undefined * }); * * @example diff --git a/Source/Scene/HorizontalOrigin.js b/Source/Scene/HorizontalOrigin.js index 805c1db223b2..db1737fcf688 100644 --- a/Source/Scene/HorizontalOrigin.js +++ b/Source/Scene/HorizontalOrigin.js @@ -10,6 +10,10 @@ define([ * or {@link Label}. For example, setting the horizontal origin to LEFT * or RIGHT will display a billboard to the left or right (in screen space) * of the anchor position. + *

+ *
+ *
+ *
* * @exports HorizontalOrigin * diff --git a/Source/Scene/Label.js b/Source/Scene/Label.js index e2bf4f2d4fa1..ceca9e14094c 100644 --- a/Source/Scene/Label.js +++ b/Source/Scene/Label.js @@ -90,7 +90,7 @@ define([ this._backgroundColor = defaultValue(options.backgroundColor, new Color(0.165, 0.165, 0.165, 0.8)); this._backgroundPadding = defaultValue(options.backgroundPadding, new Cartesian2(2, 2)); this._style = defaultValue(options.style, LabelStyle.FILL); - this._verticalOrigin = defaultValue(options.verticalOrigin, VerticalOrigin.BOTTOM); + this._verticalOrigin = defaultValue(options.verticalOrigin, VerticalOrigin.BASELINE); this._horizontalOrigin = defaultValue(options.horizontalOrigin, HorizontalOrigin.LEFT); this._pixelOffset = Cartesian2.clone(defaultValue(options.pixelOffset, Cartesian2.ZERO)); this._eyeOffset = Cartesian3.clone(defaultValue(options.eyeOffset, Cartesian3.ZERO)); @@ -656,7 +656,7 @@ define([ /** * Gets or sets the horizontal origin of this label, which determines if the label is drawn - * to the left, center, or right of its position. + * to the left, center, or right of its anchor position. *

*
*
@@ -688,10 +688,10 @@ define([ /** * Gets or sets the vertical origin of this label, which determines if the label is - * to the above, below, or at the center of its position. + * to the above, below, or at the center of its anchor position. *

*
- *
+ *
*
* @memberof Label.prototype * @type {VerticalOrigin} diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index 6a79b67bcd7d..8edc0a29ce13 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -440,7 +440,6 @@ define([ this.__backgroundTextureAtlas = undefined; this._whitePixelIndex = undefined; - // TODO: Don't allocate this until later. this._backgroundBillboardCollection = new BillboardCollection({ scene : this._scene }); @@ -550,8 +549,12 @@ define([ * pixelOffset : Cesium.Cartesian2.ZERO, * eyeOffset : Cesium.Cartesian3.ZERO, * horizontalOrigin : Cesium.HorizontalOrigin.LEFT, - * verticalOrigin : Cesium.VerticalOrigin.BOTTOM, - * scale : 1.0 + * verticalOrigin : Cesium.VerticalOrigin.BASELINE, + * scale : 1.0, + * translucencyByDistance : undefined, + * pixelOffsetScaleByDistance : undefined, + * heightReference : HeightReference.NONE, + * distanceDisplayCondition : undefined * }); * * @example diff --git a/Source/Scene/VerticalOrigin.js b/Source/Scene/VerticalOrigin.js index 3d6022c6d2d3..1e109364d60f 100644 --- a/Source/Scene/VerticalOrigin.js +++ b/Source/Scene/VerticalOrigin.js @@ -9,7 +9,11 @@ define([ * The vertical location of an origin relative to an object, e.g., a {@link Billboard} * or {@link Label}. For example, setting the vertical origin to TOP * or BOTTOM will display a billboard above or below (in screen space) - * of the anchor position. + * the anchor position. + *

+ *
+ *
+ *
* * @exports VerticalOrigin * diff --git a/Specs/Scene/LabelCollectionSpec.js b/Specs/Scene/LabelCollectionSpec.js index 61b1470ab822..f573c8458b07 100644 --- a/Specs/Scene/LabelCollectionSpec.js +++ b/Specs/Scene/LabelCollectionSpec.js @@ -90,7 +90,7 @@ defineSuite([ expect(label.eyeOffset).toEqual(Cartesian3.ZERO); expect(label.heightReference).toEqual(HeightReference.NONE); expect(label.horizontalOrigin).toEqual(HorizontalOrigin.LEFT); - expect(label.verticalOrigin).toEqual(VerticalOrigin.BOTTOM); + expect(label.verticalOrigin).toEqual(VerticalOrigin.BASELINE); expect(label.scale).toEqual(1.0); expect(label.id).not.toBeDefined(); expect(label.translucencyByDistance).not.toBeDefined(); From 178d71253977472bcc1934705e26151e453da939 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Sat, 3 Dec 2016 22:21:37 -0500 Subject: [PATCH 23/34] Add label to Ground Clamping example. Somehow this has exposed a serious bug with removing a label that has both a label background and a height reference. Further investigation is needed. --- Apps/Sandcastle/gallery/Ground Clamping.html | 37 ++++++++++++-------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/Apps/Sandcastle/gallery/Ground Clamping.html b/Apps/Sandcastle/gallery/Ground Clamping.html index d4081e1d8b1f..6729403d358c 100644 --- a/Apps/Sandcastle/gallery/Ground Clamping.html +++ b/Apps/Sandcastle/gallery/Ground Clamping.html @@ -10,10 +10,10 @@ @@ -29,8 +29,8 @@
From f330bf3db6d6bca911109d29f320cbd8dc0d7cfa Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Tue, 6 Dec 2016 14:12:29 -0500 Subject: [PATCH 24/34] Update default padding to be larger. --- Apps/Sandcastle/gallery/Terrain.html | 3 ++- Source/DataSources/LabelGraphics.js | 4 ++-- Source/DataSources/LabelVisualizer.js | 2 +- Source/Scene/Label.js | 2 +- Source/Scene/LabelCollection.js | 4 ++-- Specs/Scene/LabelCollectionSpec.js | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Apps/Sandcastle/gallery/Terrain.html b/Apps/Sandcastle/gallery/Terrain.html index 0d78a6bfda0e..09eedd03e1c4 100644 --- a/Apps/Sandcastle/gallery/Terrain.html +++ b/Apps/Sandcastle/gallery/Terrain.html @@ -154,7 +154,8 @@ fillColor : Cesium.Color.BLACK, outlineColor : Cesium.Color.BLACK, showBackground : true, - backgroundColor : new Cesium.Color(0.9, 0.9, 0.9, 0.7) + backgroundColor : new Cesium.Color(0.9, 0.9, 0.9, 0.7), + backgroundPadding : new Cesium.Cartesian2(4, 3) } }); } diff --git a/Source/DataSources/LabelGraphics.js b/Source/DataSources/LabelGraphics.js index 2898c820706b..e3aacdaca9eb 100644 --- a/Source/DataSources/LabelGraphics.js +++ b/Source/DataSources/LabelGraphics.js @@ -37,7 +37,7 @@ define([ * @param {Property} [options.show=true] A boolean Property specifying the visibility of the label. * @param {Property} [options.showBackground=true] A boolean Property specifying the visibility of the background behind the label. * @param {Property} [options.backgroundColor=new Color(0.165, 0.165, 0.165, 0.8)] A Property specifying the background {@link Color}. - * @param {Property} [options.backgroundPadding=new Cartesian2(2, 2)] A {@link Cartesian2} Property specifying the horizontal and vertical background padding in pixels. + * @param {Property} [options.backgroundPadding=new Cartesian2(7, 5)] A {@link Cartesian2} Property specifying the horizontal and vertical background padding in pixels. * @param {Property} [options.scale=1.0] A numeric Property specifying the scale to apply to the text. * @param {Property} [options.horizontalOrigin=HorizontalOrigin.CENTER] A Property specifying the {@link HorizontalOrigin}. * @param {Property} [options.verticalOrigin=VerticalOrigin.CENTER] A Property specifying the {@link VerticalOrigin}. @@ -262,7 +262,7 @@ define([ * background padding in pixels. * @memberof LabelGraphics.prototype * @type {Property} - * @default new Cartesian2(2, 2) + * @default new Cartesian2(7, 5) */ backgroundPadding : createPropertyDescriptor('backgroundPadding'), diff --git a/Source/DataSources/LabelVisualizer.js b/Source/DataSources/LabelVisualizer.js index 3d008f5ccd63..019e8527fc77 100644 --- a/Source/DataSources/LabelVisualizer.js +++ b/Source/DataSources/LabelVisualizer.js @@ -43,7 +43,7 @@ define([ var defaultOutlineWidth = 1.0; var defaultShowBackground = false; var defaultBackgroundColor = new Color(0.165, 0.165, 0.165, 0.8); - var defaultBackgroundPadding = new Cartesian2(2, 2); + var defaultBackgroundPadding = new Cartesian2(7, 5); var defaultPixelOffset = Cartesian2.ZERO; var defaultEyeOffset = Cartesian3.ZERO; var defaultHeightReference = HeightReference.NONE; diff --git a/Source/Scene/Label.js b/Source/Scene/Label.js index ceca9e14094c..62dd7e650fe9 100644 --- a/Source/Scene/Label.js +++ b/Source/Scene/Label.js @@ -88,7 +88,7 @@ define([ this._outlineWidth = defaultValue(options.outlineWidth, 1.0); this._showBackground = defaultValue(options.showBackground, false); this._backgroundColor = defaultValue(options.backgroundColor, new Color(0.165, 0.165, 0.165, 0.8)); - this._backgroundPadding = defaultValue(options.backgroundPadding, new Cartesian2(2, 2)); + this._backgroundPadding = defaultValue(options.backgroundPadding, new Cartesian2(7, 5)); this._style = defaultValue(options.style, LabelStyle.FILL); this._verticalOrigin = defaultValue(options.verticalOrigin, VerticalOrigin.BASELINE); this._horizontalOrigin = defaultValue(options.horizontalOrigin, HorizontalOrigin.LEFT); diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index 8edc0a29ce13..b9fb63bac5d9 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -437,7 +437,7 @@ define([ this._scene = options.scene; this._textureAtlas = undefined; - this.__backgroundTextureAtlas = undefined; + this._backgroundTextureAtlas = undefined; this._whitePixelIndex = undefined; this._backgroundBillboardCollection = new BillboardCollection({ @@ -544,7 +544,7 @@ define([ * outlineWidth : 1.0, * showBackground : false, * backgroundColor : new Cesium.Color(0.165, 0.165, 0.165, 0.8), - * backgroundPadding : new Cesium.Cartesian2(2, 2), + * backgroundPadding : new Cesium.Cartesian2(7, 5), * style : Cesium.LabelStyle.FILL, * pixelOffset : Cesium.Cartesian2.ZERO, * eyeOffset : Cesium.Cartesian3.ZERO, diff --git a/Specs/Scene/LabelCollectionSpec.js b/Specs/Scene/LabelCollectionSpec.js index f573c8458b07..e97bb7631c0a 100644 --- a/Specs/Scene/LabelCollectionSpec.js +++ b/Specs/Scene/LabelCollectionSpec.js @@ -84,7 +84,7 @@ defineSuite([ expect(label.outlineWidth).toEqual(1); expect(label.showBackground).toEqual(false); expect(label.backgroundColor).toEqual(new Color(0.165, 0.165, 0.165, 0.8)); - expect(label.backgroundPadding).toEqual(new Cartesian2(2, 2)); + expect(label.backgroundPadding).toEqual(new Cartesian2(7, 5)); expect(label.style).toEqual(LabelStyle.FILL); expect(label.pixelOffset).toEqual(Cartesian2.ZERO); expect(label.eyeOffset).toEqual(Cartesian3.ZERO); From 4b8c2567299a43c943e43fcb6e7afef6de5e5beb Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Tue, 6 Dec 2016 14:22:21 -0500 Subject: [PATCH 25/34] Add defaults to documentation. --- Source/Scene/Label.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Source/Scene/Label.js b/Source/Scene/Label.js index 62dd7e650fe9..c743892f782d 100644 --- a/Source/Scene/Label.js +++ b/Source/Scene/Label.js @@ -124,6 +124,7 @@ define([ * of removing it and re-adding it to the collection. * @memberof Label.prototype * @type {Boolean} + * @default true */ show : { get : function() { @@ -197,6 +198,7 @@ define([ * Gets or sets the height reference of this billboard. * @memberof Label.prototype * @type {HeightReference} + * @default HeightReference.NONE */ heightReference : { get : function() { @@ -258,6 +260,7 @@ define([ * Gets or sets the font used to draw this label. Fonts are specified using the same syntax as the CSS 'font' property. * @memberof Label.prototype * @type {String} + * @default '30px sans-serif' * @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#text-styles|HTML canvas 2D context text styles} */ font : { @@ -282,6 +285,7 @@ define([ * Gets or sets the fill color of this label. * @memberof Label.prototype * @type {Color} + * @default Color.WHITE * @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#fill-and-stroke-styles|HTML canvas 2D context fill and stroke styles} */ fillColor : { @@ -307,6 +311,7 @@ define([ * Gets or sets the outline color of this label. * @memberof Label.prototype * @type {Color} + * @default Color.BLACK * @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#fill-and-stroke-styles|HTML canvas 2D context fill and stroke styles} */ outlineColor : { @@ -332,6 +337,7 @@ define([ * Gets or sets the outline width of this label. * @memberof Label.prototype * @type {Number} + * @default 1.0 * @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#fill-and-stroke-styles|HTML canvas 2D context fill and stroke styles} */ outlineWidth : { @@ -355,6 +361,7 @@ define([ /** * Determines if a background behind this label will be shown. * @memberof Label.prototype + * @default false * @type {Boolean} */ showBackground : { @@ -379,6 +386,7 @@ define([ * Gets or sets the background color of this label. * @memberof Label.prototype * @type {Color} + * @default new Color(0.165, 0.165, 0.165, 0.8) */ backgroundColor : { get : function() { @@ -408,6 +416,7 @@ define([ * controls horizontal padding, and the y value controls vertical padding. * @memberof Label.prototype * @type {Cartesian2} + * @default new Cartesian2(7, 5) */ backgroundPadding : { get : function() { @@ -432,6 +441,7 @@ define([ * Gets or sets the style of this label. * @memberof Label.prototype * @type {LabelStyle} + * @default LabelStyle.FILL */ style : { get : function() { @@ -466,6 +476,7 @@ define([ *
* @memberof Label.prototype * @type {Cartesian2} + * @default Cartesian2.ZERO */ pixelOffset : { get : function() { @@ -623,6 +634,7 @@ define([ * * @memberof Label.prototype * @type {Cartesian3} + * @default Cartesian3.ZERO */ eyeOffset : { get : function() { @@ -663,6 +675,7 @@ define([ * * @memberof Label.prototype * @type {HorizontalOrigin} + * @default HorizontalOrigin.LEFT * @example * // Use a top, right origin * l.horizontalOrigin = Cesium.HorizontalOrigin.RIGHT; @@ -695,6 +708,7 @@ define([ * * @memberof Label.prototype * @type {VerticalOrigin} + * @default VerticalOrigin.BASELINE * @example * // Use a top, right origin * l.horizontalOrigin = Cesium.HorizontalOrigin.RIGHT; @@ -747,6 +761,7 @@ define([ * * @memberof Label.prototype * @type {Number} + * @default 1.0 */ scale : { get : function() { @@ -879,6 +894,7 @@ define([ * Determines whether or not this label will be shown or hidden because it was clustered. * @memberof Label.prototype * @type {Boolean} + * @default true * @private */ clusterShow : { From 854ee56ec5b15c188d0506b8f60bedfc16d9ad04 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Tue, 6 Dec 2016 14:37:26 -0500 Subject: [PATCH 26/34] Code style change --- Source/Scene/LabelCollection.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index b9fb63bac5d9..b56a8d44db9c 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -127,16 +127,17 @@ define([ var showBackground = label._showBackground; var backgroundBillboard = label._backgroundBillboard; + var spareBackgroundBillboards = labelCollection._spareBackgroundBillboards; if (!showBackground) { if (defined(backgroundBillboard)) { backgroundBillboard.show = false; - labelCollection._spareBackgroundBillboards.push(backgroundBillboard); + spareBackgroundBillboards.push(backgroundBillboard); label._backgroundBillboard = backgroundBillboard = undefined; } } else { if (!defined(backgroundBillboard)) { - if (labelCollection._spareBackgroundBillboards.length > 0) { - backgroundBillboard = labelCollection._spareBackgroundBillboards.pop(); + if (spareBackgroundBillboards.length > 0) { + backgroundBillboard = spareBackgroundBillboards.pop(); } else { backgroundBillboard = labelCollection._backgroundBillboardCollection.add({ collection : labelCollection, @@ -237,9 +238,10 @@ define([ // if we have a texture, configure the existing billboard, or obtain one if (glyphTextureInfo.index !== -1) { var billboard = glyph.billboard; + var spareBillboards = labelCollection._spareBillboards; if (!defined(billboard)) { - if (labelCollection._spareBillboards.length > 0) { - billboard = labelCollection._spareBillboards.pop(); + if (spareBillboards.length > 0) { + billboard = spareBillboards.pop(); } else { billboard = labelCollection._billboardCollection.add({ collection : labelCollection From 53b4d35c65b6a0445b37fc193d00ff3e43ac787a Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Tue, 6 Dec 2016 15:21:18 -0500 Subject: [PATCH 27/34] Remove spare background billboard array. --- Source/Scene/LabelCollection.js | 23 ++++++++--------------- Specs/Scene/LabelCollectionSpec.js | 7 ++----- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index b56a8d44db9c..b37fd0fb3663 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -127,24 +127,19 @@ define([ var showBackground = label._showBackground; var backgroundBillboard = label._backgroundBillboard; - var spareBackgroundBillboards = labelCollection._spareBackgroundBillboards; + var backgroundBillboardCollection = labelCollection._backgroundBillboardCollection; if (!showBackground) { if (defined(backgroundBillboard)) { - backgroundBillboard.show = false; - spareBackgroundBillboards.push(backgroundBillboard); + backgroundBillboardCollection.remove(backgroundBillboard); label._backgroundBillboard = backgroundBillboard = undefined; } } else { if (!defined(backgroundBillboard)) { - if (spareBackgroundBillboards.length > 0) { - backgroundBillboard = spareBackgroundBillboards.pop(); - } else { - backgroundBillboard = labelCollection._backgroundBillboardCollection.add({ - collection : labelCollection, - image : whitePixelCanvasId, - imageSubRegion : whitePixelBoundingRegion - }); - } + backgroundBillboard = backgroundBillboardCollection.add({ + collection : labelCollection, + image : whitePixelCanvasId, + imageSubRegion : whitePixelBoundingRegion + }); label._backgroundBillboard = backgroundBillboard; } @@ -376,8 +371,7 @@ define([ unbindGlyph(labelCollection, glyphs[i]); } if (defined(label._backgroundBillboard)) { - label._backgroundBillboard.show = false; - labelCollection._spareBackgroundBillboards.push(label._backgroundBillboard); + labelCollection._backgroundBillboardCollection.remove(label._backgroundBillboard); label._backgroundBillboard = undefined; } label._labelCollection = undefined; @@ -453,7 +447,6 @@ define([ this._billboardCollection.destroyTextureAtlas = false; this._spareBillboards = []; - this._spareBackgroundBillboards = []; this._glyphTextureCache = {}; this._labels = []; this._labelsToUpdate = []; diff --git a/Specs/Scene/LabelCollectionSpec.js b/Specs/Scene/LabelCollectionSpec.js index e97bb7631c0a..5b5936314091 100644 --- a/Specs/Scene/LabelCollectionSpec.js +++ b/Specs/Scene/LabelCollectionSpec.js @@ -779,24 +779,21 @@ defineSuite([ expect(labels._spareBillboards.length).toEqual(0); }); - it('should reuse background billboards that are not needed any more', function() { + it('should not reuse background billboards that are not needed any more', function() { var label = labels.add({ text : 'abc', showBackground : true }); scene.renderForSpecs(); expect(labels._backgroundBillboardCollection.length).toEqual(1); - expect(labels._spareBackgroundBillboards.length).toEqual(0); label.showBackground = false; scene.renderForSpecs(); - expect(labels._backgroundBillboardCollection.length).toEqual(1); - expect(labels._spareBackgroundBillboards.length).toEqual(1); + expect(labels._backgroundBillboardCollection.length).toEqual(0); label.showBackground = true; scene.renderForSpecs(); expect(labels._backgroundBillboardCollection.length).toEqual(1); - expect(labels._spareBackgroundBillboards.length).toEqual(0); }); describe('Label', function() { From 3eb09e48ef595380273eb290b6f2f5525485cfe5 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Tue, 6 Dec 2016 16:46:14 -0500 Subject: [PATCH 28/34] Disable background on empty labels. --- Source/Scene/LabelCollection.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index b37fd0fb3663..2ff096b9706f 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -125,7 +125,17 @@ define([ var glyphIndex; var textIndex; - var showBackground = label._showBackground; + // if we have more glyphs than needed, unbind the extras. + if (textLength < glyphsLength) { + for (glyphIndex = textLength; glyphIndex < glyphsLength; ++glyphIndex) { + unbindGlyph(labelCollection, glyphs[glyphIndex]); + } + } + + // presize glyphs to match the new text length + glyphs.length = textLength; + + var showBackground = label._showBackground && (glyphs.length > 0); var backgroundBillboard = label._backgroundBillboard; var backgroundBillboardCollection = labelCollection._backgroundBillboardCollection; if (!showBackground) { @@ -159,16 +169,6 @@ define([ backgroundBillboard.distanceDisplayCondition = label._distanceDisplayCondition; } - // if we have more glyphs than needed, unbind the extras. - if (textLength < glyphsLength) { - for (glyphIndex = textLength; glyphIndex < glyphsLength; ++glyphIndex) { - unbindGlyph(labelCollection, glyphs[glyphIndex]); - } - } - - // presize glyphs to match the new text length - glyphs.length = textLength; - var glyphTextureCache = labelCollection._glyphTextureCache; // walk the text looking for new characters (creating new glyphs for each) @@ -346,7 +346,7 @@ define([ } } - if (defined(backgroundBillboard)) { + if (defined(backgroundBillboard) && (glyphLength > 0)) { glyphPixelOffset.x = (widthOffset - backgroundPadding.x * scale) * resolutionScale; if (verticalOrigin === VerticalOrigin.BASELINE) { glyphPixelOffset.y = -backgroundPadding.y * scale - maxDescent * scale; From 05d680e3dfac2f2548dcd2a9ce3e03c0c65e648a Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Tue, 6 Dec 2016 17:25:48 -0500 Subject: [PATCH 29/34] Use backgrounds a bit more in Sandcastle demos. --- .../gallery/CZML Billboard and Label.html | 20 +++++++++---------- Apps/Sandcastle/gallery/Labels.html | 2 ++ .../gallery/development/Labels.html | 19 +++++++++++++----- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/Apps/Sandcastle/gallery/CZML Billboard and Label.html b/Apps/Sandcastle/gallery/CZML Billboard and Label.html index dc6e1521d663..37889f6d28ca 100644 --- a/Apps/Sandcastle/gallery/CZML Billboard and Label.html +++ b/Apps/Sandcastle/gallery/CZML Billboard and Label.html @@ -42,19 +42,19 @@ }, "label" : { "fillColor" : { - "rgba" : [0, 255, 255, 255] + "rgba" : [255, 255, 255, 255] }, - "font" : "11pt Lucida Console", + "font" : "12pt Lucida Console", "horizontalOrigin" : "LEFT", - "outlineColor" : { - "rgba":[0, 0, 0, 255] - }, - "outlineWidth" : 2, "pixelOffset" : { - "cartesian2" : [12, 0] + "cartesian2" : [8, 0] }, - "style" : "FILL_AND_OUTLINE", - "text" : "AGI" + "style" : "FILL", + "text" : "AGI", + "showBackground" : true, + "backgroundColor" : { + "rgba" : [112, 89, 57, 200] + } }, "position" : { "cartesian":[ @@ -65,7 +65,7 @@ var viewer = new Cesium.Viewer('cesiumContainer'); viewer.dataSources.add(Cesium.CzmlDataSource.load(czml)); - //Sandcastle_End +//Sandcastle_End Sandcastle.finishedLoading(); } if (typeof Cesium !== "undefined") { diff --git a/Apps/Sandcastle/gallery/Labels.html b/Apps/Sandcastle/gallery/Labels.html index 0cbe6839a575..2c4b4b4b7f96 100644 --- a/Apps/Sandcastle/gallery/Labels.html +++ b/Apps/Sandcastle/gallery/Labels.html @@ -64,6 +64,7 @@ }); entity.label.scale = 2.0; + entity.label.showBackground = true; } function offsetByDistance() { @@ -80,6 +81,7 @@ label : { text : 'Label on top of scaling billboard', font : '20px sans-serif', + showBackground : true, horizontalOrigin : Cesium.HorizontalOrigin.CENTER, pixelOffset : new Cesium.Cartesian2(0.0, -image.height), pixelOffsetScaleByDistance : new Cesium.NearFarScalar(1.5e2, 3.0, 1.5e7, 0.5) diff --git a/Apps/Sandcastle/gallery/development/Labels.html b/Apps/Sandcastle/gallery/development/Labels.html index f865fccc0f1a..d5ab916b0955 100644 --- a/Apps/Sandcastle/gallery/development/Labels.html +++ b/Apps/Sandcastle/gallery/development/Labels.html @@ -77,19 +77,27 @@ labels.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center); labels.add({ position : new Cesium.Cartesian3(0.0, 0.0, 0.0), - text : 'Center' + text : 'Center', + font : '13pt sans-serif', + showBackground : true }); labels.add({ position : new Cesium.Cartesian3(1000000.0, 0.0, 0.0), - text : 'East' + text : 'East', + font : '13pt sans-serif', + showBackground : true }); labels.add({ position : new Cesium.Cartesian3(0.0, 1000000.0, 0.0), - text : 'North' + text : 'North', + font : '13pt sans-serif', + showBackground : true }); labels.add({ position : new Cesium.Cartesian3(0.0, 0.0, 1000000.0), - text : 'Up' + text : 'Up', + font : '13pt sans-serif', + showBackground : true }); } @@ -108,8 +116,9 @@ var labels = scene.primitives.add(new Cesium.LabelCollection()); labels.add({ position : Cesium.Cartesian3.fromDegrees(-75.1641667, 39.9522222), - text : 'Label on top of scaling billboard', + text : 'Label on top of scaling billboard', font : '20px sans-serif', + showBackground : true, horizontalOrigin : Cesium.HorizontalOrigin.CENTER, pixelOffset : new Cesium.Cartesian2(0.0, -image.height), pixelOffsetScaleByDistance : new Cesium.NearFarScalar(1.5e2, 3.0, 1.5e7, 0.5) From 7b7e262cca41c8d7e1d13806bfce43480f5578ff Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Wed, 7 Dec 2016 16:46:04 -0500 Subject: [PATCH 30/34] Fix typo. --- Source/Scene/Label.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Source/Scene/Label.js b/Source/Scene/Label.js index c743892f782d..ca1b68d2e8ef 100644 --- a/Source/Scene/Label.js +++ b/Source/Scene/Label.js @@ -909,14 +909,12 @@ define([ for (var i = 0, len = glyphs.length; i < len; i++) { var glyph = glyphs[i]; if (defined(glyph.billboard)) { - // Set all the private values here, because we already clamped to ground - // so we don't want to do it again for every glyph glyph.billboard.clusterShow = value; } } var backgroundBillboard = this._backgroundBillboard; if (defined(backgroundBillboard)) { - backgroundBillboard.cluserShow = value; + backgroundBillboard.clusterShow = value; } } } From 82391da93c54b1bfb3ceb22324de940c216f5fc5 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Wed, 7 Dec 2016 16:49:35 -0500 Subject: [PATCH 31/34] CHANGES.md --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index f7659ea2524a..04869b9c4c50 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,8 @@ Change Log ### 1.29 - 2017-01-02 * Added the ability to blend a `Model` with a color/translucency. Added `color`, `colorBlendMode`, and `colorBlendAmount` properties to `Model`, `ModelGraphics`, and CZML. Added `ColorBlendMode` enum. [#4547](https://github.com/AnalyticalGraphicsInc/cesium/pull/4547) +* Added new `Label` properties `showBackground`, `backgroundColor`, and `backgroundPadding` to the primitive, Entity, and CZML layers. +* Added new enum `VerticalOrigin.BASELINE`. Previously, `VerticalOrigin.BOTTOM` would sometimes align to the baseline depending on the contents of a label. * Fixed tooltips for gallery thumbnails in Sandcastle [#4702](https://github.com/AnalyticalGraphicsInc/cesium/pull/4702) * Fixed texture rotation for `RectangleGeometry` [#2737](https://github.com/AnalyticalGraphicsInc/cesium/issues/2737) From a36cf20b164937ac9a9550e0e829cf8066810272 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Wed, 7 Dec 2016 17:24:10 -0500 Subject: [PATCH 32/34] Don't test a function by copying its logic into the test. --- Specs/Scene/LabelCollectionSpec.js | 220 +++-------------------------- 1 file changed, 22 insertions(+), 198 deletions(-) diff --git a/Specs/Scene/LabelCollectionSpec.js b/Specs/Scene/LabelCollectionSpec.js index 5b5936314091..c7bb80f3310f 100644 --- a/Specs/Scene/LabelCollectionSpec.js +++ b/Specs/Scene/LabelCollectionSpec.js @@ -931,39 +931,13 @@ defineSuite([ }); scene.renderForSpecs(); - var x = Number.POSITIVE_INFINITY; - var y = Number.POSITIVE_INFINITY; - var maxX = 0; - var maxY = 0; - - var glyphs = label._glyphs; - var length = glyphs.length; - for (var i = 0; i < length; ++i) { - var glyph = glyphs[i]; - var billboard = glyph.billboard; - if (!defined(billboard)) { - continue; - } - - var glyphWidth = billboard.width * scale; - var glyphHeight = billboard.height * scale; - var glyphX = billboard._translate.x; - var glyphY = -(billboard._translate.y + glyphHeight); - - x = Math.min(x, glyphX); - y = Math.min(y, glyphY); - maxX = Math.max(maxX, glyphX + glyphWidth); - maxY = Math.max(maxY, glyphY + glyphHeight); - } - - var width = maxX - x; - var height = maxY - y; - var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); - expect(bbox.x).toEqual(x); - expect(bbox.y).toEqual(y); - expect(bbox.width).toEqual(width); - expect(bbox.height).toEqual(height); + expect(bbox.x).toBeDefined(); + expect(bbox.y).toBeDefined(); + expect(bbox.width).toBeGreaterThan(30); + expect(bbox.width).toBeLessThan(200); + expect(bbox.height).toBeGreaterThan(10); + expect(bbox.height).toBeLessThan(50); }); it('computes screen space bounding box with result', function() { @@ -975,40 +949,14 @@ defineSuite([ }); scene.renderForSpecs(); - var x = Number.POSITIVE_INFINITY; - var y = Number.POSITIVE_INFINITY; - var maxX = 0; - var maxY = 0; - - var glyphs = label._glyphs; - var length = glyphs.length; - for (var i = 0; i < length; ++i) { - var glyph = glyphs[i]; - var billboard = glyph.billboard; - if (!defined(billboard)) { - continue; - } - - var glyphWidth = billboard.width * scale; - var glyphHeight = billboard.height * scale; - var glyphX = billboard._translate.x; - var glyphY = -(billboard._translate.y + glyphHeight); - - x = Math.min(x, glyphX); - y = Math.min(y, glyphY); - maxX = Math.max(maxX, glyphX + glyphWidth); - maxY = Math.max(maxY, glyphY + glyphHeight); - } - - var width = maxX - x; - var height = maxY - y; - var result = new BoundingRectangle(); var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO, result); - expect(bbox.x).toEqual(x); - expect(bbox.y).toEqual(y); - expect(bbox.width).toEqual(width); - expect(bbox.height).toEqual(height); + expect(bbox.x).toBeDefined(); + expect(bbox.y).toBeDefined(); + expect(bbox.width).toBeGreaterThan(30); + expect(bbox.width).toBeLessThan(200); + expect(bbox.height).toBeGreaterThan(10); + expect(bbox.height).toBeLessThan(50); expect(bbox).toBe(result); }); @@ -1022,39 +970,9 @@ defineSuite([ }); scene.renderForSpecs(); - var x = Number.POSITIVE_INFINITY; - var y = Number.POSITIVE_INFINITY; - var maxX = 0; - var maxY = 0; - - var glyphs = label._glyphs; - var length = glyphs.length; - for (var i = 0; i < length; ++i) { - var glyph = glyphs[i]; - var billboard = glyph.billboard; - if (!defined(billboard)) { - continue; - } - - var glyphWidth = billboard.width * scale; - var glyphHeight = billboard.height * scale; - var glyphX = billboard._translate.x; - var glyphY = -(billboard._translate.y + glyphHeight * 0.5); - - x = Math.min(x, glyphX); - y = Math.min(y, glyphY); - maxX = Math.max(maxX, glyphX + glyphWidth); - maxY = Math.max(maxY, glyphY + glyphHeight); - } - - var width = maxX - x; - var height = maxY - y; - var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); - expect(bbox.x).toEqual(x); - expect(bbox.y).toEqual(y); - expect(bbox.width).toEqual(width); - expect(bbox.height).toEqual(height); + expect(bbox.y).toBeGreaterThan(bbox.height * -1.0); + expect(bbox.y).toBeLessThan(bbox.height * -0.5); }); it('computes screen space bounding box with vertical origin top', function() { @@ -1067,39 +985,9 @@ defineSuite([ }); scene.renderForSpecs(); - var x = Number.POSITIVE_INFINITY; - var y = Number.POSITIVE_INFINITY; - var maxX = 0; - var maxY = 0; - - var glyphs = label._glyphs; - var length = glyphs.length; - for (var i = 0; i < length; ++i) { - var glyph = glyphs[i]; - var billboard = glyph.billboard; - if (!defined(billboard)) { - continue; - } - - var glyphWidth = billboard.width * scale; - var glyphHeight = billboard.height * scale; - var glyphX = billboard._translate.x; - var glyphY = -billboard._translate.y; - - x = Math.min(x, glyphX); - y = Math.min(y, glyphY); - maxX = Math.max(maxX, glyphX + glyphWidth); - maxY = Math.max(maxY, glyphY + glyphHeight); - } - - var width = maxX - x; - var height = maxY - y; - var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); - expect(bbox.x).toEqual(x); - expect(bbox.y).toEqual(y); - expect(bbox.width).toEqual(width); - expect(bbox.height).toEqual(height); + expect(bbox.y).toBeLessThan(5); + expect(bbox.y).toBeGreaterThan(-5); }); it('computes screen space bounding box with vertical origin baseline', function() { @@ -1112,39 +1000,9 @@ defineSuite([ }); scene.renderForSpecs(); - var x = Number.POSITIVE_INFINITY; - var y = Number.POSITIVE_INFINITY; - var maxX = 0; - var maxY = 0; - - var glyphs = label._glyphs; - var length = glyphs.length; - for (var i = 0; i < length; ++i) { - var glyph = glyphs[i]; - var billboard = glyph.billboard; - if (!defined(billboard)) { - continue; - } - - var glyphWidth = billboard.width * scale; - var glyphHeight = billboard.height * scale; - var glyphX = billboard._translate.x; - var glyphY = -(billboard._translate.y + glyphHeight); - - x = Math.min(x, glyphX); - y = Math.min(y, glyphY); - maxX = Math.max(maxX, glyphX + glyphWidth); - maxY = Math.max(maxY, glyphY + glyphHeight); - } - - var width = maxX - x; - var height = maxY - y; - var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); - expect(bbox.x).toEqual(x); - expect(bbox.y).toEqual(y); - expect(bbox.width).toEqual(width); - expect(bbox.height).toEqual(height); + expect(bbox.y).toBeLessThan(bbox.height * -0.8); + expect(bbox.y).toBeGreaterThan(bbox.height * -1.2); }); it('computes screen space bounding box with horizontal origin', function() { @@ -1157,49 +1015,15 @@ defineSuite([ }); scene.renderForSpecs(); - var x = Number.POSITIVE_INFINITY; - var y = Number.POSITIVE_INFINITY; - var maxX = 0; - var maxY = 0; - - var glyphs = label._glyphs; - var length = glyphs.length; - for (var i = 0; i < length; ++i) { - var glyph = glyphs[i]; - var billboard = glyph.billboard; - if (!defined(billboard)) { - continue; - } - - var glyphWidth = billboard.width * scale; - var glyphHeight = billboard.height * scale; - var glyphX = billboard._translate.x; - var glyphY = -(billboard._translate.y + glyphHeight); - - x = Math.min(x, glyphX); - y = Math.min(y, glyphY); - maxX = Math.max(maxX, glyphX + glyphWidth); - maxY = Math.max(maxY, glyphY + glyphHeight); - } - - var width = maxX - x; - var height = maxY - y; - - var halfWidth = width * 0.5; - var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); - expect(bbox.x).toEqual(-halfWidth); - expect(bbox.y).toEqual(y); - expect(bbox.width).toEqual(width); - expect(bbox.height).toEqual(height); + expect(bbox.x).toBeLessThan(bbox.width * -0.3); + expect(bbox.x).toBeGreaterThan(bbox.width * -0.7); label.horizontalOrigin = HorizontalOrigin.RIGHT; scene.renderForSpecs(); bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); - expect(bbox.x).toEqual(-width); - expect(bbox.y).toEqual(y); - expect(bbox.width).toEqual(width); - expect(bbox.height).toEqual(height); + expect(bbox.x).toBeLessThan(bbox.width * -0.8); + expect(bbox.x).toBeGreaterThan(bbox.width * -1.2); }); it('computes screen space bounding box with padded background', function() { From 0eb64e790bb342cfd25998f7478f6ea4d286de9f Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Wed, 7 Dec 2016 19:48:44 -0500 Subject: [PATCH 33/34] Make background color less harsh on clamping demo. --- Apps/Sandcastle/gallery/Ground Clamping.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Apps/Sandcastle/gallery/Ground Clamping.html b/Apps/Sandcastle/gallery/Ground Clamping.html index 6729403d358c..81c1d41a123b 100644 --- a/Apps/Sandcastle/gallery/Ground Clamping.html +++ b/Apps/Sandcastle/gallery/Ground Clamping.html @@ -61,7 +61,9 @@ heightReference : Cesium.HeightReference.CLAMP_TO_GROUND, horizontalOrigin : Cesium.HorizontalOrigin.LEFT, verticalOrigin : Cesium.VerticalOrigin.BASELINE, + fillColor : Cesium.Color.BLACK, showBackground : true, + backgroundColor : new Cesium.Color(1, 1, 1, 0.7), backgroundPadding : new Cesium.Cartesian2(8, 4) } }); From e76e6930ab1ec43c76869df1229f73636fe8e8d4 Mon Sep 17 00:00:00 2001 From: Ed Mackey Date: Thu, 8 Dec 2016 14:28:28 -0500 Subject: [PATCH 34/34] Make label tests more robust, fix vertical center test. --- Specs/Scene/LabelCollectionSpec.js | 33 ++++++++++++++++-------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/Specs/Scene/LabelCollectionSpec.js b/Specs/Scene/LabelCollectionSpec.js index c7bb80f3310f..8a87183fa3ab 100644 --- a/Specs/Scene/LabelCollectionSpec.js +++ b/Specs/Scene/LabelCollectionSpec.js @@ -48,6 +48,9 @@ defineSuite([ var labels; var labelsWithHeight; + // This Unicode square block will more reliably cover the center pixel than an 'x' or a 'w' char. + var solidBox = '\u25a0'; + beforeAll(function() { scene = createScene(); camera = scene.camera; @@ -343,19 +346,19 @@ defineSuite([ it('can render after adding a label', function() { labels.add({ position : Cartesian3.ZERO, - text : 'w', + text : solidBox, horizontalOrigin : HorizontalOrigin.CENTER, verticalOrigin : VerticalOrigin.CENTER }); var actual = scene.renderForSpecs(); - expect(actual[0]).toBeGreaterThan(10); - expect(actual[1]).toBeGreaterThan(10); - expect(actual[2]).toBeGreaterThan(10); + expect(actual[0]).toBeGreaterThan(200); + expect(actual[1]).toBeGreaterThan(200); + expect(actual[2]).toBeGreaterThan(200); labels.add({ position : new Cartesian3(1.0, 0.0, 0.0), // Closer to camera - text : 'x', + text : solidBox, fillColor : { red : 1.0, green : 0.0, @@ -367,7 +370,7 @@ defineSuite([ }); actual = scene.renderForSpecs(); - expect(actual[0]).toBeGreaterThan(10); + expect(actual[0]).toBeGreaterThan(200); expect(actual[1]).toBeLessThan(10); expect(actual[2]).toBeLessThan(10); }); @@ -543,7 +546,7 @@ defineSuite([ labels.add({ position : Cartesian3.ZERO, pixelOffset : new Cartesian2(1.0, 0.0), - text : 'x', + text : solidBox, horizontalOrigin : HorizontalOrigin.CENTER, verticalOrigin : VerticalOrigin.CENTER, pixelOffsetScaleByDistance: new NearFarScalar(2.0, 0.0, 4.0, 1000.0) @@ -559,7 +562,7 @@ defineSuite([ it('renders label with distanceDisplayCondition', function() { labels.add({ position : Cartesian3.ZERO, - text : 'm', + text : solidBox, distanceDisplayCondition : new DistanceDisplayCondition(10.0, 100.0), horizontalOrigin : HorizontalOrigin.CENTER, verticalOrigin : VerticalOrigin.CENTER @@ -597,7 +600,7 @@ defineSuite([ it('can pick a label', function() { var label = labels.add({ position : Cartesian3.ZERO, - text : 'x', + text : solidBox, horizontalOrigin : HorizontalOrigin.CENTER, verticalOrigin : VerticalOrigin.CENTER, id : 'id' @@ -611,7 +614,7 @@ defineSuite([ it('can change pick id', function() { var label = labels.add({ position : Cartesian3.ZERO, - text : 'x', + text : solidBox, horizontalOrigin : HorizontalOrigin.CENTER, verticalOrigin : VerticalOrigin.CENTER, id : 'id' @@ -632,7 +635,7 @@ defineSuite([ labels.add({ show : false, position : Cartesian3.ZERO, - text : 'x', + text : solidBox, horizontalOrigin : HorizontalOrigin.CENTER, verticalOrigin : VerticalOrigin.CENTER }); @@ -644,7 +647,7 @@ defineSuite([ it('picks a label using translucencyByDistance', function() { var label = labels.add({ position : Cartesian3.ZERO, - text : 'x', + text : solidBox, horizontalOrigin : HorizontalOrigin.CENTER, verticalOrigin : VerticalOrigin.CENTER }); @@ -667,7 +670,7 @@ defineSuite([ var label = labels.add({ position : Cartesian3.ZERO, pixelOffset : new Cartesian2(0.0, 100.0), - text : 'x', + text : solidBox, horizontalOrigin : HorizontalOrigin.CENTER, verticalOrigin : VerticalOrigin.CENTER }); @@ -971,8 +974,8 @@ defineSuite([ scene.renderForSpecs(); var bbox = Label.getScreenSpaceBoundingBox(label, Cartesian2.ZERO); - expect(bbox.y).toBeGreaterThan(bbox.height * -1.0); - expect(bbox.y).toBeLessThan(bbox.height * -0.5); + expect(bbox.y).toBeGreaterThan(bbox.height * -0.9); + expect(bbox.y).toBeLessThan(bbox.height * -0.3); }); it('computes screen space bounding box with vertical origin top', function() {