forked from 1b-agent/opencadstudio-upstream
fix(text): render TTF glyphs as solid fills, fix font fallback, correct Z-order
Text elements previously rendered as hollow wireframe outlines and sometimes fell back to the wrong system font due to case-sensitive exact matching. Also, the viewport background grid drew on top of solid entities. Case-insensitive and partial-name fallback matching (e.g., mapping "arialn" to "Arial Narrow") is now added to `sysfont::canonical_family_name`, ensuring text resolves to the correct installed system font. TrueType font contours are now triangulated into solid fills (`fill_tris`) using `lyon_tessellation` in `ttf_glyph.rs`. To prevent the GPU from misclassifying this 2D text as a 3D mesh (which broke flat shading), text tessellation splits the output into separate outline and fill `WireModel`s. The background grid is extracted from `SelectionCanvas` into `GridCanvas` and moved beneath the 3D viewport to ensure proper layering. Finally, `DepthBiasState` is enabled on all face/fill pipelines to apply a polygon offset, perfectly resolving Z-fighting between coplanar wireframe lines and solid faces.
This commit is contained in:
1 parent
c599c2ba09
commit
627761b629
20 files changed
+624
-191
No files matched your search
@@ -45,14 +45,17 @@ impl Face {
|
||||
/// Resolve a style's font name to a concrete face. Embedded stroke fonts
|
||||
/// take priority; only otherwise-unknown names try the system fonts.
|
||||
pub fn resolve(font_name: &str) -> Face {
|
||||
if !lff::is_builtin(font_name) && sysfont::has_family(font_name) {
|
||||
let word = ttf_glyph::glyph(font_name, ' ')
|
||||
let is_builtin = lff::is_builtin(font_name);
|
||||
let has_sys = sysfont::has_family(font_name);
|
||||
if !is_builtin && has_sys {
|
||||
let canonical = sysfont::canonical_family_name(font_name).unwrap_or_else(|| font_name.to_string());
|
||||
let word = ttf_glyph::glyph(&canonical, ' ')
|
||||
.map(|g| g.advance)
|
||||
// Fall back to a sensible blank-width if the font has no space.
|
||||
.filter(|w| *w > 0.0)
|
||||
.unwrap_or(4.5);
|
||||
return Face::Ttf {
|
||||
family: font_name.to_string(),
|
||||
family: canonical,
|
||||
word,
|
||||
};
|
||||
}
|
||||
@@ -130,7 +133,7 @@ mod tests {
|
||||
.find(|f| ttf_glyph::glyph(f, 'A').is_some())
|
||||
.expect("a system family with an 'A'");
|
||||
assert!(matches!(Face::resolve(fam), Face::Ttf { .. }));
|
||||
let strokes =
|
||||
let (strokes, _) =
|
||||
lff::tessellate_text_ex([0.0, 0.0], 10.0, 0.0, 1.0, 0.0, fam, "ABC");
|
||||
assert!(!strokes.is_empty(), "TTF run produced no strokes");
|
||||
}
|
||||
@@ -168,8 +171,8 @@ mod tests {
|
||||
.expect("a system family with an 'A'");
|
||||
// Underlined shaped text: glyph contours plus exactly one underline
|
||||
// segment (a 2-point polyline) emitted on the \l toggle.
|
||||
let plain = lff::tessellate_text_ex([0.0, 0.0], 10.0, 0.0, 1.0, 0.0, fam, "AB");
|
||||
let deco = lff::tessellate_text_ex([0.0, 0.0], 10.0, 0.0, 1.0, 0.0, fam, "\\LAB\\l");
|
||||
let (plain, _) = lff::tessellate_text_ex([0.0, 0.0], 10.0, 0.0, 1.0, 0.0, fam, "AB");
|
||||
let (deco, _) = lff::tessellate_text_ex([0.0, 0.0], 10.0, 0.0, 1.0, 0.0, fam, "\\LAB\\l");
|
||||
assert!(!plain.is_empty());
|
||||
assert_eq!(
|
||||
deco.len(),
|
||||
@@ -177,4 +180,32 @@ mod tests {
|
||||
"underline should add exactly one segment on the TTF path"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn case_insensitive_ttf_resolution() {
|
||||
let fams = sysfont::families();
|
||||
if fams.is_empty() {
|
||||
eprintln!("no system fonts; skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
// Arial and Cambria are typical on Windows/Mac/Linux
|
||||
for test_name in &["arial", "ARIAL", "ARIALN", "cambria"] {
|
||||
let resolved = Face::resolve(test_name);
|
||||
match resolved {
|
||||
Face::Ttf { family, .. } => {
|
||||
assert!(
|
||||
family == "Arial" || family == "Arial Narrow" || family == "Cambria",
|
||||
"Resolved to unexpected family name: {}", family
|
||||
);
|
||||
}
|
||||
Face::Lff(_) => {
|
||||
// It's possible some test environments don't have Arial or Cambria,
|
||||
// but if they are present in sysfont::families() (or we can fallback),
|
||||
// they should resolve to Ttf. If they aren't installed, Lff fallback is accepted.
|
||||
eprintln!("Font {} resolved to Lff (probably not installed)", test_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user