fix(text): keep inline \C colours through block colour inheritance (SDF)
Ports PR #301 (by Kevin Griffin) to the SDF text renderer. Block-nested MTEXT with inline colour codes (\C1;, \c…) collapsed to a single colour: the block cache applies ByBlock / layer-0 colour inheritance per entity, so at emit time every segment's colour was replaced with the inherited colour — including segments carrying an explicit inline override. - block_cache tessellate_sub_local: gate ByBlock / layer-0 colour inheritance on `wire_on_base_color` (a wire whose colour differs from the entity base carries an explicit override and keeps it) — #301's fix, still correct for the remaining stroke wires. - block_cache emit_wire: the per-vertex analogue for SDF text — a glyph whose colour equals the wire's base inherits the resolved colour; a glyph with an inline \C / \c override keeps it. (Previously every block glyph was recoloured with final_color, collapsing colour-split MTEXT.) - tests/text_font_rendering.rs: restore the block colour-split regression test (it had stopped compiling on main when expand_insert gained the InheritStyle param) and adapt it — and drawable_point_count — to SDF text_verts, since block MTEXT now renders as glyph quads, not colour-split stroke wires. \C (ACI) / \c (true-colour) are parsed by acadrust (mtext_format SpanProperties.color) → OCS InlineColor → the glyph's per-vertex colour. Co-Authored-By: Kevin Griffin <117586586+KevinGriffin-new@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
8498b08abf
commit
0964e97cfa
2 changed files with 47 additions and 13 deletions
24
src/scene/cache/block_cache.rs
vendored
24
src/scene/cache/block_cache.rs
vendored
|
|
@ -447,6 +447,13 @@ fn tessellate_sub_local(
|
||||||
.chain(wire.text_verts.iter().map(|v| v.pos)),
|
.chain(wire.text_verts.iter().map(|v| v.pos)),
|
||||||
);
|
);
|
||||||
let is_fill_only = wire.points.is_empty() && !wire.fill_tris.is_empty();
|
let is_fill_only = wire.points.is_empty() && !wire.fill_tris.is_empty();
|
||||||
|
// A wire whose colour differs from the entity's resolved base colour
|
||||||
|
// carries an explicit per-segment override (e.g. an MTEXT `\C1;` inline
|
||||||
|
// colour). ByBlock / layer-0 inheritance applies only to wires still on
|
||||||
|
// the base colour — folding an explicit segment into the inherited
|
||||||
|
// colour would collapse colour-split geometry to one colour. (PR #301,
|
||||||
|
// Kevin Griffin — extended to SDF text per-vertex in emit_wire.)
|
||||||
|
let wire_on_base_color = wire.color == sub_color;
|
||||||
|
|
||||||
result.push(LocalWire {
|
result.push(LocalWire {
|
||||||
points: wire.points,
|
points: wire.points,
|
||||||
|
|
@ -464,10 +471,10 @@ fn tessellate_sub_local(
|
||||||
line_weight_px: lw_px,
|
line_weight_px: lw_px,
|
||||||
plinegen: wire.plinegen,
|
plinegen: wire.plinegen,
|
||||||
is_fill_only,
|
is_fill_only,
|
||||||
color_is_byblock,
|
color_is_byblock: color_is_byblock && wire_on_base_color,
|
||||||
lt_is_byblock,
|
lt_is_byblock,
|
||||||
lw_is_byblock,
|
lw_is_byblock,
|
||||||
color_l0,
|
color_l0: color_l0 && wire_on_base_color,
|
||||||
lt_l0,
|
lt_l0,
|
||||||
lw_l0,
|
lw_l0,
|
||||||
aabb_local,
|
aabb_local,
|
||||||
|
|
@ -1243,11 +1250,22 @@ fn emit_wire(
|
||||||
if hy > entry.max_y {
|
if hy > entry.max_y {
|
||||||
entry.max_y = hy;
|
entry.max_y = hy;
|
||||||
}
|
}
|
||||||
|
// Base glyphs inherit the resolved (ByBlock / layer-0) colour; a glyph
|
||||||
|
// carrying an inline `\C` / `\c` override — colour differs from the
|
||||||
|
// wire's base — keeps it, so block-nested colour-split MTEXT stays
|
||||||
|
// multi-colour. Per-vertex analogue of PR #301's wire-level gate.
|
||||||
|
let rgb = if [tv.color[0], tv.color[1], tv.color[2]]
|
||||||
|
== [lw.color[0], lw.color[1], lw.color[2]]
|
||||||
|
{
|
||||||
|
[final_color[0], final_color[1], final_color[2]]
|
||||||
|
} else {
|
||||||
|
[tv.color[0], tv.color[1], tv.color[2]]
|
||||||
|
};
|
||||||
entry.text_verts.push(crate::scene::pipeline::text_gpu::TextVertex {
|
entry.text_verts.push(crate::scene::pipeline::text_gpu::TextVertex {
|
||||||
pos: [hx, hy, hz],
|
pos: [hx, hy, hz],
|
||||||
pos_low: [lx, ly, lz],
|
pos_low: [lx, ly, lz],
|
||||||
uv: tv.uv,
|
uv: tv.uv,
|
||||||
color: [final_color[0], final_color[1], final_color[2], tv.color[3]],
|
color: [rgb[0], rgb[1], rgb[2], tv.color[3]],
|
||||||
draw_depth: tv.draw_depth,
|
draw_depth: tv.draw_depth,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,19 @@ use acadrust::tables::{BlockRecord, TextStyle};
|
||||||
use acadrust::types::Vector3;
|
use acadrust::types::Vector3;
|
||||||
use acadrust::{CadDocument, EntityType, Handle};
|
use acadrust::{CadDocument, EntityType, Handle};
|
||||||
use OpenCADStudio::scene::cache::block_cache::{expand_insert, BlockCache};
|
use OpenCADStudio::scene::cache::block_cache::{expand_insert, BlockCache};
|
||||||
|
use OpenCADStudio::scene::view::render::InheritStyle;
|
||||||
use OpenCADStudio::scene::WireModel;
|
use OpenCADStudio::scene::WireModel;
|
||||||
|
|
||||||
fn drawable_point_count(wires: &[WireModel]) -> usize {
|
fn drawable_point_count(wires: &[WireModel]) -> usize {
|
||||||
|
// SDF text carries glyph quads on `text_verts` (no stroke points/fills), so
|
||||||
|
// count those too — otherwise "did the text render?" reads as zero.
|
||||||
wires
|
wires
|
||||||
.iter()
|
.iter()
|
||||||
.map(|w| w.points.iter().filter(|p| p[0].is_finite()).count() + w.fill_tris.len())
|
.map(|w| {
|
||||||
|
w.points.iter().filter(|p| p[0].is_finite()).count()
|
||||||
|
+ w.fill_tris.len()
|
||||||
|
+ w.text_verts.len()
|
||||||
|
})
|
||||||
.sum()
|
.sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,6 +58,14 @@ fn expand_block_mtext(
|
||||||
0.0,
|
0.0,
|
||||||
[0.0; 8],
|
[0.0; 8],
|
||||||
1.0,
|
1.0,
|
||||||
|
// The INSERT sits on layer "0" which resolves to the white/Continuous
|
||||||
|
// fallback — matching the resolved style passed above.
|
||||||
|
InheritStyle {
|
||||||
|
color: [1.0, 1.0, 1.0, 1.0],
|
||||||
|
pat_len: 0.0,
|
||||||
|
pat: [0.0; 8],
|
||||||
|
lw_px: 1.0,
|
||||||
|
},
|
||||||
false,
|
false,
|
||||||
1.0,
|
1.0,
|
||||||
None,
|
None,
|
||||||
|
|
@ -91,9 +106,10 @@ fn block_nested_mtext_uses_its_style_font() {
|
||||||
#[test]
|
#[test]
|
||||||
fn block_nested_colour_split_mtext_keeps_per_wire_colour() {
|
fn block_nested_colour_split_mtext_keeps_per_wire_colour() {
|
||||||
// `\C1;` = ACI red, `\C2;` = ACI yellow → two colour bins. The block cache
|
// `\C1;` = ACI red, `\C2;` = ACI yellow → two colour bins. The block cache
|
||||||
// must keep them as separate per-wire colours; the fold-to-one-colour bug
|
// must keep them as separate per-glyph colours; the fold-to-one-colour bug
|
||||||
// this PR fixes silently collapsed every segment to the first colour. Uses
|
// (PR #301, Kevin Griffin) collapsed every segment to the inherited colour.
|
||||||
// the builtin stroke font so the split is observable without a system TTF.
|
// SDF text carries per-glyph colour on `text_verts`, so the split is checked
|
||||||
|
// there (not per-wire). Uses the builtin stroke font so it works without TTF.
|
||||||
let wires = expand_block_mtext(
|
let wires = expand_block_mtext(
|
||||||
"\\C1;AAA\\C2;BBB",
|
"\\C1;AAA\\C2;BBB",
|
||||||
"txt",
|
"txt",
|
||||||
|
|
@ -103,12 +119,12 @@ fn block_nested_colour_split_mtext_keeps_per_wire_colour() {
|
||||||
|
|
||||||
let mut colours: Vec<[u8; 3]> = wires
|
let mut colours: Vec<[u8; 3]> = wires
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|w| !w.points.is_empty() || !w.fill_tris.is_empty())
|
.flat_map(|w| w.text_verts.iter())
|
||||||
.map(|w| {
|
.map(|v| {
|
||||||
[
|
[
|
||||||
(w.color[0] * 255.0).round() as u8,
|
(v.color[0] * 255.0).round() as u8,
|
||||||
(w.color[1] * 255.0).round() as u8,
|
(v.color[1] * 255.0).round() as u8,
|
||||||
(w.color[2] * 255.0).round() as u8,
|
(v.color[2] * 255.0).round() as u8,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
@ -117,7 +133,7 @@ fn block_nested_colour_split_mtext_keeps_per_wire_colour() {
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
colours.len() >= 2,
|
colours.len() >= 2,
|
||||||
"colour-split MTEXT in a block must keep ≥2 distinct wire colours, got {colours:?}"
|
"colour-split MTEXT in a block must keep ≥2 distinct glyph colours, got {colours:?}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue