# HG changeset patch # User sam # Date 1734455500 -25200 # Node ID caf441eebc23f1fb575919589473aa1b195b8285 # Parent 4ecb004ee7f827a9e0b2e95236187b92fc12b0ff did: try to add text-anchor diff -r 4ecb004ee7f8 -r caf441eebc23 semicongine/text.nim --- a/semicongine/text.nim Mon Dec 16 23:58:35 2024 +0700 +++ b/semicongine/text.nim Wed Dec 18 00:11:40 2024 +0700 @@ -32,6 +32,8 @@ advance*: Table[Rune, float32] kerning*: Table[(Rune, Rune), float32] lineAdvance*: float32 + lineHeight*: float32 # like lineAdvance - lineGap + ascent*: float32 # from baseline to highest glyph descriptorSet*: DescriptorSetData[GlyphDescriptorSet[N]] descriptorGlyphIndex: Table[Rune, uint16] fallbackCharacter: Rune @@ -115,10 +117,17 @@ scale = 1'f32, color = vec4(1, 1, 1, 1), ) = + ## Add text for rendering. + ## `position` is the display position, where as `(0, 0) is top-left and (1, 1) is bottom right. + ## The z-compontent goes from 0 (near plane) to 1 (far plane) and is usually just used for ordering layers + ## this should be called again after aspect ratio of window changes assert text.len <= glyphs.position.len, &"Set {text.len} but Glyphs-object only supports {glyphs.position.len}" - var origin = - vec3(position.x * 2'f32 - 1'f32, -(position.y * 2'f32 - 1'f32), position.z) + var origin = vec3( + position.x * getAspectRatio() * 2'f32 - 1'f32, + -(position.y * 2'f32 - 1'f32), + position.z, + ) let s = scale * glyphs.baseScale var cursorPos = origin for i in 0 ..< text.len: @@ -147,6 +156,61 @@ cursorPos.x = cursorPos.x + glyphs.font.kerning.getOrDefault((text[i], text[i + 1]), 0) * s +proc textDimension(glyphs: var Glyphs, text: seq[Rune], scale: float32): Vec2f = + let s = scale * glyphs.baseScale + let nLines = text.countIt(it == Rune('\n')).float32 + let height = nLines * glyphs.font.lineAdvance * s + glyphs.font.lineHeight * s + + var width = 0'f32 + var lineI = 0 + var currentWidth = 0'f32 + for i in 0 ..< text.len: + if text[i] == NEWLINE: + width = max(currentWidth, width) + currentWidth = 0'f32 + inc lineI + else: + if not (i == text.len - 1 and text[i].isWhiteSpace): + if text[i] in glyphs.font.advance: + currentWidth += glyphs.font.advance[text[i]] * s + else: + currentWidth += glyphs.font.advance[glyphs.font.fallbackCharacter] * s + if i < text.len - 1: + currentWidth += glyphs.font.kerning.getOrDefault((text[i], text[i + 1]), 0) * s + return vec2(width, height) + +proc add*( + glyphs: var Glyphs, + text: seq[Rune], + position: Vec3f, + anchor: Vec2f, + scale = 1'f32, + color = vec4(1, 1, 1, 1), +) = + let s = scale * glyphs.baseScale + let baselineStart = vec2(0, glyphs.font.ascent * s) + let pos = position.xy + anchor * textDimension(glyphs, text, scale) + baselineStart + add(glyphs, text, pos.toVec3(position.z), scale, color) + +proc add*( + glyphs: var Glyphs, + text: string, + position: Vec3f, + scale = 1'f32, + color = vec4(1, 1, 1, 1), +) = + add(glyphs, text.toRunes, position, scale, color) + +proc add*( + glyphs: var Glyphs, + text: string, + position: Vec3f, + anchor: Vec2f, + scale = 1'f32, + color = vec4(1, 1, 1, 1), +) = + add(glyphs, text.toRunes, position, anchor, scale, color) + proc reset*(glyphs: var Glyphs) = glyphs.cursor = 0 diff -r 4ecb004ee7f8 -r caf441eebc23 semicongine/text/font.nim --- a/semicongine/text/font.nim Mon Dec 16 23:58:35 2024 +0700 +++ b/semicongine/text/font.nim Wed Dec 18 00:11:40 2024 +0700 @@ -151,6 +151,8 @@ var ascent, descent, lineGap: cint stbtt_GetFontVMetrics(addr fi, addr ascent, addr descent, addr lineGap) result.lineAdvance = float32(ascent - descent + lineGap) * glyph2QuadScale + result.lineHeight = float32(ascent - descent) * glyph2QuadScale + result.ascent = float32(ascent) * glyph2QuadScale proc loadFont*[N: static int]( path: string, diff -r 4ecb004ee7f8 -r caf441eebc23 tests/test_text.nim --- a/tests/test_text.nim Mon Dec 16 23:58:35 2024 +0700 +++ b/tests/test_text.nim Wed Dec 18 00:11:40 2024 +0700 @@ -34,12 +34,13 @@ while ((getMonoTime() - start).inMilliseconds().int / 1000) < time: let t = getMonoTime() glyphs.reset() - glyphs.add("semi-\ncon-\nginea".toRunes(), vec3(0.0, 0.0)) - glyphs.add("semi-\ncon-\ngine".toRunes(), vec3(0.5, -0.5)) - glyphs.add("semi-\ncon-\ngine".toRunes(), vec3(-0.5, 0.5)) - glyphs.add("semi-\ncon-\ngineb".toRunes(), vec3(0.5, 0.5)) - glyphs.add("semi-\ncon-\ngineb".toRunes(), vec3(0.9, 0.1)) - glyphs.add("semi-\ncon-\ngineb".toRunes(), vec3(0.1, 0.9)) + glyphs.add("semi-\ncon-\nginea", vec3(0.0, 0.0), vec2(0, 0)) + # glyphs.add("semi-\ncon-\ngine".toRunes(), vec3(0.5, -0.5)) + # glyphs.add("semi-\ncon-\ngine".toRunes(), vec3(-0.5, 0.5)) + # glyphs.add("semi-\ncon-\ngineb".toRunes(), vec3(0.5, 0.5)) + glyphs.add("semi-\ncon-\ngineb", vec3(0.5, 0.5), vec2(0.5, 0.5)) + glyphs.add("semi-\ncon-\ngineb", vec3(0.9, 0.9), vec2(0, 0)) + # glyphs.add("semi-\ncon-\ngineb".toRunes(), vec3(0.1, 0.9)) glyphs.updateAllGPUBuffers(flush = true) withNextFrame(framebuffer, commandbuffer):