Commit: fa83288bcff5781e8ba841fe4854dcb5c08d3013
Parent: faaea7b1c3ee24bee0cb96c77993b994d4f6c7e8
Author: Randy Palamar
Date: Mon, 9 Mar 2026 14:38:08 -0600
ui: support the 3 common 2d planes (xy, xz, yz)
This is quite hacky and can hopefully be fixed with a better UI
structure. For now though we want to at least cover the 3 most
common cases in a reasonable (from the user's perspective) way.
Diffstat:
4 files changed, 152 insertions(+), 45 deletions(-)
diff --git a/math.c b/math.c
@@ -766,16 +766,41 @@ das_transform_1d(v3 p1, v3 p2)
}
function m4
-das_transform_2d_xz(v2 min_coordinate, v2 max_coordinate)
+das_transform_2d_xz(v2 min_coordinate, v2 max_coordinate, f32 y_off)
{
v2 extent = v2_abs(v2_sub(max_coordinate, min_coordinate));
- // NOTE(rnp): DAS assumes 3D grid with z down -> swap y and z
m4 result;
- result.c[0] = (v4){{extent.x, 0.0f, 0.0f, 0.0f}};
- result.c[1] = (v4){{0.0f, 0.0f, extent.y, 0.0f}};
- result.c[2] = (v4){{0.0f, 1.0f, 0.0f, 0.0f}};
- result.c[3] = (v4){{min_coordinate.x, 0.0f, min_coordinate.y, 1.0f}};
+ result.c[0] = (v4){{extent.x, 0.0f, 0.0f, 0.0f}};
+ result.c[1] = (v4){{0.0f, 0.0f, extent.y, 0.0f}};
+ result.c[2] = (v4){{0.0f, 1.0f, 0.0f, 0.0f}};
+ result.c[3] = (v4){{min_coordinate.x, y_off, min_coordinate.y, 1.0f}};
+ return result;
+}
+
+function m4
+das_transform_2d_yz(v2 min_coordinate, v2 max_coordinate, f32 x_off)
+{
+ v2 extent = v2_abs(v2_sub(max_coordinate, min_coordinate));
+
+ m4 result;
+ result.c[0] = (v4){{0.0f, extent.x, 0.0f, 0.0f}};
+ result.c[1] = (v4){{0.0f, 0.0f, extent.y, 0.0f}};
+ result.c[2] = (v4){{1.0f, 0.0f, 0.0f, 0.0f}};
+ result.c[3] = (v4){{x_off, min_coordinate.x, min_coordinate.y, 1.0f}};
+ return result;
+}
+
+function m4
+das_transform_2d_xy(v2 min_coordinate, v2 max_coordinate, f32 z_off)
+{
+ v2 extent = v2_abs(v2_sub(max_coordinate, min_coordinate));
+
+ m4 result;
+ result.c[0] = (v4){{extent.x, 0.0f, 0.0f, 0.0f}};
+ result.c[1] = (v4){{0.0f, extent.y, 0.0f, 0.0f}};
+ result.c[2] = (v4){{0.0f, 0.0f, 1.0f, 0.0f}};
+ result.c[3] = (v4){{min_coordinate.x, min_coordinate.y, z_off, 1.0f}};
return result;
}
@@ -795,9 +820,9 @@ das_transform(v3 min_coordinate, v3 max_coordinate, iv3 *points)
*points = das_output_dimension(*points);
switch (iv3_dimension(*points)) {
- case 1:{result = das_transform_1d( min_coordinate, max_coordinate); }break;
- case 2:{result = das_transform_2d_xz(XY(min_coordinate), XY(max_coordinate));}break;
- case 3:{result = das_transform_3d( min_coordinate, max_coordinate); }break;
+ case 1:{result = das_transform_1d( min_coordinate, max_coordinate); }break;
+ case 2:{result = das_transform_2d_xz(XY(min_coordinate), XY(max_coordinate), 0);}break;
+ case 3:{result = das_transform_3d( min_coordinate, max_coordinate); }break;
}
return result;
diff --git a/shaders/render_3d.frag.glsl b/shaders/render_3d.frag.glsl
@@ -80,6 +80,7 @@ void main(void)
out_colour = vec4(u_bb_colour.xyz, alpha);
}break;
+ case 0: // NOTE(rnp): 0 is a special case for X-Plane Rendering
case 2:
case 3:
{
diff --git a/ui.c b/ui.c
@@ -435,11 +435,13 @@ struct BeamformerUI {
u32 selected_parameter_block;
m4 das_transform;
- v3 min_coordinate;
- v3 max_coordinate;
+ v2 min_coordinate;
+ v2 max_coordinate;
f32 off_axis_position;
f32 beamform_plane;
+ BeamformerViewPlaneTag plane_layout;
+
FrameViewRenderContext *frame_view_render_context;
BeamformerSharedMemory * shared_memory;
@@ -564,6 +566,22 @@ rl_rect(Rect a)
return result;
}
+function BeamformerViewPlaneTag
+ui_plane_layout_from_normal(v3 normal)
+{
+ BeamformerViewPlaneTag result = BeamformerViewPlaneTag_Arbitrary;
+ b32 has_x = !f32_equal(normal.x, 0.0f);
+ b32 has_y = !f32_equal(normal.y, 0.0f);
+ b32 has_z = !f32_equal(normal.z, 0.0f);
+ if ((has_x + has_y + has_z) == 1) {
+ if (has_x) result = BeamformerViewPlaneTag_YZ;
+ if (has_y) result = BeamformerViewPlaneTag_XZ;
+ if (has_z) result = BeamformerViewPlaneTag_XY;
+ assert(result != BeamformerViewPlaneTag_Arbitrary);
+ }
+ return result;
+}
+
function f32
ui_blinker_update(UIBlinker *b, f32 scale)
{
@@ -1208,11 +1226,11 @@ add_beamformer_parameters_view(Variable *parent, BeamformerCtx *ctx)
VariableGroupKind_Vector, ui->font);
{
add_beamformer_variable(ui, group, &ui->arena, s8("Min:"), s8("[mm]"),
- ui->min_coordinate.E + 0, v2_inf, 1e3f, 0.5e-3f,
- V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
+ &ui->min_coordinate.x, v2_inf, 1e3f, 0.5e-3f,
+ V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
add_beamformer_variable(ui, group, &ui->arena, s8("Max:"), s8("[mm]"),
- ui->max_coordinate.E + 0, v2_inf, 1e3f, 0.5e-3f,
+ &ui->max_coordinate.x, v2_inf, 1e3f, 0.5e-3f,
V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
}
group = end_variable_group(group);
@@ -1221,11 +1239,11 @@ add_beamformer_parameters_view(Variable *parent, BeamformerCtx *ctx)
VariableGroupKind_Vector, ui->font);
{
add_beamformer_variable(ui, group, &ui->arena, s8("Min:"), s8("[mm]"),
- ui->min_coordinate.E + 2, v2_inf, 1e3f, 0.5e-3f,
+ &ui->min_coordinate.y, v2_inf, 1e3f, 0.5e-3f,
V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
add_beamformer_variable(ui, group, &ui->arena, s8("Max:"), s8("[mm]"),
- ui->max_coordinate.E + 2, v2_inf, 1e3f, 0.5e-3f,
+ &ui->max_coordinate.y, v2_inf, 1e3f, 0.5e-3f,
V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
}
group = end_variable_group(group);
@@ -1314,10 +1332,41 @@ ui_beamformer_frame_view_convert(BeamformerUI *ui, Arena *arena, Variable *view,
axial->zoom_starting_coord = F32_INFINITY;
b32 copy = kind == BeamformerFrameViewKind_Copy;
- lateral->min_value = copy ? &bv->min_coordinate.x : &ui->min_coordinate.x;
- lateral->max_value = copy ? &bv->max_coordinate.x : &ui->max_coordinate.x;
- axial->min_value = copy ? &bv->min_coordinate.z : &ui->min_coordinate.z;
- axial->max_value = copy ? &bv->max_coordinate.z : &ui->max_coordinate.z;
+ BeamformerViewPlaneTag plane = ui->plane_layout;
+ if (old && old->frame) {
+ v3 normal = cross(old->frame->voxel_transform.c[0].xyz, old->frame->voxel_transform.c[1].xyz);
+ plane = ui_plane_layout_from_normal(normal);
+ }
+
+ switch (plane) {
+ case BeamformerViewPlaneTag_XY:{
+ lateral->min_value = copy ? &bv->min_coordinate.x : &ui->min_coordinate.x;
+ lateral->max_value = copy ? &bv->max_coordinate.x : &ui->max_coordinate.x;
+ axial->min_value = copy ? &bv->min_coordinate.y : &ui->min_coordinate.y;
+ axial->max_value = copy ? &bv->max_coordinate.y : &ui->max_coordinate.y;
+ }break;
+
+ case BeamformerViewPlaneTag_XZ:{
+ lateral->min_value = copy ? &bv->min_coordinate.x : &ui->min_coordinate.x;
+ lateral->max_value = copy ? &bv->max_coordinate.x : &ui->max_coordinate.x;
+ axial->min_value = copy ? &bv->min_coordinate.z : &ui->min_coordinate.y;
+ axial->max_value = copy ? &bv->max_coordinate.z : &ui->max_coordinate.y;
+ }break;
+
+ case BeamformerViewPlaneTag_YZ:{
+ lateral->min_value = copy ? &bv->min_coordinate.y : &ui->min_coordinate.x;
+ lateral->max_value = copy ? &bv->max_coordinate.y : &ui->max_coordinate.x;
+ axial->min_value = copy ? &bv->min_coordinate.z : &ui->min_coordinate.y;
+ axial->max_value = copy ? &bv->max_coordinate.z : &ui->max_coordinate.y;
+ }break;
+
+ default:{
+ lateral->min_value = copy ? &bv->min_coordinate.x : &ui->min_coordinate.x;
+ lateral->max_value = copy ? &bv->max_coordinate.x : &ui->max_coordinate.x;
+ axial->min_value = copy ? &bv->min_coordinate.z : &ui->min_coordinate.y;
+ axial->max_value = copy ? &bv->max_coordinate.z : &ui->max_coordinate.y;
+ }break;
+ }
#define X(id, text) add_button(ui, menu, arena, s8(text), UI_BID_ ##id, 0, ui->small_font);
FRAME_VIEW_BUTTONS
@@ -1527,17 +1576,12 @@ ui_copy_frame(BeamformerUI *ui, Variable *view, RegionSplitDirection direction)
function v3
beamformer_frame_view_plane_size(BeamformerUI *ui, BeamformerFrameView *view)
{
- v3 result;
- if (view->kind == BeamformerFrameViewKind_3DXPlane) {
- result = v3_sub(ui->max_coordinate, ui->min_coordinate);
- swap(result.y, result.z);
- result.x = Max(1e-3f, result.x);
- result.y = Max(1e-3f, result.y);
- result.z = Max(1e-3f, result.z);
- } else {
- v2 size = v2_sub(XZ(view->max_coordinate), XZ(view->min_coordinate));
- result = (v3){.x = size.x, .y = size.y};
- }
+ assert(view->kind == BeamformerFrameViewKind_3DXPlane);
+ v3 result = {0};
+ result.xy = v2_sub(ui->max_coordinate, ui->min_coordinate);
+ result.x = Max(1e-3f, result.x);
+ result.y = Max(1e-3f, result.y);
+ result.z = Max(1e-3f, result.z);
return result;
}
@@ -1562,8 +1606,8 @@ normalized_p_in_rect(Rect r, v2 p, b32 invert_y)
function v3
x_plane_position(BeamformerUI *ui)
{
- f32 y_min = ui->min_coordinate.E[2];
- f32 y_max = ui->max_coordinate.E[2];
+ f32 y_min = ui->min_coordinate.y;
+ f32 y_max = ui->max_coordinate.y;
v3 result = {.y = y_min + (y_max - y_min) / 2};
return result;
}
@@ -4039,8 +4083,8 @@ validate_ui_parameters(BeamformerUI *ui)
{
if (ui->min_coordinate.x > ui->max_coordinate.x)
swap(ui->min_coordinate.x, ui->max_coordinate.x);
- if (ui->min_coordinate.z > ui->max_coordinate.z)
- swap(ui->min_coordinate.z, ui->max_coordinate.z);
+ if (ui->min_coordinate.y > ui->max_coordinate.y)
+ swap(ui->min_coordinate.y, ui->max_coordinate.y);
}
function void
@@ -4063,11 +4107,41 @@ draw_ui(BeamformerCtx *ctx, BeamformerInput *input, BeamformerFrame *frame_to_dr
mem_copy(&ui->params, &pb->parameters_ui, sizeof(ui->params));
mem_copy(ui->das_transform.E, pb->parameters.das_voxel_transform.E, sizeof(ui->das_transform));
+ v3 normal = cross(ui->das_transform.c[0].xyz, ui->das_transform.c[1].xyz);
+ ui->plane_layout = ui_plane_layout_from_normal(normal);
+
atomic_and_u32(&ctx->ui_dirty_parameter_blocks, ~selected_mask);
beamformer_parameter_block_unlock(ui->shared_memory, selected_block);
- ui->min_coordinate = m4_mul_v4(ui->das_transform, (v4){{0.0f, 0.0f, 0.0f, 1.0f}}).xyz;
- ui->max_coordinate = m4_mul_v4(ui->das_transform, (v4){{1.0f, 1.0f, 1.0f, 1.0f}}).xyz;
+ ui->off_axis_position = 0.0f;
+ ui->beamform_plane = 0.0f;
+
+ v3 min_coordinate = m4_mul_v4(ui->das_transform, (v4){{0.0f, 0.0f, 0.0f, 1.0f}}).xyz;
+ v3 max_coordinate = m4_mul_v4(ui->das_transform, (v4){{1.0f, 1.0f, 1.0f, 1.0f}}).xyz;
+ switch (ui->plane_layout) {
+ case BeamformerViewPlaneTag_XY:{
+ ui->min_coordinate = min_coordinate.xy;
+ ui->max_coordinate = max_coordinate.xy;
+ ui->off_axis_position = min_coordinate.z;
+ }break;
+
+ case BeamformerViewPlaneTag_XZ:{
+ ui->min_coordinate = (v2){{min_coordinate.x, min_coordinate.z}};
+ ui->max_coordinate = (v2){{max_coordinate.x, max_coordinate.z}};
+ ui->off_axis_position = min_coordinate.y;
+ }break;
+
+ case BeamformerViewPlaneTag_YZ:{
+ ui->min_coordinate = min_coordinate.yz;
+ ui->max_coordinate = max_coordinate.yz;
+ ui->off_axis_position = min_coordinate.x;
+ }break;
+
+ default:{
+ ui->min_coordinate = (v2){{min_coordinate.x, min_coordinate.z}};
+ ui->max_coordinate = (v2){{max_coordinate.x, max_coordinate.z}};
+ }break;
+ }
}
}
@@ -4083,23 +4157,28 @@ draw_ui(BeamformerCtx *ctx, BeamformerInput *input, BeamformerFrame *frame_to_dr
if (pb) {
ui->flush_params = 0;
- v3 min_coordinate = ui->min_coordinate;
- v3 max_coordinate = ui->max_coordinate;
+ v2 min = ui->min_coordinate;
+ v2 max = ui->max_coordinate;
iv3 points = ctx->latest_frame->dim;
i32 dimension = iv3_dimension(points);
// TODO(rnp): this is immediate mode code that should be in the ui building code
- swap(min_coordinate.y, min_coordinate.z);
- swap(max_coordinate.y, max_coordinate.z);
- m4 new_transform = das_transform(min_coordinate, max_coordinate, &points);
+ m4 new_transform = ui->das_transform;
+ switch (ui->plane_layout) {
+ case BeamformerViewPlaneTag_XY:{new_transform = das_transform_2d_xy(min, max, 0.0f);}break;
+ case BeamformerViewPlaneTag_XZ:{new_transform = das_transform_2d_xz(min, max, 0.0f);}break;
+ case BeamformerViewPlaneTag_YZ:{new_transform = das_transform_2d_yz(min, max, 0.0f);}break;
+ default:{}break;
+ }
+
switch (dimension) {
case 1:{}break;
case 2:{
v3 U = ui->das_transform.c[0].xyz;
v3 V = ui->das_transform.c[1].xyz;
- v3 N = v3_normalize(cross(V, U));
+ v3 N = v3_normalize(cross(U, V));
v3 rotation_axis = v3_normalize(cross(U, N));
diff --git a/util.h b/util.h
@@ -277,8 +277,10 @@ typedef union {
} v2;
typedef union {
- struct { f32 x, y, z; };
- struct { f32 w, h, d; };
+ struct { f32 x, y, z; };
+ struct { f32 w, h, d; };
+ struct { v2 xy; f32 _1; };
+ struct { f32 _2; v2 yz; };
f32 E[3];
} v3;