refactor(text): SDF for all text kinds; remove the legacy stroke text path

SDF is the only text renderer now — the stroke path and its LOD ladder are
gone, and the remaining text kinds render as SDF glyph quads too.

- ATTDEF / ATTRIB: carry a GlyphRun so single-line attribute text renders as
  SDF (multi-line already did via layout_mtext). Tolerance / complex-linetype
  text already SDF.
- Remove `sdf_text_enabled()` (the OCS_TEXT_SDF flag / kill switch) and take
  the SDF path unconditionally everywhere: tessellate Text arm (per-group by
  GlyphRun presence), hit_test fallbacks, render gather, complex_lt, table,
  mleader.
- Delete the text LOD ladder: tess.rs baseline/greek block and the
  block_cache greek/baseline path (emit_greeked_text / emit_text_baseline),
  plus the now-dead helpers text_baseline_points / text_greek_obb_tris /
  text_obb_corners_native / mtext_line_count and the LocalWire
  text_height_local / text_obb_local fields. All text is crisp at every zoom;
  text entities are exempt from the sub-pixel stub cull.
- Fix a latent bug: block-internal SDF text was culled because its LocalWire
  aabb_local was [0,0,0,0] (no points) — fold the glyph-quad extent into
  aabb_local and exempt text_verts-carrying wires from the sub-pixel cull.
- Clear the pre-existing dead-code warnings (TextPlacement.elevation, TabKind,
  MTextRunKind::Tab).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-07 21:07:00 +03:00
1 parent 67ce45d40b
commit 5c15091e74
12 files changed
+145 -1238

No files matched your search

+16 -37
View File
@@ -69,10 +69,8 @@ pub fn apply_along(
let mut strokes: Vec<Vec<[f32; 3]>> = Vec::new();
let mut cur_stroke: Vec<[f32; 3]> = Vec::new();
// When SDF text is on, embedded linetype text (LtSeg::Text) renders as glyph
// quads collected here instead of stroke polylines; emitted as one extra
// text-carrying WireModel at the end.
let sdf_on = crate::scene::text::sdf_atlas::sdf_text_enabled();
// Embedded linetype text (LtSeg::Text) renders as SDF glyph quads collected
// here; emitted as one extra text-carrying WireModel at the end.
let mut text_verts: Vec<crate::scene::pipeline::text_gpu::TextVertex> = Vec::new();
let mut elem_idx: usize = 0;
@@ -180,46 +178,27 @@ pub fn apply_along(
let insert = offset_pt(insert, fwd, perp, *x, *y);
let fwd_angle = fwd[1].atan2(fwd[0]) + rot_deg.to_radians();
let resolved = resolve_dxf_special_chars(text);
if sdf_on {
// SDF: glyph quads at the insert point (rotation baked in
// by layout_glyph_quads), collected for the text wire.
if let Ok(mut atlas) = crate::scene::text::sdf_atlas::text_atlas().lock() {
let quads = crate::scene::text::glyph_quads::layout_glyph_quads(
&mut atlas,
*tx_scale,
fwd_angle,
1.0,
0.0,
0.0,
style,
&resolved,
);
crate::scene::pipeline::text_gpu::push_glyph_vertices(
&mut text_verts,
&quads,
[insert[0] as f64, insert[1] as f64, insert[2] as f64],
1.0,
color,
0.0,
);
}
} else {
let (text_strokes, _) = lff::tessellate_text_ex(
[insert[0], insert[1]],
// SDF: glyph quads at the insert point (rotation baked in by
// layout_glyph_quads), collected for the text wire.
if let Ok(mut atlas) = crate::scene::text::sdf_atlas::text_atlas().lock() {
let quads = crate::scene::text::glyph_quads::layout_glyph_quads(
&mut atlas,
*tx_scale,
fwd_angle,
1.0,
0.0,
0.0,
style,
&resolved,
);
for stroke in &text_strokes {
if stroke.len() >= 2 {
let pts: Vec<[f32; 3]> =
stroke.iter().map(|&[sx, sy]| [sx, sy, insert[2]]).collect();
strokes.push(pts);
}
}
crate::scene::pipeline::text_gpu::push_glyph_vertices(
&mut text_verts,
&quads,
[insert[0] as f64, insert[1] as f64, insert[2] as f64],
1.0,
color,
0.0,
);
}
elem_idx += 1;
+1 -10
View File
@@ -39,18 +39,9 @@ pub fn text_atlas() -> &'static Mutex<GlyphAtlas> {
ATLAS.get_or_init(|| Mutex::new(GlyphAtlas::new(1024, 1024)))
}
/// Whether SDF text rendering is enabled, read once. Default ON — text draws
/// as SDF glyph quads (crisp at every zoom, no LOD; the wire path suppresses
/// the glyph strokes and keeps the insertion snap). Set `OCS_TEXT_SDF=0` to
/// fall back to the legacy stroke path (kill switch for the stroke renderer).
pub fn sdf_text_enabled() -> bool {
static ON: OnceLock<bool> = OnceLock::new();
*ON.get_or_init(|| std::env::var("OCS_TEXT_SDF").map_or(true, |v| v != "0"))
}
/// Debug: when env `OCS_TEXT_BOX` is set, each SDF text run also emits a
/// rectangle outline around its glyph bounds so the text pick box is visible.
/// Read once. Only meaningful together with [`sdf_text_enabled`].
/// Read once.
pub fn text_box_debug() -> bool {
static ON: OnceLock<bool> = OnceLock::new();
*ON.get_or_init(|| std::env::var_os("OCS_TEXT_BOX").is_some())