changeset 1402:caf441eebc23

did: try to add text-anchor
author sam <sam@basx.dev>
date Wed, 18 Dec 2024 00:11:40 +0700
parents 4ecb004ee7f8
children 02d302c868d5
files semicongine/text.nim semicongine/text/font.nim tests/test_text.nim
diffstat 3 files changed, 75 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- 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
 
--- 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,
--- 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):