From 9f6047006744687e6c7b3af6c80c8b1a70765247 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Tue, 7 Jul 2026 08:18:08 +0300 Subject: [PATCH] feat(text): suppress glyph strokes when SDF text is on MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With OCS_TEXT_SDF set, text now renders only as SDF quads: the wire path skips building glyph-stroke geometry (bins stays empty, so the existing empty-wire branch still emits the insertion snap point). This removes the double draw (strokes + SDF) and, as a side effect, fixes the long-standing snap pollution (#276) — snapping near text no longer catches individual glyph-stroke endpoints, only the insertion point. Shared sdf_text_enabled() (cached env read) gates both the collector and the stroke suppression so they stay in lock-step. Flag off: unchanged. Verified in-app: TEXT/MTEXT/decorations render clean as single SDF, and snap no longer hits glyph strokes. Click-pick on text is limited while the flag is on (no stroke geometry) pending a text OBB pick box. Co-Authored-By: Claude Opus 4.8 --- src/scene/convert/tessellate.rs | 8 +++++++- src/scene/text/sdf_atlas.rs | 8 ++++++++ src/scene/view/render.rs | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/scene/convert/tessellate.rs b/src/scene/convert/tessellate.rs index 24ef44997..5bb014222 100644 --- a/src/scene/convert/tessellate.rs +++ b/src/scene/convert/tessellate.rs @@ -198,7 +198,13 @@ pub fn tessellate( }; let anno = anno_scale as f64; - for group in &stroke_groups { + // With SDF text on, glyphs render as quads elsewhere — skip + // building the stroke geometry so text is not double-drawn. + // `bins` stays empty, so the empty-wire path below still emits + // the insertion snap point (text stays snappable, no glyph + // strokes polluting snap/pick). + let sdf_text = crate::scene::text::sdf_atlas::sdf_text_enabled(); + for group in stroke_groups.iter().filter(|_| !sdf_text) { let lx_v = group.origin[0]; let ly_v = group.origin[1]; let slx_v = (lx_v - ref_lx_v) * anno + ref_lx_v; diff --git a/src/scene/text/sdf_atlas.rs b/src/scene/text/sdf_atlas.rs index dccf4f637..d8b3b7242 100644 --- a/src/scene/text/sdf_atlas.rs +++ b/src/scene/text/sdf_atlas.rs @@ -39,6 +39,14 @@ pub fn text_atlas() -> &'static Mutex { ATLAS.get_or_init(|| Mutex::new(GlyphAtlas::new(1024, 1024))) } +/// Whether SDF text rendering is enabled (env `OCS_TEXT_SDF`), read once. +/// When on, the wire path suppresses text glyph strokes so text draws only as +/// SDF quads (no double draw); the insertion snap point is kept. +pub fn sdf_text_enabled() -> bool { + static ON: OnceLock = OnceLock::new(); + *ON.get_or_init(|| std::env::var_os("OCS_TEXT_SDF").is_some()) +} + // ── Bake configuration ────────────────────────────────────────────────────── /// Texels per glyph unit. Glyphs live in a 9-unit cap-height space, so a cap diff --git a/src/scene/view/render.rs b/src/scene/view/render.rs index 049145d32..ff6893da9 100644 --- a/src/scene/view/render.rs +++ b/src/scene/view/render.rs @@ -784,7 +784,7 @@ impl Scene { pub(in crate::scene) fn sdf_text_vertices( &self, ) -> Vec { - self.sdf_text_vertices_enabled(std::env::var_os("OCS_TEXT_SDF").is_some()) + self.sdf_text_vertices_enabled(crate::scene::text::sdf_atlas::sdf_text_enabled()) } #[allow(dead_code)]