Skip to content

Commit efcd5be

Browse files
authored
Merge pull request #172 from cginternals/compute-label-extent
Compute label extent
2 parents f4d703c + 004d5ce commit efcd5be

File tree

4 files changed

+95
-7
lines changed

4 files changed

+95
-7
lines changed

source/text/glyph.frag

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ float aastep(float t, float value)
3535
/* float afwidth = length(vec2(dFdx(value), dFdy(value))) * u_aaStepScale; */
3636
float afwidth = fwidth(value) * u_aaStepScale;
3737
/* The aa step scale is more of a hack to provide seemingly smoother (e.g., >= 1.0) or crisper (e.g., between 0.0
38-
* and 1.0) contours without specific sampling. Its just scaling the outcome of the derivatives.
38+
* and 1.0) contours without specific sampling. It's just scaling the outcome of the derivatives.
3939
*/
4040

4141
return smoothstep(t - afwidth, t + afwidth, value);

source/text/glyph.vert

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ void main(void)
4545

4646
v_uv = a_vertex * texExt + vec2(a_texCoord[0], 1.0 - a_texCoord[1]);
4747

48-
/* POSITIONING*/
48+
/* POSITIONING */
4949
/* quad data as flat array: [0, 0, 0, 1, 1, 0, 1, 1] (a_vertex), which translates to ll, lr, ul, ur corners.
5050
* 2-------4
5151
* | \ |

source/text/label.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export abstract class Label {
5656
/** @see {@link type} */
5757
protected _type: Label.Type;
5858

59-
/** @see {@link transform} */
59+
/** @see {@link staticTransform} */
6060
protected _staticTransform: mat4;
6161

6262
/** @see {@link dynamicTransform} */
@@ -460,13 +460,15 @@ export abstract class Label {
460460
}
461461

