Skip to content

Commit

Permalink
Fix collision boxes for text with variable placement (#10709)
Browse files Browse the repository at this point in the history
  • Loading branch information
Arindam Bose committed May 27, 2021
1 parent 71f3846 commit 30faf71
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 47 deletions.
33 changes: 15 additions & 18 deletions src/data/array_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -603,11 +603,11 @@ register('StructArrayLayout2i2ui3ul3ui2f3ub1ul1i1ub48', StructArrayLayout2i2ui3u
* [0]: Int16[8]
* [16]: Uint16[15]
* [48]: Uint32[1]
* [52]: Float32[4]
* [52]: Float32[3]
*
* @private
*/
class StructArrayLayout8i15ui1ul4f68 extends StructArray {
class StructArrayLayout8i15ui1ul3f64 extends StructArray {
uint8: Uint8Array;
int16: Int16Array;
uint16: Uint16Array;
Expand All @@ -622,15 +622,15 @@ class StructArrayLayout8i15ui1ul4f68 extends StructArray {
this.float32 = new Float32Array(this.arrayBuffer);
}

emplaceBack(v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, v16: number, v17: number, v18: number, v19: number, v20: number, v21: number, v22: number, v23: number, v24: number, v25: number, v26: number, v27: number) {
emplaceBack(v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, v16: number, v17: number, v18: number, v19: number, v20: number, v21: number, v22: number, v23: number, v24: number, v25: number, v26: number) {
const i = this.length;
this.resize(i + 1);
return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27);
return this.emplace(i, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26);
}

emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, v16: number, v17: number, v18: number, v19: number, v20: number, v21: number, v22: number, v23: number, v24: number, v25: number, v26: number, v27: number) {
const o2 = i * 34;
const o4 = i * 17;
emplace(i: number, v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number, v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number, v16: number, v17: number, v18: number, v19: number, v20: number, v21: number, v22: number, v23: number, v24: number, v25: number, v26: number) {
const o2 = i * 32;
const o4 = i * 16;
this.int16[o2 + 0] = v0;
this.int16[o2 + 1] = v1;
this.int16[o2 + 2] = v2;
Expand Down Expand Up @@ -658,13 +658,12 @@ class StructArrayLayout8i15ui1ul4f68 extends StructArray {
this.float32[o4 + 13] = v24;
this.float32[o4 + 14] = v25;
this.float32[o4 + 15] = v26;
this.float32[o4 + 16] = v27;
return i;
}
}

StructArrayLayout8i15ui1ul4f68.prototype.bytesPerElement = 68;
register('StructArrayLayout8i15ui1ul4f68', StructArrayLayout8i15ui1ul4f68);
StructArrayLayout8i15ui1ul3f64.prototype.bytesPerElement = 64;
register('StructArrayLayout8i15ui1ul3f64', StructArrayLayout8i15ui1ul3f64);

/**
* Implementation of the StructArray layout:
Expand Down Expand Up @@ -1003,7 +1002,6 @@ class SymbolInstanceStruct extends Struct {
numVerticalIconVertices: number;
useRuntimeCollisionCircles: number;
crossTileID: number;
textBoxScale: number;
textOffset0: number;
textOffset1: number;
collisionCircleDiameter: number;
Expand Down Expand Up @@ -1032,20 +1030,19 @@ class SymbolInstanceStruct extends Struct {
get useRuntimeCollisionCircles() { return this._structArray.uint16[this._pos2 + 22]; }
get crossTileID() { return this._structArray.uint32[this._pos4 + 12]; }
set crossTileID(x: number) { this._structArray.uint32[this._pos4 + 12] = x; }
get textBoxScale() { return this._structArray.float32[this._pos4 + 13]; }
get textOffset0() { return this._structArray.float32[this._pos4 + 14]; }
get textOffset1() { return this._structArray.float32[this._pos4 + 15]; }
get collisionCircleDiameter() { return this._structArray.float32[this._pos4 + 16]; }
get textOffset0() { return this._structArray.float32[this._pos4 + 13]; }
get textOffset1() { return this._structArray.float32[this._pos4 + 14]; }
get collisionCircleDiameter() { return this._structArray.float32[this._pos4 + 15]; }
}

SymbolInstanceStruct.prototype.size = 68;
SymbolInstanceStruct.prototype.size = 64;

export type SymbolInstance = SymbolInstanceStruct;

/**
* @private
*/
export class SymbolInstanceArray extends StructArrayLayout8i15ui1ul4f68 {
export class SymbolInstanceArray extends StructArrayLayout8i15ui1ul3f64 {
/**
* Return the SymbolInstanceStruct at the given location in the array.
* @param {number} index The index of the element.
Expand Down Expand Up @@ -1157,7 +1154,7 @@ export {
StructArrayLayout2ub2f12,
StructArrayLayout3ui6,
StructArrayLayout2i2ui3ul3ui2f3ub1ul1i1ub48,
StructArrayLayout8i15ui1ul4f68,
StructArrayLayout8i15ui1ul3f64,
StructArrayLayout1f4,
StructArrayLayout3i6,
StructArrayLayout1ul3ui12,
Expand Down
1 change: 0 additions & 1 deletion src/data/bucket/symbol_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ export const symbolInstance = createLayout([
{type: 'Uint16', name: 'numVerticalIconVertices'},
{type: 'Uint16', name: 'useRuntimeCollisionCircles'},
{type: 'Uint32', name: 'crossTileID'},
{type: 'Float32', name: 'textBoxScale'},
{type: 'Float32', components: 2, name: 'textOffset'},
{type: 'Float32', name: 'collisionCircleDiameter'},
]);
Expand Down
10 changes: 5 additions & 5 deletions src/render/draw_symbol.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,14 @@ function drawSymbols(painter: Painter, sourceCache: SourceCache, layer: SymbolSt
}
}

function calculateVariableRenderShift(anchor, width, height, textOffset, textBoxScale, renderTextSize): Point {
function calculateVariableRenderShift(anchor, width, height, textOffset, textScale, renderTextSize): Point {
const {horizontalAlign, verticalAlign} = getAnchorAlignment(anchor);
const shiftX = -(horizontalAlign - 0.5) * width;
const shiftY = -(verticalAlign - 0.5) * height;
const variableOffset = evaluateVariableOffset(anchor, textOffset);
return new Point(
(shiftX / textBoxScale + variableOffset[0]) * renderTextSize,
(shiftY / textBoxScale + variableOffset[1]) * renderTextSize
(shiftX / textScale + variableOffset[0]) * renderTextSize,
(shiftY / textScale + variableOffset[1]) * renderTextSize
);
}

Expand Down Expand Up @@ -168,10 +168,10 @@ function updateVariableAnchorsForBucket(bucket, rotateWithMap, pitchWithMap, var
renderTextSize *= bucket.tilePixelRatio / tileScale;
}

const {width, height, anchor, textOffset, textBoxScale} = variableOffset;
const {width, height, anchor, textOffset, textScale} = variableOffset;

const shift = calculateVariableRenderShift(
anchor, width, height, textOffset, textBoxScale, renderTextSize);
anchor, width, height, textOffset, textScale, renderTextSize);

// Usual case is that we take the projected anchor and add the pixel-based shift
// calculated above. In the (somewhat weird) case of pitch-aligned text, we add an equivalent
Expand Down
25 changes: 12 additions & 13 deletions src/symbol/placement.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,14 @@ class CollisionGroups {
}
}

function calculateVariableLayoutShift(anchor: TextAnchor, width: number, height: number, textOffset: [number, number], textBoxScale: number): Point {
function calculateVariableLayoutShift(anchor: TextAnchor, width: number, height: number, textOffset: [number, number], textScale: number): Point {
const {horizontalAlign, verticalAlign} = getAnchorAlignment(anchor);
const shiftX = -(horizontalAlign - 0.5) * width;
const shiftY = -(verticalAlign - 0.5) * height;
const offset = evaluateVariableOffset(anchor, textOffset);
return new Point(
shiftX + offset[0] * textBoxScale,
shiftY + offset[1] * textBoxScale
shiftX + offset[0] * textScale,
shiftY + offset[1] * textScale
);
}

Expand All @@ -154,7 +154,7 @@ export type VariableOffset = {
width: number,
height: number,
anchor: TextAnchor,
textBoxScale: number,
textScale: number,
prevAnchor?: TextAnchor
};

Expand Down Expand Up @@ -298,14 +298,13 @@ export class Placement {
}

attemptAnchorPlacement(anchor: TextAnchor, textBox: SingleCollisionBox, width: number, height: number,
textBoxScale: number, rotateWithMap: boolean, pitchWithMap: boolean, textPixelRatio: number,
textScale: number, rotateWithMap: boolean, pitchWithMap: boolean, textPixelRatio: number,
posMatrix: mat4, collisionGroup: CollisionGroup, textAllowOverlap: boolean,
symbolInstance: SymbolInstance, symbolIndex: number, bucket: SymbolBucket,
orientation: number, iconBox: ?SingleCollisionBox, textSize: any, iconSize: any): ?{ shift: Point, placedGlyphBoxes: { box: Array<number>, offscreen: boolean } } {

const textOffset = [symbolInstance.textOffset0, symbolInstance.textOffset1];
const textScale = bucket.getSymbolInstanceTextSize(textSize, symbolInstance, this.transform.zoom, symbolIndex);
const shift = calculateVariableLayoutShift(anchor, width, height, textOffset, textBoxScale);
const shift = calculateVariableLayoutShift(anchor, width, height, textOffset, textScale);

const placedGlyphBoxes = this.collisionIndex.placeCollisionBox(
textScale, textBox, offsetShift(shift.x, shift.y, rotateWithMap, pitchWithMap, this.transform.angle),
Expand Down Expand Up @@ -335,7 +334,7 @@ export class Placement {
width,
height,
anchor,
textBoxScale,
textScale,
prevAnchor
};
this.markUsedJustification(bucket, anchor, symbolInstance, orientation);
Expand Down Expand Up @@ -515,9 +514,9 @@ export class Placement {
}

const placeBoxForVariableAnchors = (collisionTextBox, collisionIconBox, orientation) => {
const textBoxScale = symbolInstance.textBoxScale;
const width = (collisionTextBox.x2 - collisionTextBox.x1) * textBoxScale + 2.0 * collisionTextBox.padding;
const height = (collisionTextBox.y2 - collisionTextBox.y1) * textBoxScale + 2.0 * collisionTextBox.padding;
const textScale = bucket.getSymbolInstanceTextSize(partiallyEvaluatedTextSize, symbolInstance, this.transform.zoom, symbolIndex);
const width = (collisionTextBox.x2 - collisionTextBox.x1) * textScale + 2.0 * collisionTextBox.padding;
const height = (collisionTextBox.y2 - collisionTextBox.y1) * textScale + 2.0 * collisionTextBox.padding;

const variableIconBox = hasIconTextFit && !iconAllowOverlap ? collisionIconBox : null;
if (variableIconBox) updateBoxData(variableIconBox);
Expand All @@ -528,7 +527,7 @@ export class Placement {
const anchor = anchors[i % anchors.length];
const allowOverlap = (i >= anchors.length);
const result = this.attemptAnchorPlacement(
anchor, collisionTextBox, width, height, textBoxScale, rotateWithMap,
anchor, collisionTextBox, width, height, textScale, rotateWithMap,
pitchWithMap, textPixelRatio, posMatrix, collisionGroup, allowOverlap,
symbolInstance, symbolIndex, bucket, orientation, variableIconBox,
partiallyEvaluatedTextSize, partiallyEvaluatedIconSize);
Expand Down Expand Up @@ -991,7 +990,7 @@ export class Placement {
variableOffset.width,
variableOffset.height,
variableOffset.textOffset,
variableOffset.textBoxScale);
variableOffset.textScale);
if (rotateWithMap) {
shift._rotate(pitchWithMap ? this.transform.angle : -this.transform.angle);
}
Expand Down
16 changes: 6 additions & 10 deletions src/symbol/symbol_layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,6 @@ function addFeature(bucket: SymbolBucket,
const defaultHorizontalShaping = getDefaultHorizontalShaping(shapedTextOrientations.horizontal);
const glyphSize = ONE_EM,
fontScale = layoutTextSize / glyphSize,
textBoxScale = bucket.tilePixelRatio * fontScale,
textMaxBoxScale = bucket.tilePixelRatio * textMaxSize / glyphSize,
iconBoxScale = bucket.tilePixelRatio * layoutIconSize,
symbolMinDistance = bucket.tilePixelRatio * layout.get('symbol-spacing'),
Expand Down Expand Up @@ -408,8 +407,8 @@ function addFeature(bucket: SymbolBucket,
}

addSymbol(bucket, anchor, line, shapedTextOrientations, shapedIcon, imageMap, verticallyShapedIcon, bucket.layers[0],
bucket.collisionBoxArray, feature.index, feature.sourceLayerIndex, bucket.index,
textBoxScale, textPadding, textAlongLine, textOffset,
bucket.collisionBoxArray, feature.index, feature.sourceLayerIndex,
bucket.index, textPadding, textAlongLine, textOffset,
iconBoxScale, iconPadding, iconAlongLine, iconOffset,
feature, sizes, isSDFIcon, canonical, layoutTextSize);
};
Expand Down Expand Up @@ -551,7 +550,6 @@ export function evaluateBoxCollisionFeature(collisionBoxArray: CollisionBoxArray
sourceLayerIndex: number,
bucketIndex: number,
shaped: Object,
boxScale: number,
padding: number,
rotate: number): number {
let y1 = shaped.top;
Expand Down Expand Up @@ -628,7 +626,6 @@ function addSymbol(bucket: SymbolBucket,
featureIndex: number,
sourceLayerIndex: number,
bucketIndex: number,
textBoxScale: number,
textPadding: number,
textAlongLine: boolean,
textOffset: [number, number],
Expand Down Expand Up @@ -674,9 +671,9 @@ function addSymbol(bucket: SymbolBucket,
} else {
const textRotation = layer.layout.get('text-rotate').evaluate(feature, {}, canonical);
const verticalTextRotation = textRotation + 90.0;
verticalTextBoxIndex = evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, verticalShaping, textBoxScale, textPadding, verticalTextRotation);
verticalTextBoxIndex = evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, verticalShaping, textPadding, verticalTextRotation);
if (verticallyShapedIcon) {
verticalIconBoxIndex = evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, verticallyShapedIcon, iconBoxScale, iconPadding, verticalTextRotation);
verticalIconBoxIndex = evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, verticallyShapedIcon, iconPadding, verticalTextRotation);
}
}
}
Expand All @@ -690,7 +687,7 @@ function addSymbol(bucket: SymbolBucket,
const hasIconTextFit = layer.layout.get('icon-text-fit') !== 'none';
const iconQuads = getIconQuads(shapedIcon, iconRotate, isSDFIcon, hasIconTextFit);
const verticalIconQuads = verticallyShapedIcon ? getIconQuads(verticallyShapedIcon, iconRotate, isSDFIcon, hasIconTextFit) : undefined;
iconBoxIndex = evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, shapedIcon, iconBoxScale, iconPadding, iconRotate);
iconBoxIndex = evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, shapedIcon, iconPadding, iconRotate);
numIconVertices = iconQuads.length * 4;

const sizeData = bucket.iconSizeData;
Expand Down Expand Up @@ -761,7 +758,7 @@ function addSymbol(bucket: SymbolBucket,
textCircle = evaluateCircleCollisionFeature(shaping);
} else {
const textRotate = layer.layout.get('text-rotate').evaluate(feature, {}, canonical);
textBoxIndex = evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, shaping, textBoxScale, textPadding, textRotate);
textBoxIndex = evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, shaping, textPadding, textRotate);
}
}

Expand Down Expand Up @@ -834,7 +831,6 @@ function addSymbol(bucket: SymbolBucket,
numVerticalIconVertices,
useRuntimeCollisionCircles,
0,
textBoxScale,
textOffset0,
textOffset1,
collisionCircleDiameter);
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"version": 8,
"metadata": {
"test": {
"collisionDebug": true,
"height": 256
}
},
"center": [
13.418056,
52.499167
],
"zoom": 17.5,
"sources": {
"mapbox": {
"type": "vector",
"maxzoom": 14,
"tiles": [
"local://tiles/{z}-{x}-{y}.mvt"
]
}
},
"glyphs": "local://glyphs/{fontstack}/{range}.pbf",
"sprite": "local://sprites/sprite",
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "white"
}
},
{
"id": "line",
"type": "symbol",
"source": "mapbox",
"source-layer": "road_label",
"layout": {
"text-field": "test test",
"text-size": [
"interpolate",
["linear"],
["zoom"],
17,
10,
18,
30
],
"text-font": [
"Open Sans Semibold",
"Arial Unicode MS Bold"
],
"text-variable-anchor": ["left", "right"],
"symbol-placement": "point",
"symbol-spacing": 20
},
"paint": {
"icon-opacity": 1
}
}
]
}

0 comments on commit 30faf71

Please sign in to comment.