Colours in exports from materials and appearances; Appearance command

- Bodies export (STEP, 3MF) in the colour they are shown in: an appearance,
  else the physical material's look, else the colour the body was imported
  with (Document::body_color / painted). Physical materials used to export
  no colour at all.
- AppearanceCommand (Modify): a colour per body ("#rrggbb" or [r, g, b];
  null follows the material again), kept in the design and across renames.
- Material looks move to solvecraft-doc (material_color); the viewport draws
  appearance, material or imported colour, as exported.
- Engine test: brass and red ABS bodies export, re-import with their RGB, and
  keep it through open and export again.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
bherbruckandClaude Opus 5.5 committed 2026-10-08 08:11:28 +00:00
1 parent 578b35e88a
commit ae4570ed39
8 files changed
+200 -86

No files matched your search

+1 -16
View File
@@ -262,22 +262,7 @@ impl Tokens {
/// How a physical material looks (the default appearance follows the material).
pub fn material_color(name: &str) -> Option<Color32> {
Some(match name {
"Steel" => Color32::from_rgb(158, 164, 172),
"Stainless Steel" => Color32::from_rgb(186, 191, 198),
"Aluminum" => Color32::from_rgb(204, 208, 214),
"Brass" => Color32::from_rgb(208, 172, 88),
"Copper" => Color32::from_rgb(204, 122, 80),
"Titanium" => Color32::from_rgb(150, 148, 158),
"Cast Iron" => Color32::from_rgb(104, 104, 110),
"ABS Plastic" => Color32::from_rgb(222, 222, 216),
"PLA" => Color32::from_rgb(110, 160, 222),
"Nylon" => Color32::from_rgb(236, 230, 214),
"Polycarbonate" => Color32::from_rgb(196, 216, 232),
"Oak" => Color32::from_rgb(190, 146, 92),
"Glass" => Color32::from_rgb(170, 208, 218),
_ => return None,
})
solvecraft_engine::doc::material_color(name).map(|[r, g, b]| Color32::from_rgb(r, g, b))
}
/// Switch themes (the visuals follow on the next [`apply`]).
+10 -3
View File
@@ -215,7 +215,7 @@ fn build_scene(app: &SolveApp) -> GpuScene {
}
// With a component active, bodies outside it are drawn faded.
let faded = s.active_component != 0 && !s.doc.component_within(s.doc.body_component(&b.name, b.feature), s.active_component);
let base = s.doc.materials.get(&b.name).and_then(|m| crate::theme::material_color(m)).unwrap_or(tk.body);
let base = body_color(&s.doc, &b.name, &b.body).unwrap_or(tk.body);
let col = if faded { c4(base.gamma_multiply(0.35)) } else { c4(base) };
let m = b.mesh();
let (shaded, edges) = match app.ui.visual_style {
@@ -930,9 +930,16 @@ fn build_highlight(app: &SolveApp) -> GpuScene {
sc
}
/// A body's display colour (its material's, else the theme's) as RGB.
/// A body's display colour (appearance, material or imported colour), if it has one.
fn body_color(doc: &solvecraft_engine::doc::Document, name: &str, body: &solvecraft_engine::kernel::Body) -> Option<Color32> {
let to8 = |x: f32| (x.clamp(0.0, 1.0) * 255.0).round() as u8;
doc.body_color(name, body).map(|[r, g, b]| Color32::from_rgb(to8(r), to8(g), to8(b)))
}
/// A body's display colour (else the theme's) as RGB.
fn body_rgb(app: &SolveApp, name: &str) -> (u8, u8, u8) {
let c = app.session.doc.materials.get(name).and_then(|m| crate::theme::material_color(m)).unwrap_or(Tokens::get().body);
let st = app.session.model.state();
let c = st.body(name).and_then(|b| body_color(&app.session.doc, name, &b.body)).unwrap_or(Tokens::get().body);
(c.r(), c.g(), c.b())
}