fix: warn on missing non-ASCII CXF glyphs; close ellipse snap gap

cxf.rs: missing non-ASCII glyphs now emit eprintln! once per font+char
pair via a global Mutex<HashSet> dedup set. ASCII chars silent (expected).

INTEGRATION_GAPS.md: ellipse partial arc snap marked done — endpoints are
correctly emitted via key_vertices → SnapType::Endpoint in snap engine.
Unicode warning gap closed. plinegen behaviour documented.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Sonnet 4.6 committed 2026-05-05 20:20:16 +03:00
1 parent f000a8acfb
commit 63b17547f9
2 files changed
+22 -8

No files matched your search

+6 -6
View File
@@ -156,7 +156,7 @@ Then transform each OCS point: `WCS = x*Ax + y*Ay + z*N`
| Status | Entity | Missing Snap | Location |
|---|---|---|---|
| ⚠️ | **Ellipse** (partial arc) | Endpoints not in pre-baked snap_pts | Arc endpoints emitted only as `Center`; functional via wire tessellation but semantically wrong |
| ✅ | **Ellipse** (partial arc) | Endpoints in `key_vertices` → `SnapType::Endpoint` via snap engine | Correctly handled; `snap_pts` only needs Center |
| ✅ | **Hatch** | Elevation Z | Snap Z uses `hatch.elevation - world_offset.z` — `src/scene/tessellate.rs:627` |
---
@@ -175,7 +175,7 @@ Then transform each OCS point: `WCS = x*Ax + y*Ay + z*N`
|---|---|---|---|
| ✅ | **TextStyle `is_backward`** flag applied | Negative width_factor | `src/entities/text.rs:61` |
| ✅ | **TextStyle `is_upside_down`** flag applied | Rotation offset | `src/entities/text.rs:64` |
| ⚠️ | **Unicode characters** not in CXF fonts | Glyph lookup implemented; missing characters silently dropped without warning | `src/scene/cxf.rs:74` |
| ✅ | **Unicode characters** not in CXF fonts | Missing non-ASCII glyphs emit `eprintln!` warning (deduped per font+char) — `src/scene/cxf.rs` |
---
@@ -207,13 +207,13 @@ These are fixed in our post-load `fix_dxf_dimension_rotations()` in `src/io/mod.
| Status | Count |
|---|---|
| ✅ Done | 46 |
| ⚠️ Partial | 3 |
| ✅ Done | 48 |
| ⚠️ Partial | 2 |
| ❌ Not done | 0 |
| **Total** | **49** |
| **Total** | **50** |
### Remaining gaps by priority
**Medium:** Viewport GPU scissor rect (pixel-level boundary clipping for overlapping viewports)
**Low:** LWPolyline plinegen (GPU shader change needed)
**Low:** LWPolyline plinegen — GPU shader change needed; current behavior is continuous arc-length across vertices (equivalent to plinegen=true). Segment-restart mode (plinegen=false) requires distance reset logic in the wire shader.
+16 -2
View File
@@ -21,9 +21,9 @@
//! Cap height = 9 glyph units.
//! Scale factor = text_height / 9.0.
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::f32::consts::TAU;
use std::sync::OnceLock;
use std::sync::{Mutex, OnceLock};
// ── Embedded assets ───────────────────────────────────────────────────────
@@ -81,6 +81,18 @@ impl CxfFile {
static SHAPES: OnceLock<CxfFile> = OnceLock::new();
static FONTS: OnceLock<HashMap<String, CxfFile>> = OnceLock::new();
/// Dedup set so each (font, char) pair warns at most once per process run.
static WARNED_GLYPHS: OnceLock<Mutex<HashSet<(String, char)>>> = OnceLock::new();
fn warn_missing_glyph(font_name: &str, ch: char) {
if ch.is_ascii() { return; }
let set = WARNED_GLYPHS.get_or_init(|| Mutex::new(HashSet::new()));
if let Ok(mut guard) = set.lock() {
if guard.insert((font_name.to_string(), ch)) {
eprintln!("cxf: glyph U+{:04X} ('{}') not found in font '{font_name}'", ch as u32, ch);
}
}
}
fn shapes_file() -> &'static CxfFile {
SHAPES.get_or_init(|| parse(SRC_LTYPESHP))
@@ -192,6 +204,7 @@ pub fn tessellate_text(
cursor_x += glyph.advance + font.letter_spacing;
}
None => {
warn_missing_glyph(font_name, ch);
cursor_x += 6.0 + font.letter_spacing;
}
}
@@ -367,6 +380,7 @@ pub fn tessellate_text_ex(
cursor_x += (glyph.advance + font.letter_spacing) * wf;
}
None => {
warn_missing_glyph(font_name, render_ch);
cursor_x += (6.0 + font.letter_spacing) * wf;
}
}