Files
OneCAD-Simulation/crates/render/src/font.rs
T
jyvikoandClaude Opus 5.5 93f1a03137 render: symbols for boundary conditions, loads and interactions, and scene labels
glyphs::Glyphs picks where a model's setup symbols go (resolving its sets
once, thinning them on a grid, capping each definition and sampling large
ones with a note) and draws them into a Scene at the nodes' current
positions: cones for constrained translations (and rotations where nodes
rotate), arrows for forces, double-headed arrows for moments, arrows normal
to pressure faces, one arrow for gravity and body forces, a curved arrow for
centrifugal loads, crosses with names for reference points, lines for
couplings, hatched squares for rigid walls. Colours come from the caller by
role, muted over results.

Scene gains labels, drawn by the CPU rasterizer with a small bitmap font of
its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
2026-10-14 00:34:33 -04:00

87 lines
4.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! A small bitmap font for labels in headless pictures (the CPU rasterizer has no text
//! otherwise): capitals, digits and a few marks on a 5 × 7 grid, drawn for FieldCraft. Lower case
//! is drawn in capitals; anything else is a question mark.
/// Glyph width and height in font pixels, and the advance between glyphs.
pub const W: usize = 5;
pub const H: usize = 7;
pub const ADVANCE: usize = 6;
/// The rows of a character, top first, the leftmost pixel in bit 4.
pub fn rows(c: char) -> [u8; H] {
match c.to_ascii_uppercase() {
'A' => [0x0e, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11],
'B' => [0x1e, 0x11, 0x11, 0x1e, 0x11, 0x11, 0x1e],
'C' => [0x0e, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0e],
'D' => [0x1e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1e],
'E' => [0x1f, 0x10, 0x10, 0x1e, 0x10, 0x10, 0x1f],
'F' => [0x1f, 0x10, 0x10, 0x1e, 0x10, 0x10, 0x10],
'G' => [0x0e, 0x11, 0x10, 0x17, 0x11, 0x11, 0x0f],
'H' => [0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11],
'I' => [0x0e, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0e],
'J' => [0x07, 0x02, 0x02, 0x02, 0x02, 0x12, 0x0c],
'K' => [0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11],
'L' => [0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1f],
'M' => [0x11, 0x1b, 0x15, 0x15, 0x11, 0x11, 0x11],
'N' => [0x11, 0x11, 0x19, 0x15, 0x13, 0x11, 0x11],
'O' => [0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e],
'P' => [0x1e, 0x11, 0x11, 0x1e, 0x10, 0x10, 0x10],
'Q' => [0x0e, 0x11, 0x11, 0x11, 0x15, 0x12, 0x0d],
'R' => [0x1e, 0x11, 0x11, 0x1e, 0x14, 0x12, 0x11],
'S' => [0x0f, 0x10, 0x10, 0x0e, 0x01, 0x01, 0x1e],
'T' => [0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04],
'U' => [0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e],
'V' => [0x11, 0x11, 0x11, 0x11, 0x11, 0x0a, 0x04],
'W' => [0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0a],
'X' => [0x11, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x11],
'Y' => [0x11, 0x11, 0x0a, 0x04, 0x04, 0x04, 0x04],
'Z' => [0x1f, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1f],
'0' => [0x0e, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0e],
'1' => [0x04, 0x0c, 0x04, 0x04, 0x04, 0x04, 0x0e],
'2' => [0x0e, 0x11, 0x01, 0x02, 0x04, 0x08, 0x1f],
'3' => [0x1f, 0x02, 0x04, 0x02, 0x01, 0x11, 0x0e],
'4' => [0x02, 0x06, 0x0a, 0x12, 0x1f, 0x02, 0x02],
'5' => [0x1f, 0x10, 0x1e, 0x01, 0x01, 0x11, 0x0e],
'6' => [0x06, 0x08, 0x10, 0x1e, 0x11, 0x11, 0x0e],
'7' => [0x1f, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08],
'8' => [0x0e, 0x11, 0x11, 0x0e, 0x11, 0x11, 0x0e],
'9' => [0x0e, 0x11, 0x11, 0x0f, 0x01, 0x02, 0x0c],
' ' => [0; H],
'-' => [0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00],
'_' => [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f],
'.' => [0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c],
',' => [0x00, 0x00, 0x00, 0x00, 0x0c, 0x04, 0x08],
':' => [0x00, 0x0c, 0x0c, 0x00, 0x0c, 0x0c, 0x00],
'(' => [0x02, 0x04, 0x08, 0x08, 0x08, 0x04, 0x02],
')' => [0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08],
'/' => [0x01, 0x01, 0x02, 0x04, 0x08, 0x10, 0x10],
'+' => [0x00, 0x04, 0x04, 0x1f, 0x04, 0x04, 0x00],
'#' => [0x0a, 0x0a, 0x1f, 0x0a, 0x1f, 0x0a, 0x0a],
_ => [0x0e, 0x11, 0x01, 0x02, 0x04, 0x00, 0x04],
}
}
/// Whether font pixel (`x`, `y`) of `c` is set.
pub fn on(c: char, x: usize, y: usize) -> bool {
x < W && rows(c).get(y).is_some_and(|r| r & (0x10 >> x) != 0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn characters_are_distinct_and_lower_case_draws_capitals() {
let set: Vec<char> = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.,:()/+#".chars().collect();
for (i, a) in set.iter().enumerate() {
assert!(rows(*a).iter().all(|r| *r < 0x20), "{a}");
for b in set.iter().skip(i + 1) {
assert_ne!(rows(*a), rows(*b), "{a} and {b}");
}
}
assert_eq!(rows('r'), rows('R'));
assert_eq!(rows('é'), rows('?'));
assert!(on('L', 0, 6) && !on('L', 4, 0) && !on('L', 9, 0) && !on('L', 0, 9));
}
}