462462
/**
463-
* The typesetter sets this extent after typesetting and applying the transform.
463+
* The typesetter sets this extent after typesetting and applying the static transform. Don't set this manually
464+
* without typesetting.
464465
*/
465466
set extent(e: [number, number]) {
466467
this._extent = e;
467468
}
468469
/**
469-
* Returns the width and height of the typset label. Both are zero if not typeset yet.
470+
* Returns the width and height of the typset label in fontSizeUnit. Both are zero if not typeset yet. The static
471+
* transform is already applied.
470472
*/
471473
get extent(): [number, number] {
472474
return this._extent;

source/text/typesetter.ts

+88-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
/* spellchecker: disable */
33

4-
import { mat4, vec2, vec3 } from 'gl-matrix';
4+
import { mat4, vec2, vec3, vec4 } from 'gl-matrix';
55

66
import { assert } from '../auxiliaries';
77
import { v3 } from '../gl-matrix-extensions';
@@ -216,7 +216,6 @@ export class Typesetter {
216216
switch (label.elide) {
217217
case Label.Elide.Right:
218218
return [label.lineWidth - ellipsisWidth, 0.0];
219-
break;
220219
case Label.Elide.Middle:
221220
const threshold = label.lineWidth / 2 - ellipsisWidth / 2;
222221
return [threshold, threshold];
@@ -323,6 +322,71 @@ export class Typesetter {
323322
}
324323
}
325324

325+
/**
326+
* @param currentRectangle - [minX, minY, minZ, maxX, maxY, maxZ] is updated in-place
327+
* @param newRectangle - [minX, minY, minZ, maxX, maxY, maxZ] used to update currentRectangle
328+
*/
329+
private static updateRectangleMinMax(currentRectangle: number[], newRectangle: number[]): void {
330+
assert(currentRectangle.length === 6 && newRectangle.length === 6, `expected the rectangles to have 6 values!`);
331+
332+
let i = 0;
333+
for (; i < 3; i++) {
334+
currentRectangle[i] = Math.min(currentRectangle[i], newRectangle[i]);
335+
}
336+
for (; i < 6; i++) {
337+
currentRectangle[i] = Math.max(currentRectangle[i], newRectangle[i]);
338+
}
339+
}
340+
341+
/**
342+
* Returns a vec2 [min, max] containing the minimum and the maximum of the given values.
343+
* @param currentMin - the current minimum (e.g., initialized to +Infinity)
344+
* @param currentMax - the current maximum (e.g., initialized to -Infinity)
345+
* @param values - find the maximum and minimum of the given values
346+
*/
347+
private static minMax(currentMin: number, currentMax: number, values: number[]): vec2 {
348+
const min = Math.min(currentMin, ...values);
349+
const max = Math.max(currentMax, ...values);
350+
return vec2.fromValues(min, max);
351+
}
352+
353+
/**
354+
* Returns [minX, minY, minZ, maxX, maxY, maxZ] of the vertices coordinates, i.e., origins,
355+
* origins + tangents, origins + ups, from which a bounding rectangle can be calculated.
356+
* @param vertices - Glyph vertices to be transformed (expected untransformed, in typesetting space).
357+
* @param begin - Vertex index to start alignment at.
358+
* @param end - Vertex index to stop alignment at.
359+
*/
360+
private static getMinMaxVertices(vertices: GlyphVertices, begin: number, end: number)
361+
: [number, number, number, number, number, number] {
362+
363+
let minX = Number.POSITIVE_INFINITY;
364+
let maxX = Number.NEGATIVE_INFINITY;
365+
let minY = Number.POSITIVE_INFINITY;
366+
let maxY = Number.NEGATIVE_INFINITY;
367+
let minZ = Number.POSITIVE_INFINITY;
368+
let maxZ = Number.NEGATIVE_INFINITY;
369+
370+
for (let i: number = begin; i < end; ++i) {
371+
const x = Typesetter.minMax(minX, maxX, [vertices.origin(i)[0], vertices.origin(i)[0] + vertices.up(i)[0],
372+
vertices.origin(i)[0] + vertices.tangent(i)[0]]);
373+
minX = x[0];
374+
maxX = x[1];
375+
376+
const y = Typesetter.minMax(minY, maxY, [vertices.origin(i)[1], vertices.origin(i)[1] + vertices.up(i)[1],
377+
vertices.origin(i)[1] + vertices.tangent(i)[1]]);
378+
minY = y[0];
379+
maxY = y[1];
380+
381+
const z = Typesetter.minMax(minZ, maxZ, [vertices.origin(i)[2], vertices.origin(i)[2] + vertices.up(i)[2],
382+
vertices.origin(i)[2] + vertices.tangent(i)[2]]);
383+
minZ = z[0];
384+
maxZ = z[1];
385+
}
386+
387+
return [minX, minY, minZ, maxX, maxY, maxZ];
388+
}
389+
326390
/**
327391
* Adjusts the vertices for a line after typesetting (done due to line feed, word wrap, or end of line) w.r.t.
328392
* the targeted line alignment.
@@ -356,10 +420,32 @@ export class Typesetter {
356420
* @param lines - Indices of glyph vertices on same lines to apply line-based transformations.
357421
*/
358422
private static transform(label: Label, vertices: GlyphVertices, lines: Array<Line>): void {
423+
424+
const boundingRectangle = [
425+
Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY,
426+
Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY];
427+
359428
for (const line of lines) {
360429
Typesetter.transformAlignment(line[2], label.alignment, vertices, line[0], line[1]);
430+
431+
Typesetter.updateRectangleMinMax(boundingRectangle,
432+
Typesetter.getMinMaxVertices(vertices, line[0], line[1]));
433+
361434
Typesetter.transformVertices(label.staticTransform, vertices, line[0], line[1]);
362435
}
436+
437+
// transform extent from Typesetting Space to the label's fontUnitSize space (depending on the label, e.g.
438+
// screen space (px) or world space)
439+
const width = boundingRectangle[3] - boundingRectangle[0];
440+
const height = boundingRectangle[4] - boundingRectangle[1];
441+
442+
const ll = vec4.transformMat4(vec4.create(), vec4.fromValues(0, 0, 0, 1), label.staticTransform);
443+
const lr = vec4.transformMat4(vec4.create(), vec4.fromValues(width, 0, 0, 1), label.staticTransform);
444+
const ul = vec4.transformMat4(vec4.create(), vec4.fromValues(0, height, 0, 1), label.staticTransform);
445+
446+
const extent = vec2.fromValues(vec4.distance(lr, ll), vec4.distance(ul, ll));
447+
448+
label.extent = [extent[0], extent[1]];
363449
}
364450

365451

0 commit comments

Comments
 (0)