diff --git a/src/mol-geo/geometry/text/font-atlas.ts b/src/mol-geo/geometry/text/font-atlas.ts index 7c58f276306cb31f1c24b8bd76b46d1762d2b778..eccb821267b667eda9b4b49eed52b44edc2e522b 100644 --- a/src/mol-geo/geometry/text/font-atlas.ts +++ b/src/mol-geo/geometry/text/font-atlas.ts @@ -33,7 +33,10 @@ export const FontAtlasParams = { export type FontAtlasParams = typeof FontAtlasParams export type FontAtlasProps = PD.Values<FontAtlasParams> -export type FontAtlasMap = { x: number, y: number, w: number, h: number } +export type FontAtlasMap = { + x: number, y: number, w: number, h: number, + nw: number, nh: number // normalized to lineheight +} export class FontAtlas { readonly props: Readonly<FontAtlasProps> @@ -120,7 +123,8 @@ export class FontAtlas { this.mapped[char] = { x: this.currentX, y: this.currentY, - w: this.scratchW, h: this.scratchH + w: this.scratchW, h: this.scratchH, + nw: this.scratchW / this.lineHeight, nh: this.scratchH / this.lineHeight } for (let y = 0; y < this.scratchH; ++y) { diff --git a/src/mol-geo/geometry/text/text-builder.ts b/src/mol-geo/geometry/text/text-builder.ts index bd7c381373c94a70197da94f2020aa75f489eb1a..052e3703c711367d36eed5d76f6ee1d4f83bbe4a 100644 --- a/src/mol-geo/geometry/text/text-builder.ts +++ b/src/mol-geo/geometry/text/text-builder.ts @@ -32,11 +32,11 @@ export namespace TextBuilder { const { attachment, background, backgroundMargin } = p const fontAtlas = getFontAtlas(p) - const { lineHeight } = fontAtlas - - const margin = (lineHeight * backgroundMargin * 0.1) - 10 - const outline = fontAtlas.buffer - console.log('margin', margin) + const margin = (1 / 2.5) * backgroundMargin + const outline = fontAtlas.buffer / fontAtlas.lineHeight + // console.log('margin', margin) + // console.log('attachment', attachment) + // console.log('background', background) return { add: (str: string, x: number, y: number, z: number, group: number) => { @@ -46,15 +46,15 @@ export namespace TextBuilder { // calculate width for (let iChar = 0; iChar < nChar; ++iChar) { const c = fontAtlas.get(str[iChar]) - xadvance += c.w - 2 * outline + xadvance += c.nw - 2 * outline } // attachment let yShift: number, xShift: number if (attachment.startsWith('top')) { - yShift = lineHeight / 1.25 + yShift = 1 / 1.25 } else if (attachment.startsWith('middle')) { - yShift = lineHeight / 2.5 + yShift = 1 / 2.5 } else { yShift = 0 // "bottom" } @@ -65,19 +65,17 @@ export namespace TextBuilder { } else { xShift = 0 // "left" } - xShift += outline - yShift += outline // background if (background) { - ChunkedArray.add2(mappings, -lineHeight / 6 - xShift - margin, lineHeight - yShift + margin) - ChunkedArray.add2(mappings, -lineHeight / 6 - xShift - margin, 0 - yShift - margin) - ChunkedArray.add2(mappings, xadvance + lineHeight / 6 - xShift + 2 * outline + margin, lineHeight - yShift + margin) - ChunkedArray.add2(mappings, xadvance + lineHeight / 6 - xShift + 2 * outline + margin, 0 - yShift - margin) + ChunkedArray.add2(mappings, -xadvance + xShift - margin - 0.1, yShift + margin) // top left + ChunkedArray.add2(mappings, -xadvance + xShift - margin - 0.1, -yShift - margin) // bottom left + ChunkedArray.add2(mappings, xadvance - xShift + margin + 0.1, yShift + margin) // top right + ChunkedArray.add2(mappings, xadvance - xShift + margin + 0.1, -yShift - margin) // bottom right const offset = centers.elementCount for (let i = 0; i < 4; ++i) { - ChunkedArray.add2(tcoords, 0, 10) + ChunkedArray.add2(tcoords, 10, 10) ChunkedArray.add3(centers, x, y, z); ChunkedArray.add(groups, group); } @@ -85,15 +83,17 @@ export namespace TextBuilder { ChunkedArray.add3(indices, offset + quadIndices[3], offset + quadIndices[4], offset + quadIndices[5]) } + xShift += outline + yShift += outline xadvance = 0 for (let iChar = 0; iChar < nChar; ++iChar) { const c = fontAtlas.get(str[iChar]) - ChunkedArray.add2(mappings, xadvance - xShift, c.h - yShift) // top left - ChunkedArray.add2(mappings, xadvance - xShift, 0 - yShift) // bottom left - ChunkedArray.add2(mappings, xadvance + c.w - xShift, c.h - yShift) // top right - ChunkedArray.add2(mappings, xadvance + c.w - xShift, 0 - yShift) // bottom right + ChunkedArray.add2(mappings, xadvance - xShift, c.nh - yShift) // top left + ChunkedArray.add2(mappings, xadvance - xShift, -yShift) // bottom left + ChunkedArray.add2(mappings, xadvance + c.nw - xShift, c.nh - yShift) // top right + ChunkedArray.add2(mappings, xadvance + c.nw - xShift, -yShift) // bottom right const texWidth = fontAtlas.texture.width const texHeight = fontAtlas.texture.height @@ -103,7 +103,7 @@ export namespace TextBuilder { ChunkedArray.add2(tcoords, (c.x + c.w) / texWidth, c.y / texHeight) // top right ChunkedArray.add2(tcoords, (c.x + c.w) / texWidth, (c.y + c.h) / texHeight) // bottom right - xadvance += c.w - 2 * outline + xadvance += c.nw - 2 * outline const offset = centers.elementCount for (let i = 0; i < 4; ++i) { @@ -120,10 +120,11 @@ export namespace TextBuilder { const ib = ChunkedArray.compact(indices, true) as Uint32Array const gb = ChunkedArray.compact(groups, true) as Float32Array const tb = ChunkedArray.compact(tcoords, true) as Float32Array + const ft = fontAtlas.texture return { kind: 'text', charCount: centers.elementCount / 4, - fontAtlas, + fontTexture: text ? ValueCell.update(text.fontTexture, ft) : ValueCell.create(ft), centerBuffer: text ? ValueCell.update(text.centerBuffer, cb) : ValueCell.create(cb), mappingBuffer: text ? ValueCell.update(text.centerBuffer, mb) : ValueCell.create(mb), indexBuffer: text ? ValueCell.update(text.indexBuffer, ib) : ValueCell.create(ib), diff --git a/src/mol-geo/geometry/text/text.ts b/src/mol-geo/geometry/text/text.ts index b1c4d3f0bfc74858cabed410cdfb35742a3ad76e..c36bea3f282145acbef821440e574c3118389a08 100644 --- a/src/mol-geo/geometry/text/text.ts +++ b/src/mol-geo/geometry/text/text.ts @@ -18,11 +18,11 @@ import { NullLocation } from 'mol-model/location'; import { UniformColorTheme } from 'mol-theme/color/uniform'; import { UniformSizeTheme } from 'mol-theme/size/uniform'; import { Sphere3D } from 'mol-math/geometry'; -import { calculateBoundingSphere } from 'mol-gl/renderable/util'; +import { calculateBoundingSphere, TextureImage, createTextureImage } from 'mol-gl/renderable/util'; import { TextValues } from 'mol-gl/renderable/text'; import { Color } from 'mol-util/color'; import { Vec3 } from 'mol-math/linear-algebra'; -import { FontAtlas, getFontAtlas, FontAtlasParams } from './font-atlas'; +import { FontAtlasParams } from './font-atlas'; import { RenderableState } from 'mol-gl/renderable'; import { clamp } from 'mol-math/interpolate'; @@ -35,7 +35,7 @@ export interface Text { /** Number of characters in the text */ readonly charCount: number, /** Font Atlas */ - readonly fontAtlas: FontAtlas, + readonly fontTexture: ValueCell<TextureImage<Uint8Array>>, /** Center buffer as array of xyz values wrapped in a value cell */ readonly centerBuffer: ValueCell<Float32Array>, @@ -56,10 +56,11 @@ export namespace Text { const ib = text ? text.indexBuffer.ref.value : new Uint32Array(0) const gb = text ? text.groupBuffer.ref.value : new Float32Array(0) const tb = text ? text.tcoordBuffer.ref.value : new Float32Array(0) + const ft = text ? text.fontTexture.ref.value : createTextureImage(0, 1) return { kind: 'text', charCount: 0, - fontAtlas: getFontAtlas({}), + fontTexture: text ? ValueCell.update(text.fontTexture, ft) : ValueCell.create(ft), centerBuffer: text ? ValueCell.update(text.centerBuffer, cb) : ValueCell.create(cb), mappingBuffer: text ? ValueCell.update(text.mappingBuffer, mb) : ValueCell.create(mb), indexBuffer: text ? ValueCell.update(text.indexBuffer, ib) : ValueCell.create(ib), @@ -75,13 +76,13 @@ export namespace Text { borderWidth: PD.Numeric(0, { min: 0, max: 1, step: 0.01 }), borderColor: PD.Color(ColorNames.grey), - offsetX: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }), - offsetY: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }), - offsetZ: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }), + offsetX: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }), + offsetY: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }), + offsetZ: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }), background: PD.Boolean(false), - backgroundMargin: PD.Numeric(0.2, { min: 0, max: 10, step: 0.1 }), + backgroundMargin: PD.Numeric(0.2, { min: 0, max: 1, step: 0.01 }), backgroundColor: PD.Color(ColorNames.grey), - backgroundOpacity: PD.Numeric(0, { min: 0, max: 1, step: 0.01 }), + backgroundOpacity: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }), attachment: PD.Select('normal', [['bottom-left', 'bottom-left'], ['bottom-center', 'bottom-center'], ['bottom-right', 'bottom-right'], ['middle-left', 'middle-left'], ['top-left', 'top-left'], ['top-center', 'top-center'], ['top-right', 'top-right']] as [TextAttachment, string][]), } @@ -105,8 +106,6 @@ export namespace Text { transform.aTransform.ref.value, instanceCount, padding ) - console.log(props.sizeFactor, text.fontAtlas.lineHeight, props.fontSize) - return { aPosition: text.centerBuffer, aMapping: text.mappingBuffer, @@ -120,11 +119,11 @@ export namespace Text { ...transform, aTexCoord: text.tcoordBuffer, - tFont: ValueCell.create(text.fontAtlas.texture), + tFont: text.fontTexture, padding: ValueCell.create(padding), ...Geometry.createValues(props, counts), - uSizeFactor: ValueCell.create(props.sizeFactor / text.fontAtlas.lineHeight), + uSizeFactor: ValueCell.create(props.sizeFactor), uBorderWidth: ValueCell.create(clamp(props.borderWidth / 2, 0, 0.5)), uBorderColor: ValueCell.create(Color.toArrayNormalized(props.borderColor, Vec3.zero(), 0)), diff --git a/src/tests/browser/render-text.ts b/src/tests/browser/render-text.ts index 5f3b36f59163b6c3ac7686f877b8b59a608d68cb..f0395806d41d24561e12738a0dad393ed4babc34 100644 --- a/src/tests/browser/render-text.ts +++ b/src/tests/browser/render-text.ts @@ -34,6 +34,7 @@ function textRepr() { attachment: 'middle-center', fontSize: 96, fontWeight: 'bold', + background: true } const textBuilder = TextBuilder.create(props, 1, 1) @@ -53,12 +54,13 @@ function textRepr() { } function spheresRepr() { - const spheresBuilder = SpheresBuilder.create(2, 1) + const spheresBuilder = SpheresBuilder.create(1, 1) + spheresBuilder.add(0, 0, 0, 0) spheresBuilder.add(5, 0, 0, 0) spheresBuilder.add(-4, 1, 0, 0) const spheres = spheresBuilder.getSpheres() - const values = Spheres.createValuesSimple(spheres, {}, Color(0xFF0000), 1) + const values = Spheres.createValuesSimple(spheres, {}, Color(0xFF0000), 0.2) const state = Geometry.createRenderableState() const renderObject = createSpheresRenderObject(values, state) console.log('spheres', renderObject)