1. tuned arrow mesh drawing
2. centralixzed arrow config in layout.py 3. seperate mesh render and display resolution to speed up (~40 fps)
This commit is contained in:
+131
-1
@@ -13,15 +13,40 @@ Structure:
|
||||
Each panel uses explicit width_px and height_px.
|
||||
top_stretch / log_stretch control how extra vertical space is shared on resize.
|
||||
|
||||
MeshLighting (below) controls the 3D mesh panel sun light:
|
||||
azimuth_deg — horizontal angle in degrees (0° = +X, 90° = +Y)
|
||||
elevation_deg — vertical angle in degrees (positive = from above)
|
||||
intensity — Filament sun strength (typical 30000–120000)
|
||||
ambient/diffuse — Windows viewer shading weights (0–1)
|
||||
|
||||
MESH_CONTACT_ARROW (below) controls the yellow 3D contact arrow on the mesh panel:
|
||||
reference_normal_force — Fn at which arrow reaches full size (log/power/linear curve)
|
||||
force_strength_mode — "log" | "power" | "linear"
|
||||
min/max_length_fraction, min/max_thickness_scale, color
|
||||
|
||||
Mesh panel has two sizes (edit MESH_RENDER_* and TOP_ROW mesh slot):
|
||||
display (TOP_ROW mesh width_px/height_px) — Qt widget size on screen
|
||||
render (MESH_RENDER_WIDTH/HEIGHT) — Open3D offscreen resolution (upscaled for display)
|
||||
|
||||
Performance (3D dashboard; edit DashboardLayout defaults):
|
||||
timer_interval_ms — Qt timer period (16 ≈ 60 Hz tick rate)
|
||||
mesh_render_stride — render 3D mesh every N frames (2 ≈ half mesh cost)
|
||||
chart_update_stride — update force charts every N frames
|
||||
mesh_async_render — background mesh render (Linux only; Windows uses sync)
|
||||
|
||||
Edit TOP_ROW.slots to reorder or resize panels.
|
||||
Edit PANEL_SETUP to change top vs log proportions.
|
||||
Edit LOG_CONFIG for live status line and log panel appearance.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal
|
||||
|
||||
from viz.contact_arrow import ContactArrowStyle, DEFAULT_CONTACT_ARROW_STYLE
|
||||
|
||||
PanelId = Literal["mesh", "arrows", "charts", "log"]
|
||||
|
||||
|
||||
@@ -73,6 +98,91 @@ PANEL_SETUP = PanelSetup(
|
||||
log_stretch=1,
|
||||
)
|
||||
|
||||
# Open3D offscreen resolution for the mesh panel (independent of on-screen widget size).
|
||||
MESH_RENDER_WIDTH_PX = 256
|
||||
MESH_RENDER_HEIGHT_PX = 256
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MeshLighting:
|
||||
"""3D mesh panel light — edit MESH_LIGHTING or DashboardLayout.mesh_lighting."""
|
||||
|
||||
azimuth_deg: float = -45.0 # finger outline
|
||||
elevation_deg: float = -35.0 # tip to root
|
||||
intensity: float = 75000.0
|
||||
ambient: float = 0.35
|
||||
diffuse: float = 0.65
|
||||
|
||||
@property
|
||||
def direction(self) -> tuple[float, float, float]:
|
||||
"""Unit light direction derived from azimuth / elevation (degrees)."""
|
||||
az = math.radians(self.azimuth_deg)
|
||||
el = math.radians(self.elevation_deg)
|
||||
cos_el = math.cos(el)
|
||||
return (cos_el * math.cos(az), cos_el * math.sin(az), math.sin(el))
|
||||
|
||||
|
||||
MESH_LIGHTING = MeshLighting(
|
||||
azimuth_deg=-45.0,
|
||||
elevation_deg=0,
|
||||
intensity=75000.0,
|
||||
ambient=0.5,
|
||||
diffuse=0.5,
|
||||
)
|
||||
|
||||
# 3D mesh contact arrow — edit here for dashboard tuning (see ContactArrowStyle in viz/contact_arrow.py).
|
||||
MESH_CONTACT_ARROW = ContactArrowStyle(
|
||||
reference_normal_force=10000.0,
|
||||
force_strength_mode="log",
|
||||
force_strength_gamma=0.5,
|
||||
min_length_fraction=0.02,
|
||||
max_length_fraction=0.30,
|
||||
min_thickness_scale=0.20,
|
||||
max_thickness_scale=1.0,
|
||||
normal_force_scale=1.0,
|
||||
color=(1.0, 0.92, 0.10),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LogConfig:
|
||||
"""Log panel — edit LOG_CONFIG or DashboardLayout.log_config."""
|
||||
|
||||
status_enabled: bool = True
|
||||
max_lines: int = 2000
|
||||
font_family: str = "Consolas"
|
||||
font_pt: int = 12
|
||||
show_flow: bool = True
|
||||
show_mask: bool = True
|
||||
|
||||
def format_status(
|
||||
self,
|
||||
*,
|
||||
fps: float,
|
||||
fnormal: float,
|
||||
fshearx: float,
|
||||
fsheary: float,
|
||||
flow_rms: float = 0.0,
|
||||
mask_coverage: float = 0.0,
|
||||
wall_fps: float | None = None,
|
||||
) -> str:
|
||||
shear_mag = float((fshearx**2 + fsheary**2) ** 0.5)
|
||||
if wall_fps is not None:
|
||||
parts = [f"FPS={wall_fps:.1f}", f"sdk={fps:.1f}"]
|
||||
else:
|
||||
parts = [f"FPS={fps:.1f}"]
|
||||
parts.extend(
|
||||
[
|
||||
f"Fn={fnormal:.1f}",
|
||||
f"|Fs|={shear_mag:.1f}",
|
||||
f"Fs=({fshearx:.1f}, {fsheary:.1f})",
|
||||
]
|
||||
)
|
||||
return " ".join(parts)
|
||||
|
||||
|
||||
LOG_CONFIG = LogConfig()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DashboardLayout:
|
||||
@@ -100,7 +210,27 @@ class DashboardLayout:
|
||||
# UV arrow panel: min flow magnitude to draw an arrow (display only; not dd02-ov.json contact.flow_threshold)
|
||||
arrow_flow_threshold: float = 1.0
|
||||
|
||||
timer_interval_ms: int = 10
|
||||
mesh_lighting: MeshLighting = MESH_LIGHTING
|
||||
mesh_contact_arrow: ContactArrowStyle = MESH_CONTACT_ARROW
|
||||
|
||||
log_config: LogConfig = LOG_CONFIG
|
||||
|
||||
mesh_render_width_px: int = MESH_RENDER_WIDTH_PX
|
||||
mesh_render_height_px: int = MESH_RENDER_HEIGHT_PX
|
||||
|
||||
timer_interval_ms: int = 16
|
||||
mesh_async_render: bool = True
|
||||
mesh_render_stride: int = 2
|
||||
chart_update_stride: int = 2
|
||||
|
||||
def mesh_display_size(self) -> tuple[int, int]:
|
||||
"""On-screen mesh panel size from TOP_ROW mesh slot."""
|
||||
slot = self.top_slot("mesh")
|
||||
return slot.width_px, slot.height_px
|
||||
|
||||
def mesh_render_size(self) -> tuple[int, int]:
|
||||
"""Open3D offscreen render target size (may differ from display size)."""
|
||||
return self.mesh_render_width_px, self.mesh_render_height_px
|
||||
|
||||
def slot(self, panel_id: PanelId) -> PanelSlot | None:
|
||||
if self.panel_setup.log_panel.id == panel_id:
|
||||
|
||||
Reference in New Issue
Block a user