///|
pub(all) struct DotText2D {
chars : Array[Int]
cx : Double
cy : Double
color : @gfx.Color
scale : Double
}
///|
pub fn DotText2D::new(
chars~ : Array[Int],
cx~ : Double,
cy~ : Double,
color~ : @gfx.Color,
scale? : Double = 1.0,
) -> DotText2D {
{ chars, cx, cy, color, scale, }
}
///|
/// Rendered size of a dot-text run of `char_count` glyphs: `(width, height)`.
///
/// Exposed because the UI integrity gate compares a label's measured extent
/// against its node rect to catch text that will spill or be clipped; that
/// comparison is only sound if it uses the same arithmetic `append_dot_text`
/// lays the glyphs out with, so both read it from here.
pub fn dot_text_size(
char_count : Int,
scale? : Double = 1.0,
) -> (Double, Double) {
if char_count <= 0 {
return (0.0, 0.0)
}
let px_size = 3.0 * scale
let char_w = 3.0 * px_size
let gap = px_size
let width = char_count.to_double() * char_w +
(char_count - 1).to_double() * gap
(width, 5.0 * px_size)
}
///|
pub fn append_dot_text(
commands : Array[@gfx.DrawTrianglesCommand],
frame : RenderFrame2D,
text : DotText2D,
) -> Unit {
let px_size = 3.0 * text.scale
let char_w = 3.0 * px_size
let gap = px_size
let (total_w, char_h) = dot_text_size(text.chars.length(), scale=text.scale)
let start_x = text.cx - total_w / 2.0
let start_y = text.cy - char_h / 2.0
// One command for the whole run, not one per lit dot. Every dot of a run
// shares dst, shader, pipeline, blend and color -- the color is a uniform, so
// a run is exactly the granularity that can be merged without touching the
// vertex layout. The triangles and their order are unchanged, so the
// rasterizer walks the same list and produces the same pixels; a 3x5 glyph
// just stops costing ten `DrawTrianglesCommand` allocations.
// Not pre-sized. Reserving the worst case (15 dots per glyph against the ~10
// that are typically lit) separated as 1.10x faster only in the one bench
// that `moon bench -p` misreports, and not at all in the regime the gate
// runs -- while the vertex array becomes the command's `vertex_data`, so the
// slack would be held for the whole frame. See
// `docs/performance/dot-text-batching.md`.
let vertices : Array[Double] = []
let indices : Array[Int] = []
let screen_w = frame.screen_w.to_double()
let screen_h = frame.screen_h.to_double()
for i in 0.. 0 {
append_primitive_batch(commands, frame, vertices, indices, text.color)
}
}
///|
pub(all) struct Number2D {
number : Int
cx : Double
cy : Double
color : @gfx.Color
scale : Double
}
///|
pub fn Number2D::new(
number~ : Int,
cx~ : Double,
cy~ : Double,
color~ : @gfx.Color,
scale? : Double = 1.0,
) -> Number2D {
{ number, cx, cy, color, scale, }
}
///|
fn number_to_char_codes(number : Int) -> Array[Int] {
let digits : Array[Int] = []
if number == 0 {
digits.push(0)
} else {
let mut n = if number < 0 { -number } else { number }
while n > 0 {
digits.push(n % 10)
n = n / 10
}
if number < 0 {
digits.push(45)
}
let len = digits.length()
for i in 0..<(len / 2) {
let tmp = digits[i]
digits[i] = digits[len - 1 - i]
digits[len - 1 - i] = tmp
}
}
digits
}
///|
pub fn append_number(
commands : Array[@gfx.DrawTrianglesCommand],
frame : RenderFrame2D,
number : Number2D,
) -> Unit {
append_dot_text(
commands,
frame,
DotText2D::new(
chars=number_to_char_codes(number.number),
cx=number.cx,
cy=number.cy,
color=number.color,
scale=number.scale,
),
)
}