feat(proxy): honour proxy-graphics colour traits
The preview stream sets a current colour (record type 14, an ACI) before its geometry; track it and tag each decoded primitive. The Unknown-entity path groups primitives by colour and draws one wire per colour, resolving a real index (1..=255) to its RGB and letting ByLayer/ByBlock inherit the entity colour. So a custom entity's preview now renders in its own colours (e.g. an ACA door's blue swing) instead of a single flat colour. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
18d116bb33
commit
1d117f514c
2 changed files with 54 additions and 20 deletions
|
|
@ -33,12 +33,22 @@
|
||||||
/// line strips.
|
/// line strips.
|
||||||
pub struct ProxyPolyline {
|
pub struct ProxyPolyline {
|
||||||
pub points: Vec<[f64; 3]>,
|
pub points: Vec<[f64; 3]>,
|
||||||
|
/// The colour in force when this primitive was emitted, as an AutoCAD
|
||||||
|
/// Color Index. 256 = ByLayer / 0 = ByBlock (inherit the entity's colour);
|
||||||
|
/// 1..=255 override it. Set from the preview's colour traits.
|
||||||
|
pub color: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// AutoCAD Color Index meaning "inherit from the layer" — the default until a
|
||||||
|
/// colour trait says otherwise.
|
||||||
|
const COLOR_BYLAYER: i32 = 256;
|
||||||
|
|
||||||
const REC_ARC: u32 = 4;
|
const REC_ARC: u32 = 4;
|
||||||
const REC_ARC5: u32 = 5;
|
const REC_ARC5: u32 = 5;
|
||||||
const REC_POLYLINE: u32 = 6;
|
const REC_POLYLINE: u32 = 6;
|
||||||
const REC_POLYGON: u32 = 7;
|
const REC_POLYGON: u32 = 7;
|
||||||
|
/// Trait record that sets the current colour (ACI) for following primitives.
|
||||||
|
const REC_COLOR: u32 = 14;
|
||||||
|
|
||||||
fn u32_at(b: &[u8], o: usize) -> Option<u32> {
|
fn u32_at(b: &[u8], o: usize) -> Option<u32> {
|
||||||
b.get(o..o + 4).map(|s| u32::from_le_bytes([s[0], s[1], s[2], s[3]]))
|
b.get(o..o + 4).map(|s| u32::from_le_bytes([s[0], s[1], s[2], s[3]]))
|
||||||
|
|
@ -74,6 +84,7 @@ pub fn decode(blob: &[u8]) -> Vec<ProxyPolyline> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut pos = 8usize;
|
let mut pos = 8usize;
|
||||||
|
let mut color = COLOR_BYLAYER;
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
let Some(rsize) = u32_at(blob, pos) else { break };
|
let Some(rsize) = u32_at(blob, pos) else { break };
|
||||||
let rsize = rsize as usize;
|
let rsize = rsize as usize;
|
||||||
|
|
@ -82,6 +93,11 @@ pub fn decode(blob: &[u8]) -> Vec<ProxyPolyline> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
match rtype {
|
match rtype {
|
||||||
|
REC_COLOR => {
|
||||||
|
if let Some(c) = u32_at(blob, pos + 8) {
|
||||||
|
color = c as i32;
|
||||||
|
}
|
||||||
|
}
|
||||||
REC_POLYLINE | REC_POLYGON => {
|
REC_POLYLINE | REC_POLYGON => {
|
||||||
if let Some(n) = u32_at(blob, pos + 8) {
|
if let Some(n) = u32_at(blob, pos + 8) {
|
||||||
let n = n as usize;
|
let n = n as usize;
|
||||||
|
|
@ -101,18 +117,18 @@ pub fn decode(blob: &[u8]) -> Vec<ProxyPolyline> {
|
||||||
pts.push(first);
|
pts.push(first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.push(ProxyPolyline { points: pts });
|
out.push(ProxyPolyline { points: pts, color });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
REC_ARC | REC_ARC5 => {
|
REC_ARC | REC_ARC5 => {
|
||||||
if let Some(poly) = decode_arc(blob, pos) {
|
if let Some(points) = decode_arc(blob, pos) {
|
||||||
out.push(poly);
|
out.push(ProxyPolyline { points, color });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Every other record is a trait (colour, layer, lineweight, …) —
|
// Every other record is a trait (layer, lineweight, …) — skipped
|
||||||
// skipped in phase 1; `rsize` still advances us past it.
|
// for now; `rsize` still advances us past it.
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
pos += rsize;
|
pos += rsize;
|
||||||
|
|
@ -121,8 +137,9 @@ pub fn decode(blob: &[u8]) -> Vec<ProxyPolyline> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flatten a circular-arc record (centre, radius, normal, start direction,
|
/// Flatten a circular-arc record (centre, radius, normal, start direction,
|
||||||
/// sweep) into a poly-line. The normal's Z sign gives the sweep direction.
|
/// sweep) into a strip of points. The normal's Z sign gives the sweep
|
||||||
fn decode_arc(blob: &[u8], pos: usize) -> Option<ProxyPolyline> {
|
/// direction.
|
||||||
|
fn decode_arc(blob: &[u8], pos: usize) -> Option<Vec<[f64; 3]>> {
|
||||||
let center = pt3_at(blob, pos + 8)?;
|
let center = pt3_at(blob, pos + 8)?;
|
||||||
let radius = f64_at(blob, pos + 32)?;
|
let radius = f64_at(blob, pos + 32)?;
|
||||||
let normal = pt3_at(blob, pos + 40)?;
|
let normal = pt3_at(blob, pos + 40)?;
|
||||||
|
|
@ -144,7 +161,7 @@ fn decode_arc(blob: &[u8], pos: usize) -> Option<ProxyPolyline> {
|
||||||
center[2],
|
center[2],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
Some(ProxyPolyline { points })
|
Some(points)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -246,22 +246,39 @@ pub(crate) fn tessellate_entity(
|
||||||
if let Some(blob) = e.common().graphic_data.as_ref() {
|
if let Some(blob) = e.common().graphic_data.as_ref() {
|
||||||
let polys = convert::proxy_graphics::decode(blob);
|
let polys = convert::proxy_graphics::decode(blob);
|
||||||
if !polys.is_empty() {
|
if !polys.is_empty() {
|
||||||
// One wire for the whole preview: concatenate the poly-lines
|
// Group primitives by their preview colour so each colour draws
|
||||||
// with NaN separators (arcs are already flattened to points).
|
// in its own wire (the poly-lines within share a wire, joined by
|
||||||
|
// NaN separators; arcs are already flattened to points).
|
||||||
|
use std::collections::BTreeMap;
|
||||||
let nan = [f64::NAN; 3];
|
let nan = [f64::NAN; 3];
|
||||||
let mut pts64: Vec<[f64; 3]> = Vec::new();
|
let mut by_color: BTreeMap<i32, Vec<[f64; 3]>> = BTreeMap::new();
|
||||||
for poly in &polys {
|
for poly in &polys {
|
||||||
if !pts64.is_empty() {
|
let buf = by_color.entry(poly.color).or_default();
|
||||||
pts64.push(nan);
|
if !buf.is_empty() {
|
||||||
|
buf.push(nan);
|
||||||
}
|
}
|
||||||
pts64.extend_from_slice(&poly.points);
|
buf.extend_from_slice(&poly.points);
|
||||||
}
|
}
|
||||||
let (pts, pts_low) = convert::tessellate::points_to_ds(pts64);
|
let mut wires = Vec::with_capacity(by_color.len());
|
||||||
let mut w = WireModel::solid(h.value().to_string(), pts, entity_color, sel);
|
for (pcolor, pts64) in by_color {
|
||||||
w.points_low = pts_low;
|
// A real ACI index overrides the entity colour; ByLayer
|
||||||
w.line_weight_px = line_weight_px;
|
// (256) / ByBlock (0) inherit it.
|
||||||
w.aci = aci;
|
let (col, w_aci) = if (1..=255).contains(&pcolor) {
|
||||||
return vec![w];
|
let rgba = convert::tess_util::aci_to_rgba(
|
||||||
|
&acadrust::types::Color::Index(pcolor as u8),
|
||||||
|
);
|
||||||
|
(view::render::adapt_to_bg(rgba, bg_color), pcolor as u8)
|
||||||
|
} else {
|
||||||
|
(entity_color, aci)
|
||||||
|
};
|
||||||
|
let (pts, pts_low) = convert::tessellate::points_to_ds(pts64);
|
||||||
|
let mut w = WireModel::solid(h.value().to_string(), pts, col, sel);
|
||||||
|
w.points_low = pts_low;
|
||||||
|
w.line_weight_px = line_weight_px;
|
||||||
|
w.aci = w_aci;
|
||||||
|
wires.push(w);
|
||||||
|
}
|
||||||
|
return wires;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue