render: the bodies of a design with several in their own colours

Geometry triangles remember their surface (body); the viewport and the
headless pictures draw each body of a multi-body design, such as the
cells of a partitioned part, in its own colour from the theme (eight in
turn), and a single body in the geometry colour as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
This commit is contained in:
jyvikoandClaude Opus 5.5 authored and Irakli Kourtis committed 2026-10-14 04:56:46 -04:00
1 parent 7f5399c7e9
commit 98d2f55407
4 files changed
+63 -3

No files matched your search

+13 -1
View File
@@ -32,6 +32,8 @@ pub struct Palette {
pub mesh_edge: Rgb,
pub geometry: Rgb,
pub geometry_edge: Rgb,
/// The bodies of a design with several, in turn.
pub bodies: [Rgb; 8],
/// Faces of nodes without a value in a contour plot.
pub no_result: Rgb,
/// The contour colour scale, low to high.
@@ -52,6 +54,16 @@ impl Palette {
mesh_edge: Rgb(0x1e, 0x23, 0x2a),
geometry: Rgb(0xb4, 0xba, 0xc4),
geometry_edge: Rgb(0x2a, 0x30, 0x3a),
bodies: [
Rgb(0x8f, 0xb4, 0xd9),
Rgb(0xe0, 0xa4, 0x6c),
Rgb(0x8c, 0xc4, 0x8a),
Rgb(0xd6, 0x8c, 0x96),
Rgb(0xb0, 0x9c, 0xd8),
Rgb(0xd8, 0xc8, 0x70),
Rgb(0x7c, 0xc4, 0xc0),
Rgb(0xc0, 0xa0, 0x84),
],
no_result: Rgb(0x9a, 0xa0, 0xaa),
contour: [Rgb(0x2b, 0x4f, 0xd8), Rgb(0x2f, 0xb8, 0xe0), Rgb(0x3c, 0xc8, 0x64), Rgb(0xf0, 0xd2, 0x3c), Rgb(0xe0, 0x40, 0x2e)],
glyphs: GlyphColors {
@@ -125,7 +137,7 @@ fn model_scene(d: &DisplayMesh, p: &Palette) -> Scene {
s.tris = d.tris.iter().map(|t| SceneTri { p: t.p, normal: t.normal, color: p.element(t.kind) }).collect();
let faces_drawn = d.tris.is_empty();
if faces_drawn {
s.tris.extend(d.surface_tris.iter().map(|t| SceneTri { p: t.p, normal: t.normal, color: p.geometry }));
s.tris.extend(d.surface_tris.iter().map(|t| SceneTri { p: t.p, normal: t.normal, color: d.body_color(t, p.geometry, &p.bodies) }));
}
s.lines = d.edges.iter().map(|e| SceneLine { a: e.p[0], b: e.p[1], color: p.mesh_edge, width: EDGE_WIDTH }).collect();
let edge = if faces_drawn { p.mesh_edge } else { p.geometry_edge };
+23 -1
View File
@@ -55,6 +55,8 @@ pub struct DisplayNode {
pub struct SurfaceTri {
pub p: [Vec3; 3],
pub normal: Vec3,
/// Which surface (body) it belongs to: the order [`DisplayMesh::add_surface`] got them in.
pub body: u32,
}
/// Display data for a model and its geometry.
@@ -72,6 +74,8 @@ pub struct DisplayMesh {
pub surface_tris: Vec<SurfaceTri>,
/// Geometry edges: where two faces meet, and open boundaries.
pub surface_edges: Vec<[Vec3; 2]>,
/// Geometry surfaces (bodies) added.
pub bodies: u32,
pub bounds: Aabb3,
}
@@ -225,7 +229,7 @@ impl DisplayMesh {
for p in [a, b, c] {
self.bounds.add(p);
}
self.surface_tris.push(SurfaceTri { p: [a, b, c], normal });
self.surface_tris.push(SurfaceTri { p: [a, b, c], normal, body: self.bodies });
let face = tri_face.get(t).copied().unwrap_or(u32::MAX);
let [v0, v1, v2] = [vertex(i0), vertex(i1), vertex(i2)];
for (u, v) in [(v0, v1), (v1, v2), (v2, v0)] {
@@ -241,6 +245,17 @@ impl DisplayMesh {
self.surface_edges.push([a, b]);
}
}
self.bodies = self.bodies.saturating_add(1);
}
/// The colour of a geometry triangle: `single` when there is one body, else body `k`'s
/// colour from `palette` (cycling), so the bodies of a design, such as the cells of a
/// partitioned part, tell apart.
pub fn body_color<C: Copy>(&self, tri: &SurfaceTri, single: C, palette: &[C]) -> C {
if self.bodies <= 1 || palette.is_empty() {
return single;
}
palette.get(tri.body as usize % palette.len()).copied().unwrap_or(single)
}
/// Where node `label` is drawn, if it is (nodes are kept in label order).
@@ -401,6 +416,13 @@ mod tests {
welded.add_surface(&soup, &[[0, 1, 2], [3, 4, 5]], &[0, 0]);
assert_eq!(welded.surface_edges.len(), 4);
let mut hostile = DisplayMesh::default();
let mut two = DisplayMesh::default();
two.add_surface(&p, &[[0, 1, 2]], &[0]);
two.add_surface(&p, &[[0, 2, 3]], &[0]);
assert_eq!(two.bodies, 2);
let colors: Vec<u8> = two.surface_tris.iter().map(|t| two.body_color(t, 0u8, &[7, 8])).collect();
assert_eq!(colors, vec![7, 8]);
assert_eq!(one_face.body_color(&one_face.surface_tris[0], 0u8, &[7, 8]), 0);
hostile.add_surface(&[[f64::NAN, 0., 0.], [1., 0., 0.], [0., 1., 0.]], &[[0, 1, 2], [u32::MAX, 0, 1]], &[]);
assert!(hostile.surface_tris.is_empty() && hostile.surface_edges.is_empty());
}
+5 -1
View File
@@ -79,7 +79,11 @@ pub fn build(display: &DisplayMesh, style: Style, show: Show, t: &Tokens) -> Sce
s.tris = display.tris.iter().map(|d| SceneTri { p: d.p, normal: d.normal, color: rgb(t.element(d.kind)) }).collect();
}
if !mesh_faces && show.geometry {
s.tris.extend(display.surface_tris.iter().map(|d| SceneTri { p: d.p, normal: d.normal, color: rgb(t.geometry) }));
s.tris.extend(display.surface_tris.iter().map(|d| SceneTri {
p: d.p,
normal: d.normal,
color: rgb(display.body_color(d, t.geometry, &t.bodies)),
}));
}
}
if style != Style::Shaded && show.mesh {
+22
View File
@@ -66,6 +66,8 @@ pub struct Tokens {
pub geometry: Color32,
/// Their face boundaries.
pub geometry_edge: Color32,
/// The bodies of a design with several (the cells of a partitioned part), in turn.
pub bodies: [Color32; 8],
// ---- results ----
/// The contour colour scale, low to high (the same in both themes).
pub contour: [Color32; 5],
@@ -137,6 +139,16 @@ impl Tokens {
node: Color32::from_rgb(0xb4, 0x3c, 0x8c),
geometry: Color32::from_rgb(176, 186, 198),
geometry_edge: Color32::from_rgb(40, 44, 52),
bodies: [
Color32::from_rgb(0x8f, 0xb4, 0xd9),
Color32::from_rgb(0xe0, 0xa4, 0x6c),
Color32::from_rgb(0x8c, 0xc4, 0x8a),
Color32::from_rgb(0xd6, 0x8c, 0x96),
Color32::from_rgb(0xb0, 0x9c, 0xd8),
Color32::from_rgb(0xd8, 0xc8, 0x70),
Color32::from_rgb(0x7c, 0xc4, 0xc0),
Color32::from_rgb(0xc0, 0xa0, 0x84),
],
contour: CONTOUR,
no_result: Color32::from_rgb(0x9a, 0xa0, 0xaa),
glyph_constraint: Color32::from_rgb(0x00, 0x8a, 0xa8),
@@ -188,6 +200,16 @@ impl Tokens {
t.node = Color32::from_rgb(0xf2, 0xe6, 0x6e);
t.geometry = Color32::from_rgb(152, 156, 162);
t.geometry_edge = Color32::from_rgb(16, 16, 18);
t.bodies = [
Color32::from_rgb(0x6c, 0x92, 0xb8),
Color32::from_rgb(0xbc, 0x84, 0x50),
Color32::from_rgb(0x6c, 0xa2, 0x6a),
Color32::from_rgb(0xb4, 0x6e, 0x78),
Color32::from_rgb(0x90, 0x7e, 0xb6),
Color32::from_rgb(0xb6, 0xa6, 0x54),
Color32::from_rgb(0x5c, 0xa2, 0x9e),
Color32::from_rgb(0xa0, 0x82, 0x66),
];
t.no_result = Color32::from_rgb(0x70, 0x76, 0x80);
t.glyph_constraint = Color32::from_rgb(0x3c, 0xdc, 0xf0);
t.glyph_load = Color32::from_rgb(0xff, 0x5a, 0x4c);