ui-egui: select faces, edges, vertices, bodies, nodes and elements by picking

A click in the viewport picks what is under the pointer through the
selection filter (the selection bar, top centre): geometry faces, edges,
vertices and bodies, or mesh nodes, element faces and elements. Shift
adds, Cmd/Ctrl adds or removes, a click on nothing clears, Escape
clears; with Box on, a left drag selects the visible nodes, element
faces or elements inside the box. What a click would pick is
highlighted under the pointer with its name beside it.

The selection is kept in the model's persistent terms: geometry by name
(GeometryRef, named through the mesh's geometry tags or the drawn
bodies' face names), so it survives a remesh, and the mesh by label
(labels the model loses drop out). It reads as a set's members.

Picking runs on the CPU against the triangles both renderers draw. The
selection and the hover are drawn as an overlay in the theme's new
pick_selected and pick_hover colours (light and dark), outlined; the GPU
draws it as a second small layer, so a hover change uploads only that.

Command forms that apply to part of the model (sets, boundary
conditions, loads, sections, couplings) get a Target row whose Use
selection fills the members, or makes a set of the selection and names
it. A right click offers Create set from selection.

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 08:16:26 -04:00
1 parent 32c67d1988
commit 9e269a9f45
8 files changed
+1686 -59

No files matched your search

+28
View File
@@ -82,6 +82,10 @@ pub struct Tokens {
pub glyph_interaction: Color32,
/// Temperatures, heat fluxes, convection and heat sources.
pub glyph_thermal: Color32,
// ---- selection ----
/// What is selected in the viewport, and what a click would pick under the pointer.
pub pick_selected: Color32,
pub pick_hover: Color32,
}
/// The contour colour scale: blue, cyan, green, yellow, red.
@@ -155,6 +159,8 @@ impl Tokens {
glyph_load: Color32::from_rgb(0xd0, 0x24, 0x24),
glyph_interaction: Color32::from_rgb(0x9a, 0x30, 0xc0),
glyph_thermal: Color32::from_rgb(0xe8, 0x4c, 0x00),
pick_selected: Color32::from_rgb(0xd6, 0x00, 0x7f),
pick_hover: Color32::from_rgb(0xf0, 0x7c, 0xc0),
}
}
@@ -215,6 +221,8 @@ impl Tokens {
t.glyph_load = Color32::from_rgb(0xff, 0x5a, 0x4c);
t.glyph_interaction = Color32::from_rgb(0xd2, 0x82, 0xff);
t.glyph_thermal = Color32::from_rgb(0xff, 0x8c, 0x00);
t.pick_selected = Color32::from_rgb(0xff, 0x3f, 0xb4);
t.pick_hover = Color32::from_rgb(0xff, 0xa6, 0xdc);
t
}
@@ -368,6 +376,26 @@ mod tests {
}
}
/// The selection and hover colours differ from each other, from every element, geometry,
/// node and symbol colour, and from the background.
#[test]
fn selection_colours_stand_out() {
let far = |a: Color32, b: Color32| {
(i32::from(a.r()) - i32::from(b.r())).abs() + (i32::from(a.g()) - i32::from(b.g())).abs() + (i32::from(a.b()) - i32::from(b.b())).abs()
};
for dark in [true, false] {
let t = Tokens::get(dark);
assert!(far(t.pick_selected, t.pick_hover) > 90, "dark={dark}");
for c in [t.pick_selected, t.pick_hover] {
let others =
[t.geometry, t.node, t.glyph_constraint, t.glyph_load, t.glyph_interaction, t.glyph_thermal, t.viewport_bottom, t.mesh_edge];
for o in others.into_iter().chain(ElementKind::ALL.map(|k| t.element(k))) {
assert!(far(c, o) > 100, "{c:?} vs {o:?} dark={dark}");
}
}
}
}
/// Every element kind has a colour, and edges stand out from the faces they lie on.
#[test]
fn element_colours_contrast_with_edges() {