forked from 1b-agent/fieldcraft-upstream
Setup symbols: wireframe glyphs per held degree of freedom, a constant size on screen
Boundary condition, load and interaction symbols are wireframes (thin lines, no shaded cones), so they never hide the mesh or a contour plot: - a crossed wire triangle per constrained translation, tip on the node, coming from outside the part where the set's faces say which side that is; a held rotation adds a smaller triangle behind it (the double mark); fixed shows all three axes, a symmetry plane only its normal; - forces as open-headed arrows ending on the node, as long as four support units at one place and 2.5 over a set, so loads and supports stay in proportion (#78); moments as an axis line and a right-handed curved arrow (#79); contact and tie surfaces as small squares on their faces; - a constant size on screen at any zoom (#80): the symbol unit is the size setting (View > Symbol size, `ui.set {symbol_size}`, 14 pt by default) times the model length a pixel spans, rounded to a quarter of a doubling; - spread evenly on a grid a few symbol units across, so their density follows the screen, never more than 400 per definition; - a layer of their own on the desktop (GPU and CPU): zooming rebuilds and uploads the symbols only, never the mesh's scene; framing leaves them out; - colour per kind from the theme's tokens (a new contact colour), and the definition selected in the browser drawn in the selection colour, wider; - depth-tested against the model with the line bias that keeps them on their own surface; a load at a reference point or a single node and a reference point's cross are drawn over the part (#78). Headless pictures (MCP screenshot, `ui.render`) frame the camera first and draw the symbols at the default size for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
1 parent
1fcc7acf84
commit
52c12e2ad7
13 files changed
+1221
-436
No files matched your search
@@ -363,6 +363,14 @@ fn gradient(painter: &egui::Painter, rect: Rect, top: Color32, bottom: Color32)
|
||||
|
||||
fn paint_gpu(app: &mut ModelApp, ui: &egui::Ui, painter: &egui::Painter, rect: Rect) {
|
||||
let ppp = ui.ctx().pixels_per_point();
|
||||
// The symbols first: the scene's clipping radius holds them.
|
||||
let symbols_key = app.symbols().0;
|
||||
if app.viewport.symbols_uploaded != Some(symbols_key)
|
||||
&& let Ok(mut slot) = app.viewport.symbols_slot.lock()
|
||||
{
|
||||
*slot = Some(GpuScene::from_scene(&app.symbols.1, ppp));
|
||||
app.viewport.symbols_uploaded = Some(symbols_key);
|
||||
}
|
||||
let key = app.scene().0;
|
||||
let radius = app.scene.1.radius;
|
||||
if app.viewport.uploaded != Some(key) {
|
||||
@@ -385,6 +393,8 @@ fn paint_gpu(app: &mut ModelApp, ui: &egui::Ui, painter: &egui::Painter, rect: R
|
||||
let cb = ViewportCallback {
|
||||
key,
|
||||
slot: app.viewport.slot.clone(),
|
||||
symbols_key,
|
||||
symbols_slot: app.viewport.symbols_slot.clone(),
|
||||
overlay_key,
|
||||
overlay_slot: app.viewport.overlay_slot.clone(),
|
||||
view_proj: cam.view_proj(aspect, radius).to_f32(),
|
||||
@@ -405,11 +415,12 @@ fn paint_cpu(app: &mut ModelApp, ui: &egui::Ui, painter: &egui::Painter, rect: R
|
||||
let size = [w, h];
|
||||
let cam = app.cam;
|
||||
let (overlay_key, overlay) = app.overlay();
|
||||
let key = (app.scene().0, overlay_key);
|
||||
let symbols_key = app.symbols().0;
|
||||
let key = (app.scene().0, symbols_key, overlay_key);
|
||||
let fresh = app.viewport.cpu.as_ref().is_some_and(|(_, k, c, s)| *k == key && *c == cam && *s == size);
|
||||
// A scene the renderer refused is reported once and not retried until it changes.
|
||||
if !fresh && app.viewport.cpu_failed != Some(key.0) {
|
||||
let c = match fieldcraft_render::render_layers(&app.scene.1, &[&overlay], &cam, w, h) {
|
||||
let c = match fieldcraft_render::render_layers(&app.scene.1, &[&app.symbols.1, &overlay], &cam, w, h) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
app.viewport.cpu = None;
|
||||
@@ -450,7 +461,9 @@ fn cpu_size(w: usize, h: usize, max_side: usize) -> (usize, usize) {
|
||||
/// The scene's labels (reference point names) beside their points, with the interface's font.
|
||||
/// The CPU renderer draws them into its image.
|
||||
fn labels(app: &mut ModelApp, painter: &egui::Painter, t: &Tokens) {
|
||||
let labels: Vec<(Vec3, String)> = app.scene().1.labels.iter().map(|l| (l.p, l.text.clone())).collect();
|
||||
app.symbols();
|
||||
app.scene();
|
||||
let labels: Vec<(Vec3, String)> = app.scene.1.labels.iter().chain(&app.symbols.1.labels).map(|l| (l.p, l.text.clone())).collect();
|
||||
let offset = fieldcraft_render::LABEL_OFFSET as f32;
|
||||
for (p, text) in labels {
|
||||
if let Some(at) = to_screen(app, p) {
|
||||
@@ -627,6 +640,9 @@ fn nav_bar(app: &mut ModelApp, ui: &mut egui::Ui, rect: Rect, t: &Tokens) {
|
||||
ui.checkbox(&mut app.ui.show_nodes, "Nodes");
|
||||
ui.checkbox(&mut app.ui.show_loads, "Loads & BCs");
|
||||
ui.checkbox(&mut app.ui.show_interactions, "Interactions");
|
||||
let (lo, hi) = fieldcraft_render::glyphs::SIZE_RANGE;
|
||||
ui.add(egui::Slider::new(&mut app.ui.symbol_size, lo..=hi).step_by(1.0).suffix(" pt").text("Symbol size"))
|
||||
.on_hover_text("The size of boundary condition, load and interaction symbols on screen, at any zoom");
|
||||
ui.separator();
|
||||
ui.checkbox(&mut app.ui.show_tree, "Browser");
|
||||
ui.checkbox(&mut app.ui.show_timeline, "Timeline");
|
||||
|
||||
Reference in new issue
Block a user