Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for multiline labels using explicit newline character. #4306

Merged
merged 34 commits into from
Dec 13, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d47f517
Reanimated abandoned support for multiline text in Labels using expli…
dwhipps Sep 9, 2016
bac19f0
Fixed to ensure backwards compatibility. Better variable naming.
dwhipps Sep 9, 2016
1afb9a9
Cleanup.
dwhipps Sep 9, 2016
81a3a78
Removed empty block.
dwhipps Sep 9, 2016
42c0fe9
Merge branch 'master' into feature/label-newline-support
dwhipps Oct 19, 2016
c4b8703
Merge branch 'master' into feature/label-newline-support
dwhipps Oct 19, 2016
9f8e686
Looks like newline glyphs had non-zero width. Dont add widths for new…
dwhipps Oct 19, 2016
58cc9bb
Cleanup before a bit more tweaking.
dwhipps Oct 19, 2016
7e65a65
Ensure we use computedWidth rather than simple width for glyph width.
dwhipps Oct 19, 2016
77ed7b4
Ensure vertical centering works correctly.
dwhipps Oct 19, 2016
88bc7c3
Removed incorrect comment.
dwhipps Oct 19, 2016
08ed5f5
Merge branch 'master' into feature/label-newline-support
dwhipps Oct 20, 2016
1ca1c89
More renaming.
dwhipps Oct 20, 2016
d25dcdd
Added some leading. Looks better.
dwhipps Oct 20, 2016
2d2c7c2
Merge branch 'master' into feature/label-newline-support
dwhipps Oct 28, 2016
306ca1a
Fixed test for changes in Label width and height when adding newlines.
dwhipps Oct 28, 2016
6f9f57c
Merge branch 'master' into feature/label-newline-support
dwhipps Oct 31, 2016
78156d7
Merge branch 'master' of github.com:AnalyticalGraphicsInc/cesium into…
dwhipps Nov 1, 2016
6fd89ca
Merge branch 'rectangle-texture' of github.com:AnalyticalGraphicsInc/…
dwhipps Dec 7, 2016
a41e167
Fixing merge conflicts.
dwhipps Dec 7, 2016
1084cc7
Merge branch 'master' into feature/label-newline-support
dwhipps Dec 7, 2016
cc49261
Fixed undefined.
dwhipps Dec 7, 2016
a5d426d
Merge remote-tracking branch 'origin/master' into multiline-label-bac…
emackey Dec 8, 2016
1a62c75
CHANGES.md
emackey Dec 8, 2016
84d0071
Integrate logic for newlines and background size/placement.
emackey Dec 8, 2016
e17a393
Line spacing, shield against empty string with newlines.
emackey Dec 8, 2016
8fbfaa3
Line spacing
emackey Dec 9, 2016
40737cb
Fix text justification to match horizontal origin choice.
emackey Dec 9, 2016
bcc1f86
Update tests
emackey Dec 9, 2016
72697cf
Update doc.
emackey Dec 9, 2016
6539033
Update Sandcastle picking demo.
emackey Dec 12, 2016
ef86d83
Tweak CHANGES.md
pjcozzi Dec 13, 2016
02c6d93
Mention CZML in CHANGES.md
pjcozzi Dec 13, 2016
fe84bef
Fix formatting.
mramato Dec 13, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions Apps/Sandcastle/gallery/Picking.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@
Sandcastle.addDefaultToolbarButton('Show Cartographic Position on Mouse Over', function() {
var entity = viewer.entities.add({
label : {
show : false
show : false,
showBackground : true,
font : '14px monospace',
horizontalOrigin : Cesium.HorizontalOrigin.LEFT,
verticalOrigin : Cesium.VerticalOrigin.TOP,
pixelOffset : new Cesium.Cartesian2(15, 0)
}
});

Expand All @@ -53,7 +58,9 @@

entity.position = cartesian;
entity.label.show = true;
entity.label.text = '(' + longitudeString + ', ' + latitudeString + ')';
entity.label.text =
'Lon: ' + (' ' + longitudeString).slice(-7) + '\u00B0' +
'\nLat: ' + (' ' + latitudeString).slice(-7) + '\u00B0';
} else {
entity.label.show = false;
}
Expand Down Expand Up @@ -159,7 +166,11 @@
var labelEntity = viewer.entities.add({
label : {
show : false,
horizontalOrigin : Cesium.HorizontalOrigin.LEFT
showBackground : true,
font : '14px monospace',
horizontalOrigin : Cesium.HorizontalOrigin.LEFT,
verticalOrigin : Cesium.VerticalOrigin.TOP,
pixelOffset : new Cesium.Cartesian2(15, 0)
}
});

Expand All @@ -181,8 +192,11 @@

labelEntity.position = cartesian;
labelEntity.label.show = true;
labelEntity.label.text = '(' + longitudeString + ', ' + latitudeString + ', ' + heightString + ')';

labelEntity.label.text =
'Lon: ' + (' ' + longitudeString).slice(-7) + '\u00B0' +
'\nLat: ' + (' ' + latitudeString).slice(-7) + '\u00B0' +
'\nAlt: ' + (' ' + heightString).slice(-7) + 'm';

var camera = scene.camera;
labelEntity.label.eyeOffset = new Cesium.Cartesian3(0.0, 0.0, camera.frustum.near * 1.5 - Cesium.Cartesian3.distance(cartesian, camera.position));

Expand All @@ -201,7 +215,7 @@
handler = handler && handler.destroy();
};
//Sandcastle_End
Sandcastle.finishedLoading();
Sandcastle.finishedLoading();
}
if (typeof Cesium !== "undefined") {
startup(Cesium);
Expand Down
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Change Log
* 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.
* Added support for newlines (`\n`) in Cesium `Label`s and CZML. [#2402](https://github.com/AnalyticalGraphicsInc/cesium/issues/2402)
* Fixed tooltips for gallery thumbnails in Sandcastle [#4702](https://github.com/AnalyticalGraphicsInc/cesium/pull/4702)
* Fixed `Rectangle.union` to correctly account for rectangles that cross the IDL [#4732](https://github.com/AnalyticalGraphicsInc/cesium/pull/4732).
* Fixed texture rotation for `RectangleGeometry` [#2737](https://github.com/AnalyticalGraphicsInc/cesium/issues/2737)
Expand Down
Binary file modified Documentation/Images/Billboard.setHorizontalOrigin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion Source/DataSources/LabelGraphics.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ define([
* @constructor
*
* @param {Object} [options] Object with the following properties:
* @param {Property} [options.text] A Property specifying the text.
* @param {Property} [options.text] A Property specifying the text. Explicit newlines '\n' are supported.
* @param {Property} [options.font='10px sans-serif'] A Property specifying the CSS font.
* @param {Property} [options.style=LabelStyle.FILL] A Property specifying the {@link LabelStyle}.
* @param {Property} [options.fillColor=Color.WHITE] A Property specifying the fill {@link Color}.
Expand Down Expand Up @@ -110,6 +110,7 @@ define([

/**
* Gets or sets the string Property specifying the text of the label.
* Explicit newlines '\n' are supported.
* @memberof LabelGraphics.prototype
* @type {Property}
*/
Expand Down
2 changes: 1 addition & 1 deletion Source/Scene/Billboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ define([
* to the left, center, or right of its anchor position.
* <br /><br />
* <div align='center'>
* <img src='images/Billboard.setHorizontalOrigin.png' width='400' height='300' /><br />
* <img src='images/Billboard.setHorizontalOrigin.png' width='648' height='196' /><br />
* </div>
* @memberof Billboard.prototype
* @type {HorizontalOrigin}
Expand Down
2 changes: 1 addition & 1 deletion Source/Scene/HorizontalOrigin.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ define([
* of the anchor position.
* <br /><br />
* <div align='center'>
* <img src='images/Billboard.setHorizontalOrigin.png' width='400' height='300' /><br />
* <img src='images/Billboard.setHorizontalOrigin.png' width='648' height='196' /><br />
* </div>
*
* @exports HorizontalOrigin
Expand Down
2 changes: 1 addition & 1 deletion Source/Scene/Label.js
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ define([
* to the left, center, or right of its anchor position.
* <br /><br />
* <div align='center'>
* <img src='images/Billboard.setHorizontalOrigin.png' width='400' height='300' /><br />
* <img src='images/Billboard.setHorizontalOrigin.png' width='648' height='196' /><br />
* </div>
* @memberof Label.prototype
* @type {HorizontalOrigin}
Expand Down
158 changes: 98 additions & 60 deletions Source/Scene/LabelCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ define([
this.dimensions = dimensions;
}

// Traditionally, leading is %20 of the font size.
var defaultLineSpacingPercent = 1.2;

var whitePixelCanvasId = 'ID_WHITE_PIXEL';
var whitePixelSize = new Cartesian2(4, 4);
var whitePixelBoundingRegion = new BoundingRectangle(1, 1, 1, 1);
Expand Down Expand Up @@ -135,7 +138,7 @@ define([
// presize glyphs to match the new text length
glyphs.length = textLength;

var showBackground = label._showBackground && (glyphs.length > 0);
var showBackground = label._showBackground && (text.split('\n').join('').length > 0);
var backgroundBillboard = label._backgroundBillboard;
var backgroundBillboardCollection = labelCollection._backgroundBillboardCollection;
if (!showBackground) {
Expand Down Expand Up @@ -267,107 +270,142 @@ define([
label._repositionAllGlyphs = true;
}

function calculateWidthOffset(lineWidth, horizontalOrigin, backgroundPadding) {
if (horizontalOrigin === HorizontalOrigin.CENTER) {
return -lineWidth / 2;
} else if (horizontalOrigin === HorizontalOrigin.RIGHT) {
return -(lineWidth + backgroundPadding.x);
}
return backgroundPadding.x;
}

// reusable Cartesian2 instances
var glyphPixelOffset = new Cartesian2();
var scratchBackgroundPadding = new Cartesian2();

function repositionAllGlyphs(label, resolutionScale) {
var glyphs = label._glyphs;
var text = label._text;
var glyph;
var dimensions;
var totalWidth = 0;
var maxDescent = Number.NEGATIVE_INFINITY;
var maxY = 0;
var lastLineWidth = 0;
var maxLineWidth = 0;
var lineWidths = [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a scratch?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My hope is we don't need a scratch in this case. This function only gets called when the glyphs all need to be repositioned, which is an expensive operation we try to avoid (using the delay tactics you pointed out in the label background PR).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

var maxGlyphDescent = Number.NEGATIVE_INFINITY;
var maxGlyphY = 0;
var numberOfLines = 1;
var glyphIndex = 0;
var glyphLength = glyphs.length;

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) {
glyph = glyphs[glyphIndex];
dimensions = glyph.dimensions;
maxY = Math.max(maxY, dimensions.height - dimensions.descent);
maxDescent = Math.max(maxDescent, dimensions.descent);

//Computing the total width must also account for the kering that occurs between letters.
totalWidth += dimensions.width - dimensions.bounds.minx;
if (glyphIndex < glyphLength - 1) {
totalWidth += glyphs[glyphIndex + 1].dimensions.bounds.minx;
if (text.charAt(glyphIndex) === '\n') {
lineWidths.push(lastLineWidth);
++numberOfLines;
lastLineWidth = 0;
} else {
glyph = glyphs[glyphIndex];
dimensions = glyph.dimensions;
maxGlyphY = Math.max(maxGlyphY, dimensions.height - dimensions.descent);
maxGlyphDescent = Math.max(maxGlyphDescent, dimensions.descent);

//Computing the line width must also account for the kerning that occurs between letters.
lastLineWidth += dimensions.width - dimensions.bounds.minx;
if (glyphIndex < glyphLength - 1) {
lastLineWidth += glyphs[glyphIndex + 1].dimensions.bounds.minx;
}
maxLineWidth = Math.max(maxLineWidth, lastLineWidth);
}
}
var maxHeight = maxY + maxDescent;
lineWidths.push(lastLineWidth);
var maxLineHeight = maxGlyphY + maxGlyphDescent;

var scale = label._scale;
var horizontalOrigin = label._horizontalOrigin;
var widthOffset = 0;
if (horizontalOrigin === HorizontalOrigin.CENTER) {
widthOffset -= totalWidth / 2 * scale;
} else if (horizontalOrigin === HorizontalOrigin.RIGHT) {
widthOffset -= (totalWidth + backgroundPadding.x) * scale;
} else {
widthOffset += backgroundPadding.x * scale;
}
var verticalOrigin = label._verticalOrigin;
var lineIndex = 0;
var lineWidth = lineWidths[lineIndex];
var widthOffset = calculateWidthOffset(lineWidth, horizontalOrigin, backgroundPadding);
var lineSpacing = defaultLineSpacingPercent * maxLineHeight;
var otherLinesHeight = lineSpacing * (numberOfLines - 1);

glyphPixelOffset.x = widthOffset * resolutionScale;
glyphPixelOffset.x = widthOffset * scale * resolutionScale;
glyphPixelOffset.y = 0;

var verticalOrigin = label._verticalOrigin;
var lineOffsetY = 0;
for (glyphIndex = 0; glyphIndex < glyphLength; ++glyphIndex) {
glyph = glyphs[glyphIndex];
dimensions = glyph.dimensions;

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;
if (text.charAt(glyphIndex) === '\n') {
++lineIndex;
lineOffsetY += lineSpacing;
lineWidth = lineWidths[lineIndex];
widthOffset = calculateWidthOffset(lineWidth, horizontalOrigin, backgroundPadding);
glyphPixelOffset.x = widthOffset * scale * resolutionScale;
} else {
// VerticalOrigin.BOTTOM
glyphPixelOffset.y = (maxDescent - dimensions.descent + backgroundPadding.y) * scale;
}
glyph = glyphs[glyphIndex];
dimensions = glyph.dimensions;

if (verticalOrigin === VerticalOrigin.TOP) {
glyphPixelOffset.y = dimensions.height - maxGlyphY - backgroundPadding.y;
} else if (verticalOrigin === VerticalOrigin.CENTER) {
glyphPixelOffset.y = (otherLinesHeight + dimensions.height - maxGlyphY) / 2;
} else if (verticalOrigin === VerticalOrigin.BASELINE) {
glyphPixelOffset.y = otherLinesHeight;
} else {
// VerticalOrigin.BOTTOM
glyphPixelOffset.y = otherLinesHeight + maxGlyphDescent + backgroundPadding.y;
}
glyphPixelOffset.y = (glyphPixelOffset.y - dimensions.descent - lineOffsetY) * scale * resolutionScale;

glyphPixelOffset.y *= resolutionScale;
if (defined(glyph.billboard)) {
glyph.billboard._setTranslate(glyphPixelOffset);
}

if (defined(glyph.billboard)) {
glyph.billboard._setTranslate(glyphPixelOffset);
//Compute the next x offset taking into acocunt the kerning performed
//on both the current letter as well as the next letter to be drawn
//as well as any applied scale.
if (glyphIndex < glyphLength - 1) {
var nextGlyph = glyphs[glyphIndex + 1];
glyphPixelOffset.x += ((dimensions.width - dimensions.bounds.minx) + nextGlyph.dimensions.bounds.minx) * scale * resolutionScale;
}
}
}

//Compute the next x offset taking into acocunt the kerning performed
//on both the current letter as well as the next letter to be drawn
//as well as any applied scale.
if (glyphIndex < glyphLength - 1) {
var nextGlyph = glyphs[glyphIndex + 1];
glyphPixelOffset.x += ((dimensions.width - dimensions.bounds.minx) + nextGlyph.dimensions.bounds.minx) * scale * resolutionScale;
if (defined(backgroundBillboard) && (text.split('\n').join('').length > 0)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a more memory efficient way to do this?

text.split('\n').join('').length > 0

If so, does it matter here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This too is part of repositionAllGlyphs and typically happens only when there is some substantial change made to the label.

if (horizontalOrigin === HorizontalOrigin.CENTER) {
widthOffset = -maxLineWidth / 2 - backgroundPadding.x;
} else if (horizontalOrigin === HorizontalOrigin.RIGHT) {
widthOffset = -(maxLineWidth + backgroundPadding.x * 2);
} else {
widthOffset = 0;
}
}
glyphPixelOffset.x = widthOffset * scale * resolutionScale;

if (defined(backgroundBillboard) && (glyphLength > 0)) {
glyphPixelOffset.x = (widthOffset - backgroundPadding.x * scale) * resolutionScale;
if (verticalOrigin === VerticalOrigin.BASELINE) {
glyphPixelOffset.y = -backgroundPadding.y * scale - maxDescent * scale;
} else if (verticalOrigin === VerticalOrigin.TOP) {
glyphPixelOffset.y = -(maxY - maxHeight) * scale - maxDescent * scale;
if (verticalOrigin === VerticalOrigin.TOP) {
glyphPixelOffset.y = maxLineHeight - maxGlyphY - maxGlyphDescent;
} else if (verticalOrigin === VerticalOrigin.CENTER) {
glyphPixelOffset.y = -(maxY - maxHeight) / 2 * scale - maxDescent * scale;
glyphPixelOffset.y = (maxLineHeight - maxGlyphY) / 2 - maxGlyphDescent;
} else if (verticalOrigin === VerticalOrigin.BASELINE) {
glyphPixelOffset.y = -backgroundPadding.y - maxGlyphDescent;
} else {
// VerticalOrigin.BOTTOM
glyphPixelOffset.y = 0;
}
glyphPixelOffset.y *= resolutionScale;
backgroundBillboard.width = totalWidth + (backgroundPadding.x * 2);
backgroundBillboard.height = maxHeight + (backgroundPadding.y * 2);
glyphPixelOffset.y = glyphPixelOffset.y * scale * resolutionScale;

backgroundBillboard.width = maxLineWidth + (backgroundPadding.x * 2);
backgroundBillboard.height = maxLineHeight + otherLinesHeight + (backgroundPadding.y * 2);
backgroundBillboard._setTranslate(glyphPixelOffset);
}
}

function destroyLabel(labelCollection, label) {
var glyphs = label._glyphs;
for ( var i = 0, len = glyphs.length; i < len; ++i) {
for (var i = 0, len = glyphs.length; i < len; ++i) {
unbindGlyph(labelCollection, glyphs[i]);
}
if (defined(label._backgroundBillboard)) {
Expand Down Expand Up @@ -628,7 +666,7 @@ define([
LabelCollection.prototype.removeAll = function() {
var labels = this._labels;

for ( var i = 0, len = labels.length; i < len; ++i) {
for (var i = 0, len = labels.length; i < len; ++i) {
destroyLabel(this, labels[i]);
}

Expand Down
Loading