Commit: 7e25d29761fbdd09ca3d846d31e48d733d881a53
Parent: eaecccedbced8b589438a6ab16b4f70065c28ddb
Author: Randy Palamar
Date: Wed, 12 Mar 2025 07:52:14 -0600
ui: reorganize ui into a tree
more advanced interactions coming soon
Diffstat:
M | beamformer.c | | | 2 | +- |
M | beamformer.h | | | 116 | ++++--------------------------------------------------------------------------- |
M | ui.c | | | 1281 | ++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------- |
M | util.c | | | 16 | ---------------- |
M | util.h | | | 36 | +----------------------------------- |
5 files changed, 819 insertions(+), 632 deletions(-)
diff --git a/beamformer.c b/beamformer.c
@@ -776,7 +776,7 @@ DEBUG_EXPORT BEAMFORMER_FRAME_STEP_FN(beamformer_frame_step)
if (ctx->params->upload) {
/* TODO(rnp): clean this up */
- ctx->ui->read_params = 1;
+ ctx->ui_read_params = 1;
}
alloc_output_image(ctx, bp->output_points.xyz);
diff --git a/beamformer.h b/beamformer.h
@@ -10,28 +10,7 @@
#include "util.h"
-#define BG_COLOUR (v4){.r = 0.15, .g = 0.12, .b = 0.13, .a = 1.0}
-#define FG_COLOUR (v4){.r = 0.92, .g = 0.88, .b = 0.78, .a = 1.0}
-#define FOCUSED_COLOUR (v4){.r = 0.86, .g = 0.28, .b = 0.21, .a = 1.0}
-#define HOVERED_COLOUR (v4){.r = 0.11, .g = 0.50, .b = 0.59, .a = 1.0}
-#define RULER_COLOUR (v4){.r = 1.00, .g = 0.70, .b = 0.00, .a = 1.0}
-
-#define INFO_COLUMN_WIDTH 560
-/* NOTE: extra space used for allowing mouse clicks after end of text */
-#define TEXT_BOX_EXTRA_X 10.0f
-
-#define TEXT_HOVER_SPEED 5.0f
-
-#define RULER_TEXT_PAD 10.0f
-#define RULER_TICK_LENGTH 20.0f
-
-#define RECT_BTN_COLOUR (Color){.r = 0x43, .g = 0x36, .b = 0x3a, .a = 0xff}
-#define RECT_BTN_BORDER_COLOUR (Color){.r = 0x00, .g = 0x00, .b = 0x00, .a = 0xCC}
-#define RECT_BTN_ROUNDNESS 0.3f
-#define RECT_BTN_BORDER_WIDTH 6.0f
-
-/* TODO: multiple views */
-#define MAX_DISPLAYS 1
+#define INFO_COLUMN_WIDTH 480
enum gl_vendor_ids {
GL_VENDOR_AMD,
@@ -41,65 +20,6 @@ enum gl_vendor_ids {
};
typedef struct {
- u8 buf[64];
- i32 buf_len;
- i32 cursor;
- f32 cursor_blink_t;
- f32 cursor_blink_scale;
-} InputState;
-
-enum variable_flags {
- V_CAUSES_COMPUTE = 1 << 29,
- V_GEN_MIPMAPS = 1 << 30,
-};
-
-enum interaction_states {
- IS_NONE,
- IS_NOP,
- IS_SET,
- IS_DRAG,
- IS_SCROLL,
- IS_TEXT,
-
- IS_DISPLAY,
- IS_SCALE_BAR,
-};
-
-enum ruler_state {
- RS_NONE,
- RS_START,
- RS_HOLD,
-};
-
-enum scale_bar_directions {
- SB_LATERAL,
- SB_AXIAL,
-};
-
-typedef struct {
- Variable hot;
- Variable next_hot;
- Variable active;
- u32 hot_state;
- u32 state;
-} InteractionState;
-
-typedef struct v2_sll {
- struct v2_sll *next;
- v2 v;
-} v2_sll;
-
-typedef struct {
- f32 *min_value, *max_value;
- v2_sll *savepoint_stack;
- v2 zoom_starting_point;
- v2 screen_offset;
- v2 screen_space_to_value;
- f32 hover_t;
- b32 scroll_both;
-} ScaleBar;
-
-typedef struct {
b32 executable_reloaded;
b32 pipe_data_available;
iptr pipe_handle;
@@ -150,34 +70,6 @@ typedef struct {
c8 export_pipe_name[1024];
} BeamformerParametersFull;
-typedef struct {
- TempArena frame_temporary_arena;
- Arena arena_for_frame;
-
- Font font;
- Font small_font;
-
- InteractionState interaction;
- InputState text_input_state;
-
- ScaleBar scale_bars[MAX_DISPLAYS][2];
- v2_sll *scale_bar_savepoint_freelist;
-
- v2 ruler_start_p;
- v2 ruler_stop_p;
- u32 ruler_state;
-
- f32 progress_display_t;
- f32 progress_display_t_velocity;
-
- BeamformerUIParameters params;
- b32 flush_params;
- /* TODO(rnp): this is nasty and should be removed */
- b32 read_params;
-
- iptr last_displayed_frame;
-} BeamformerUI;
-
#define CS_UNIFORMS \
X(CS_DAS, voxel_offset) \
X(CS_DAS, cycle_t) \
@@ -320,8 +212,10 @@ typedef struct BeamformerCtx {
/* TODO(rnp): is there a better way of tracking this? */
b32 ready_for_rf;
- Arena ui_backing_store;
- BeamformerUI *ui;
+ Arena ui_backing_store;
+ void *ui;
+ /* TODO(rnp): this is nasty and should be removed */
+ b32 ui_read_params;
BeamformFrame beamform_frames[MAX_BEAMFORMED_SAVED_FRAMES];
u32 next_render_frame_index;
diff --git a/ui.c b/ui.c
@@ -1,4 +1,408 @@
/* See LICENSE for license details. */
+#define BG_COLOUR (v4){.r = 0.15, .g = 0.12, .b = 0.13, .a = 1.0}
+#define FG_COLOUR (v4){.r = 0.92, .g = 0.88, .b = 0.78, .a = 1.0}
+#define FOCUSED_COLOUR (v4){.r = 0.86, .g = 0.28, .b = 0.21, .a = 1.0}
+#define HOVERED_COLOUR (v4){.r = 0.11, .g = 0.50, .b = 0.59, .a = 1.0}
+#define RULER_COLOUR (v4){.r = 1.00, .g = 0.70, .b = 0.00, .a = 1.0}
+
+#define NORMALIZED_FG_COLOUR colour_from_normalized(FG_COLOUR)
+
+/* NOTE: extra space used for allowing mouse clicks after end of text */
+#define TEXT_BOX_EXTRA_X 10.0f
+
+#define TEXT_HOVER_SPEED 5.0f
+
+#define RULER_TEXT_PAD 10.0f
+#define RULER_TICK_LENGTH 20.0f
+
+typedef struct {
+ u8 buf[64];
+ i32 buf_len;
+ i32 cursor;
+ f32 cursor_blink_t;
+ f32 cursor_blink_scale;
+ Rect rect, hot_rect;
+ Font *font, *hot_font;
+} InputState;
+
+typedef enum {
+ IT_NONE,
+ IT_NOP,
+ IT_SET,
+ IT_DRAG,
+ IT_SCROLL,
+ IT_TEXT,
+
+ IT_DISPLAY,
+ IT_SCALE_BAR,
+} InteractionType;
+
+enum ruler_state {
+ RS_NONE,
+ RS_START,
+ RS_HOLD,
+};
+
+typedef struct v2_sll {
+ struct v2_sll *next;
+ v2 v;
+} v2_sll;
+
+typedef enum {
+ VT_NULL,
+ VT_B32,
+ VT_F32,
+ VT_I32,
+ VT_GROUP,
+ VT_BEAMFORMER_VARIABLE,
+ VT_BEAMFORMER_VIEW,
+ VT_SCALE_BAR,
+} VariableType;
+
+typedef enum {
+ VG_LIST,
+ /* NOTE(rnp): special groups for vectors with components
+ * stored in separate memory locations */
+ VG_V2,
+ VG_V4,
+} VariableGroupType;
+
+typedef struct Variable Variable;
+
+typedef struct {
+ Variable *first;
+ Variable *last;
+ b32 expanded;
+ f32 max_name_width;
+ VariableGroupType type;
+ /* TODO(rnp): maybe this should just be in the Variable */
+ f32 hover_t;
+} VariableGroup;
+
+struct Variable {
+ s8 name;
+ union {
+ void *generic;
+ VariableGroup group;
+ b32 b32;
+ i32 i32;
+ f32 f32;
+ } u;
+ Variable *next;
+ Variable *parent;
+ VariableType type;
+};
+
+enum variable_flags {
+ V_INPUT = 1 << 0,
+ V_TEXT = 1 << 1,
+ V_CAUSES_COMPUTE = 1 << 29,
+ V_GEN_MIPMAPS = 1 << 30,
+};
+
+typedef struct {
+ Variable base;
+ VariableType subtype;
+ u32 flags;
+
+ s8 suffix;
+ /* TODO(rnp): think of something better than this */
+ union {
+ struct {s8 *names; u32 count;} name_table;
+ struct {
+ f32 display_scale;
+ f32 scroll_scale;
+ v2 limits;
+ } params;
+ };
+ f32 hover_t;
+} BeamformerVariable;
+
+typedef enum {
+ SB_LATERAL,
+ SB_AXIAL,
+} ScaleBarDirection;
+
+typedef struct {
+ f32 *min_value, *max_value;
+ v2_sll *savepoint_stack;
+ v2 zoom_starting_point;
+ v2 screen_offset;
+ v2 screen_space_to_value;
+ v2 limits;
+ v2 scroll_scale;
+ f32 hover_t;
+} ScaleBar;
+
+typedef enum {
+ FVT_INDEXED,
+ FVT_LATEST,
+ FVT_COPY,
+} BeamformerFrameViewType;
+
+typedef struct {
+ BeamformFrame *frame;
+ ScaleBar lateral_scale_bar;
+ ScaleBar axial_scale_bar;
+ Variable menu;
+ Rect display_rect;
+ BeamformerFrameViewType type;
+} BeamformerFrameView;
+
+typedef struct {
+ Variable *hot;
+ Variable *next_hot;
+ Variable *active;
+ InteractionType hot_type;
+ InteractionType type;
+} InteractionState;
+
+typedef struct {
+ TempArena frame_temporary_arena;
+ Arena arena;
+
+ Font font;
+ Font small_font;
+
+ BeamformerVariable scratch_beamformer_variable;
+
+ InteractionState interaction;
+ InputState text_input_state;
+
+ Variable *parameters_view;
+
+ Variable beamform_views;
+
+ v2_sll *scale_bar_savepoint_freelist;
+
+ BeamformFrame *latest_frame;
+
+ v2 ruler_start_p;
+ v2 ruler_stop_p;
+ u32 ruler_state;
+
+ f32 progress_display_t;
+ f32 progress_display_t_velocity;
+
+ BeamformerUIParameters params;
+ b32 flush_params;
+
+ iptr last_displayed_frame;
+} BeamformerUI;
+
+static v2
+measure_text(Font font, s8 text)
+{
+ v2 result = {.y = font.baseSize};
+ for (size i = 0; i < text.len; i++) {
+ /* NOTE: assumes font glyphs are ordered ASCII */
+ i32 idx = (i32)text.data[i] - 0x20;
+ result.x += font.glyphs[idx].advanceX;
+ if (font.glyphs[idx].advanceX == 0)
+ result.x += (font.recs[idx].width + font.glyphs[idx].offsetX);
+ }
+ return result;
+}
+
+static void
+stream_append_variable_base(Stream *s, VariableType type, void *var, void *scale)
+{
+ switch (type) {
+ case VT_B32: {
+ s8 *text = var;
+ stream_append_s8(s, text[*(b32 *)scale != 0]);
+ } break;
+ case VT_F32: {
+ f32 val = *(f32 *)var * *(f32 *)scale;
+ stream_append_f64(s, val, 100);
+ } break;
+ default: INVALID_CODE_PATH;
+ }
+}
+
+static void
+stream_append_variable(Stream *s, Variable *var)
+{
+ switch (var->type) {
+ case VT_GROUP: stream_append_s8(s, var->name); break;
+ case VT_BEAMFORMER_VARIABLE: {
+ BeamformerVariable *bv = (BeamformerVariable *)var;
+ switch (bv->subtype) {
+ case VT_B32: {
+ stream_append_variable_base(s, VT_B32, bv->name_table.names, bv->base.u.generic);
+ } break;
+ case VT_F32: {
+ stream_append_variable_base(s, VT_F32, bv->base.u.generic, &bv->params.display_scale);
+ } break;
+ default: INVALID_CODE_PATH;
+ }
+ } break;
+ case VT_B32: {
+ s8 texts[] = {s8("False"), s8("True")};
+ stream_append_variable_base(s, VT_B32, texts, &var->u.b32);
+ } break;
+ case VT_F32: {
+ f32 scale = 1;
+ stream_append_variable_base(s, VT_F32, &var->u.f32, &scale);
+ } break;
+ default: INVALID_CODE_PATH;
+ }
+}
+
+
+static Variable *
+add_variable(Variable *group, Arena *arena, s8 name, VariableType type, Font font)
+{
+ Variable *result;
+ if (type == VT_BEAMFORMER_VARIABLE) result = (Variable *)push_struct(arena, BeamformerVariable);
+ else result = push_struct(arena, Variable);
+ result->type = type;
+ result->name = name;
+ result->parent = group;
+
+ if (group) {
+ if (group->u.group.last) group->u.group.last = group->u.group.last->next = result;
+ else group->u.group.last = group->u.group.first = result;
+
+ f32 name_width = measure_text(font, name).x;
+ group->u.group.max_name_width = MAX(group->u.group.max_name_width, name_width);
+ }
+
+ return result;
+}
+
+static Variable *
+add_variable_group(Variable *group, Arena *arena, s8 name, VariableGroupType type, Font font)
+{
+ Variable *result = add_variable(group, arena, name, VT_GROUP, font);
+ result->u.group.type = type;
+ return result;
+}
+
+static Variable *
+end_variable_group(Variable *group)
+{
+ ASSERT(group->type == VT_GROUP);
+ return group->parent;
+}
+
+static void
+add_beamformer_variable_f32(Variable *group, Arena *arena, s8 name, s8 suffix, f32 *store,
+ v2 limits, f32 display_scale, f32 scroll_scale, u32 flags, Font font)
+{
+ BeamformerVariable *bv = (BeamformerVariable *)add_variable(group, arena, name,
+ VT_BEAMFORMER_VARIABLE, font);
+ bv->suffix = suffix;
+ bv->base.u.generic = store;
+ bv->subtype = VT_F32;
+ bv->flags = flags;
+ bv->params.display_scale = display_scale;
+ bv->params.scroll_scale = scroll_scale;
+ bv->params.limits = limits;
+}
+
+static void
+add_beamformer_variable_b32(Variable *group, Arena *arena, s8 name, s8 false_text, s8 true_text,
+ b32 *store, u32 flags, Font font)
+{
+ BeamformerVariable *bv = (BeamformerVariable *)add_variable(group, arena, name,
+ VT_BEAMFORMER_VARIABLE, font);
+ bv->base.u.generic = store;
+ bv->subtype = VT_B32;
+ bv->flags = flags;
+ bv->name_table.names = alloc(arena, s8, 2);
+ bv->name_table.count = 2;
+ bv->name_table.names[0] = false_text;
+ bv->name_table.names[1] = true_text;
+}
+
+static void
+beamformer_parameters_view_init(BeamformerCtx *ctx)
+{
+ BeamformerUI *ui = ctx->ui;
+ BeamformerUIParameters *bp = &ui->params;
+
+ v2 v2_inf = {.x = -F32_INFINITY, .y = F32_INFINITY};
+
+ Variable *group = add_variable_group(0, &ui->arena, s8("Parameters List"), VG_LIST, ui->font);
+ ui->parameters_view = group;
+
+ add_beamformer_variable_f32(group, &ui->arena, s8("Sampling Frequency:"), s8("[MHz]"),
+ &bp->sampling_frequency, (v2){0}, 1e-6, 0, 0, ui->font);
+
+ add_beamformer_variable_f32(group, &ui->arena, s8("Center Frequency:"), s8("[MHz]"),
+ &bp->center_frequency, (v2){.y = 100e-6}, 1e-6, 1e5,
+ V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
+
+ add_beamformer_variable_f32(group, &ui->arena, s8("Speed of Sound:"), s8("[m/s]"),
+ &bp->speed_of_sound, (v2){.y = 1e6}, 1, 10,
+ V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
+
+ group = add_variable_group(group, &ui->arena, s8("Lateral Extent:"), VG_V2, ui->font);
+ {
+ add_beamformer_variable_f32(group, &ui->arena, s8("Min:"), s8("[mm]"),
+ &bp->output_min_coordinate.x, v2_inf, 1e3, 0.5e-3,
+ V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
+
+ add_beamformer_variable_f32(group, &ui->arena, s8("Max:"), s8("[mm]"),
+ &bp->output_max_coordinate.x, v2_inf, 1e3, 0.5e-3,
+ V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
+ }
+ group = end_variable_group(group);
+
+ group = add_variable_group(group, &ui->arena, s8("Axial Extent:"), VG_V2, ui->font);
+ {
+ add_beamformer_variable_f32(group, &ui->arena, s8("Min:"), s8("[mm]"),
+ &bp->output_min_coordinate.z, v2_inf, 1e3, 0.5e-3,
+ V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
+
+ add_beamformer_variable_f32(group, &ui->arena, s8("Max:"), s8("[mm]"),
+ &bp->output_max_coordinate.z, v2_inf, 1e3, 0.5e-3,
+ V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
+ }
+ group = end_variable_group(group);
+
+ add_beamformer_variable_f32(group, &ui->arena, s8("Off Axis Position:"), s8("[mm]"),
+ &bp->off_axis_pos, (v2){.x = -1e3, .y = 1e3}, 1e3,
+ 0.5e-3, V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
+
+ add_beamformer_variable_b32(group, &ui->arena, s8("Beamform Plane:"), s8("XZ"), s8("YZ"),
+ (b32 *)&bp->beamform_plane, V_INPUT|V_CAUSES_COMPUTE, ui->font);
+
+ add_beamformer_variable_f32(group, &ui->arena, s8("F#:"), s8(""), &bp->f_number,
+ (v2){.y = 1e3}, 1, 0.1, V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
+
+ add_beamformer_variable_f32(group, &ui->arena, s8("Dynamic Range:"), s8("[dB]"),
+ &ctx->fsctx.db, (v2){.x = -120}, 1, 1,
+ V_INPUT|V_TEXT|V_GEN_MIPMAPS, ui->font);
+
+ add_beamformer_variable_f32(group, &ui->arena, s8("Threshold:"), s8(""),
+ &ctx->fsctx.threshold, (v2){.y = 240}, 1, 1,
+ V_INPUT|V_TEXT|V_GEN_MIPMAPS, ui->font);
+
+ group = end_variable_group(group);
+ ASSERT(group == 0);
+}
+
+static BeamformerFrameView *
+beamformer_frame_view_new(BeamformerUI *ui, BeamformerFrameViewType type)
+{
+ BeamformerFrameView *result = push_struct(&ui->arena, typeof(*result));
+ result->type = type;
+ return result;
+}
+
+static BeamformFrame *
+beamformer_frame_view_frame(BeamformerUI *ui, BeamformerFrameView *bv)
+{
+ BeamformFrame *result;
+ switch (bv->type) {
+ case FVT_LATEST: result = ui->latest_frame; break;
+ default: result = bv->frame; break;
+ }
+ return result;
+}
+
static Color
colour_from_normalized(v4 rgba)
{
@@ -13,12 +417,6 @@ fade(Color a, f32 visibility)
return a;
}
-static f32
-lerp(f32 a, f32 b, f32 t)
-{
- return a + t * (b - a);
-}
-
static v4
lerp_v4(v4 a, v4 b, f32 t)
{
@@ -49,28 +447,9 @@ push_das_shader_id(Stream *s, DASShaderID shader, u32 transmit_count)
}
static v2
-measure_text(Font font, s8 text)
+draw_text(Font font, s8 text, v2 pos, Color colour)
{
- v2 result = {.y = font.baseSize};
- for (size i = 0; i < text.len; i++) {
- /* NOTE: assumes font glyphs are ordered ASCII */
- i32 idx = (i32)text.data[i] - 0x20;
- result.x += font.glyphs[idx].advanceX;
- if (font.glyphs[idx].advanceX == 0)
- result.x += (font.recs[idx].width + font.glyphs[idx].offsetX);
- }
- return result;
-}
-
-static v2
-draw_text(Font font, s8 text, v2 pos, f32 rotation, Color colour)
-{
- rlPushMatrix();
-
- rlTranslatef(pos.x, pos.y, 0);
- rlRotatef(rotation, 0, 0, 1);
-
- v2 off = {0};
+ v2 off = pos;
for (size i = 0; i < text.len; i++) {
/* NOTE: assumes font glyphs are ordered ASCII */
i32 idx = text.data[i] - 0x20;
@@ -92,8 +471,21 @@ draw_text(Font font, s8 text, v2 pos, f32 rotation, Color colour)
if (font.glyphs[idx].advanceX == 0)
off.x += font.recs[idx].width;
}
+ v2 result = {.x = off.x - pos.x, .y = font.baseSize};
+ return result;
+}
+
+static v2
+draw_text_r(Font font, s8 text, v2 pos, f32 rotation, Color colour)
+{
+ rlPushMatrix();
+
+ rlTranslatef(pos.x, pos.y, 0);
+ rlRotatef(rotation, 0, 0, 1);
+
+ v2 result = draw_text(font, text, (v2){0}, colour);
+
rlPopMatrix();
- v2 result = {.x = off.x, .y = font.baseSize};
return result;
}
@@ -158,7 +550,7 @@ draw_ruler(BeamformerUI *ui, Stream *buf, v2 start_point, v2 end_point,
if (draw_plus && value > 0) stream_append_byte(buf, '+');
stream_append_f64(buf, value, 10);
stream_append_s8(buf, suffix);
- draw_text(ui->small_font, stream_to_s8(buf), tp, 90, txt_colour);
+ draw_text_r(ui->small_font, stream_to_s8(buf), tp, 90, txt_colour);
value += value_inc;
sp.x += inc;
@@ -179,11 +571,10 @@ draw_ruler(BeamformerUI *ui, Stream *buf, v2 start_point, v2 end_point,
}
static void
-do_scale_bar(BeamformerUI *ui, Stream *buf, Variable var, v2 mouse, i32 direction, Rect draw_rect,
- f32 start_value, f32 end_value, s8 suffix)
+do_scale_bar(BeamformerUI *ui, Stream *buf, ScaleBar *sb, ScaleBarDirection direction,
+ v2 mouse, Rect draw_rect, f32 start_value, f32 end_value, s8 suffix)
{
InteractionState *is = &ui->interaction;
- ScaleBar *sb = var.store;
v2 txt_s = measure_text(ui->small_font, s8("-288.8 mm"));
@@ -216,8 +607,12 @@ do_scale_bar(BeamformerUI *ui, Stream *buf, Variable var, v2 mouse, i32 directio
}
if (hover_text(mouse, tick_rect, &sb->hover_t, 1)) {
- is->hot_state = IS_SCALE_BAR;
- is->hot = var;
+ Variable *var = zero_struct(&ui->scratch_beamformer_variable.base);
+ var->u.generic = sb;
+ var->type = VT_SCALE_BAR;
+ is->hot_type = IT_SCALE_BAR;
+ is->hot = var;
+
marker_count = 2;
}
@@ -227,11 +622,13 @@ do_scale_bar(BeamformerUI *ui, Stream *buf, Variable var, v2 mouse, i32 directio
}
static void
-draw_display_overlay(BeamformerCtx *ctx, Arena a, v2 mouse, Rect display_rect, BeamformFrame *frame)
+draw_beamform_view(BeamformerCtx *ctx, Arena a, v2 mouse, BeamformerFrameView *view)
{
BeamformerUI *ui = ctx->ui;
BeamformerUIParameters *bp = &ui->params;
InteractionState *is = &ui->interaction;
+ Rect display_rect = view->display_rect;
+ BeamformFrame *frame = beamformer_frame_view_frame(ui, view);
Stream buf = arena_stream(&a);
Texture *output = &ctx->fsctx.output.texture;
@@ -285,39 +682,37 @@ draw_display_overlay(BeamformerCtx *ctx, Arena a, v2 mouse, Rect display_rect, B
NPatchInfo tex_np = { tex_r, 0, 0, 0, 0, NPATCH_NINE_PATCH };
DrawTextureNPatch(*output, tex_np, vr.rl, (Vector2){0}, 0, WHITE);
- Variable var = {.display_scale = 1e3};
- var.store = ui->scale_bars[0] + SB_LATERAL;
- var.f32_limits = (v2){.x = -1, .y = 1};
- var.scroll_scale = 0.5e-3;
-
v2 start_pos = vr.pos;
start_pos.y += vr.size.y;
- do_scale_bar(ui, &buf, var, mouse, SB_LATERAL, (Rect){.pos = start_pos, .size = vr.size},
- bp->output_min_coordinate.x * 1e3, bp->output_max_coordinate.x * 1e3, s8(" mm"));
-
- var.store = ui->scale_bars[0] + SB_AXIAL;
- var.f32_limits = (v2){.x = 0, .y = 1};
- var.scroll_scale = 1e-3;
+ do_scale_bar(ui, &buf, &view->lateral_scale_bar, SB_LATERAL, mouse,
+ (Rect){.pos = start_pos, .size = vr.size}, bp->output_min_coordinate.x * 1e3,
+ bp->output_max_coordinate.x * 1e3, s8(" mm"));
start_pos = vr.pos;
start_pos.x += vr.size.x;
- do_scale_bar(ui, &buf, var, mouse, SB_AXIAL, (Rect){.pos = start_pos, .size = vr.size},
- bp->output_max_coordinate.z * 1e3, bp->output_min_coordinate.z * 1e3, s8(" mm"));
+ do_scale_bar(ui, &buf, &view->axial_scale_bar, SB_AXIAL, mouse,
+ (Rect){.pos = start_pos, .size = vr.size}, bp->output_max_coordinate.z * 1e3,
+ bp->output_min_coordinate.z * 1e3, s8(" mm"));
v2 pixels_to_mm = output_dim;
pixels_to_mm.x /= vr.size.x * 1e-3;
pixels_to_mm.y /= vr.size.y * 1e-3;
if (CheckCollisionPointRec(mouse.rl, vr.rl)) {
- is->hot_state = IS_DISPLAY;
- is->hot.store = &ctx->fsctx.threshold;
- is->hot.type = VT_F32;
- is->hot.f32_limits = (v2){.y = 240};
- is->hot.flags = V_GEN_MIPMAPS;
- is->hot.display_scale = 1;
- is->hot.scroll_scale = 1;
+ BeamformerVariable *bv = zero_struct(&ui->scratch_beamformer_variable);
+ bv->base.type = VT_BEAMFORMER_VARIABLE;
+ bv->base.u.generic = &ctx->fsctx.threshold;
+ bv->subtype = VT_F32;
+ bv->flags = V_GEN_MIPMAPS;
+ bv->params.limits = (v2){.y = 240};
+ bv->params.display_scale = 1;
+ bv->params.scroll_scale = 1;
+
+ is->hot_type = IT_DISPLAY;
+ is->hot = (Variable *)bv;
+
v2 relative_mouse = sub_v2(mouse, vr.pos);
v2 mm = mul_v2(relative_mouse, pixels_to_mm);
@@ -331,8 +726,7 @@ draw_display_overlay(BeamformerCtx *ctx, Arena a, v2 mouse, Rect display_rect, B
.x = vr.pos.x + vr.size.w - txt_s.w - 4,
.y = vr.pos.y + vr.size.h - txt_s.h - 4,
};
- draw_text(ui->small_font, stream_to_s8(&buf), txt_p, 0,
- colour_from_normalized(RULER_COLOUR));
+ draw_text(ui->small_font, stream_to_s8(&buf), txt_p, colour_from_normalized(RULER_COLOUR));
}
{
@@ -343,9 +737,9 @@ draw_display_overlay(BeamformerCtx *ctx, Arena a, v2 mouse, Rect display_rect, B
.x = vr.pos.x + vr.size.w - txt_s.w - 16,
.y = vr.pos.y + 4,
};
- draw_text(ui->font, shader, txt_p, 0, BLACK);
+ draw_text(ui->font, shader, txt_p, fade(BLACK, 0.8));
txt_p = floor_v2(sub_v2(txt_p, (v2){.x = -1.5, .y = -1.5}));
- draw_text(ui->font, shader, txt_p, 0, colour_from_normalized(RULER_COLOUR));
+ draw_text(ui->font, shader, txt_p, colour_from_normalized(RULER_COLOUR));
}
/* TODO(rnp): store converted ruler points instead of screen points */
@@ -372,317 +766,153 @@ draw_display_overlay(BeamformerCtx *ctx, Arena a, v2 mouse, Rect display_rect, B
v2 txt_s = measure_text(ui->small_font, stream_to_s8(&buf));
if (pixel_delta.y < 0) txt_p.y -= txt_s.y;
if (pixel_delta.x < 0) txt_p.x -= txt_s.x;
- draw_text(ui->small_font, stream_to_s8(&buf), txt_p, 0, colour);
+ draw_text(ui->small_font, stream_to_s8(&buf), txt_p, colour);
}
}
-/* TODO(rnp): this is known after the first frame, we could unbind
- * the texture for the first draw pass or just accept a slight glitch
- * at start up (make a good default guess) */
-/* NOTE: This is kinda sucks no matter how you spin it. If we want the values to be
- * left aligned in the center column we need to know the longest prefix length but
- * without either hardcoding one of the prefixes as the longest one or measuring all
- * of them we can't know this ahead of time. For now we hardcode this and manually
- * adjust when needed */
-#define LISTING_LEFT_COLUMN_WIDTH 270.0f
-#define LISTING_LINE_PAD 6.0f
-
-static Rect
-do_value_listing(s8 prefix, s8 suffix, Arena a, f32 value, Font font, Rect r)
-{
- v2 suffix_s = measure_text(font, suffix);
- v2 suffix_p = {.x = r.pos.x + r.size.w - suffix_s.w, .y = r.pos.y};
-
- Stream buf = arena_stream(&a);
- stream_append_f64(&buf, value, 100);
- v2 txt_p = {.x = r.pos.x + LISTING_LEFT_COLUMN_WIDTH, .y = r.pos.y};
-
- draw_text(font, prefix, r.pos, 0, colour_from_normalized(FG_COLOUR));
- draw_text(font, stream_to_s8(&buf), txt_p, 0, colour_from_normalized(FG_COLOUR));
- draw_text(font, suffix, suffix_p, 0, colour_from_normalized(FG_COLOUR));
- r.pos.y += suffix_s.h + LISTING_LINE_PAD;
- r.size.y -= suffix_s.h + LISTING_LINE_PAD;
-
- return r;
-}
-
-static Rect
-do_text_input_listing(s8 prefix, s8 suffix, Variable var, BeamformerUI *ui, Rect r,
- v2 mouse, f32 *hover_t)
+static v2
+draw_text_beamformer_variable(BeamformerUI *ui, Arena arena, Variable *var, v2 at, v2 mouse, f32 *hover_t)
{
- InputState *is = &ui->text_input_state;
- b32 text_input_active = (ui->interaction.state == IS_TEXT) &&
- (var.store == ui->interaction.active.store);
-
- Arena arena = ui->arena_for_frame;
- Stream buf = arena_stream(&arena);
- v2 txt_s;
+ InputState *is = &ui->text_input_state;
+ b32 var_text_active = (ui->interaction.type == IT_TEXT) && (var == ui->interaction.active);
- if (text_input_active) {
- txt_s = measure_text(ui->font, (s8){.len = is->buf_len, .data = is->buf});
- } else {
- stream_append_variable(&buf, &var);
- txt_s = measure_text(ui->font, stream_to_s8(&buf));
- }
+ Stream buf = arena_stream(&arena);
+ if (var_text_active) stream_append_s8(&buf, (s8){.len = is->buf_len, .data = is->buf});
+ else stream_append_variable(&buf, var);
- Rect edit_rect = {
- .pos = {.x = r.pos.x + LISTING_LEFT_COLUMN_WIDTH, .y = r.pos.y},
- .size = {.x = txt_s.w + TEXT_BOX_EXTRA_X, .y = txt_s.h}
- };
+ v2 text_size = measure_text(ui->font, stream_to_s8(&buf));
+ Rect text_rect = {.pos = at, .size = text_size};
- b32 hovering = hover_text(mouse, edit_rect, hover_t, !text_input_active);
- if (hovering)
+ if (hover_text(mouse, text_rect, hover_t, !var_text_active)) {
ui->interaction.hot = var;
-
- /* TODO: where should this go? */
- if (text_input_active && is->cursor == -1) {
- /* NOTE: extra offset to help with putting a cursor at idx 0 */
- #define TEXT_HALF_CHAR_WIDTH 10
- f32 hover_p = CLAMP01((mouse.x - edit_rect.pos.x) / edit_rect.size.w);
- f32 x_off = TEXT_HALF_CHAR_WIDTH, x_bounds = edit_rect.size.w * hover_p;
- i32 i;
- for (i = 0; i < is->buf_len && x_off < x_bounds; i++) {
- /* NOTE: assumes font glyphs are ordered ASCII */
- i32 idx = is->buf[i] - 0x20;
- x_off += ui->font.glyphs[idx].advanceX;
- if (ui->font.glyphs[idx].advanceX == 0)
- x_off += ui->font.recs[idx].width;
- }
- is->cursor = i;
+ is->hot_rect = text_rect;
+ is->hot_font = &ui->font;
}
Color colour = colour_from_normalized(lerp_v4(FG_COLOUR, HOVERED_COLOUR, *hover_t));
+ draw_text(ui->font, stream_to_s8(&buf), text_rect.pos, colour);
- if (text_input_active) {
- s8 buf = {.len = is->buf_len, .data = is->buf};
- v2 ts = measure_text(ui->font, buf);
- v2 pos = {.x = edit_rect.pos.x, .y = edit_rect.pos.y + (edit_rect.size.y - ts.y) / 2};
-
- #define MAX_DISP_CHARS 7
- i32 buf_delta = is->buf_len - MAX_DISP_CHARS;
- if (buf_delta < 0) buf_delta = 0;
- buf.len -= buf_delta;
- buf.data += buf_delta;
- {
- /* NOTE: drop a char if the subtext still doesn't fit */
- v2 nts = measure_text(ui->font, buf);
- if (nts.w > 0.96 * edit_rect.size.w) {
- buf.data++;
- buf.len--;
- }
- }
- draw_text(ui->font, buf, pos, 0, colour);
-
- v4 bg = FOCUSED_COLOUR;
- bg.a = 0;
- Color cursor_colour = colour_from_normalized(lerp_v4(bg, FOCUSED_COLOUR,
- CLAMP01(is->cursor_blink_t)));
- buf.len = is->cursor - buf_delta;
- v2 sts = measure_text(ui->font, buf);
- f32 cursor_x = pos.x + sts.x;
- f32 cursor_width;
- if (is->cursor == is->buf_len) cursor_width = 20;
- else cursor_width = 4;
- Rect cursor_r = {
- .pos = {.x = cursor_x, .y = pos.y},
- .size = {.w = cursor_width, .h = ts.h},
- };
-
- DrawRectanglePro(cursor_r.rl, (Vector2){0}, 0, cursor_colour);
- } else {
- draw_text(ui->font, stream_to_s8(&buf), edit_rect.pos, 0, colour);
- }
-
- v2 suffix_s = measure_text(ui->font, suffix);
- v2 suffix_p = {.x = r.pos.x + r.size.w - suffix_s.w, .y = r.pos.y};
- draw_text(ui->font, prefix, r.pos, 0, colour_from_normalized(FG_COLOUR));
- draw_text(ui->font, suffix, suffix_p, 0, colour_from_normalized(FG_COLOUR));
-
- r.pos.y += suffix_s.h + LISTING_LINE_PAD;
- r.size.y -= suffix_s.h + LISTING_LINE_PAD;
-
- return r;
+ return text_size;
}
-static Rect
-do_text_toggle_listing(s8 prefix, s8 text0, s8 text1, Variable var,
- BeamformerUI *ui, Rect r, v2 mouse, f32 *hover_t)
+static v2
+draw_beamformer_variable(BeamformerUI *ui, Arena arena, Variable *var, v2 at, v2 mouse, f32 *hover_t)
{
- b32 toggle = *(b32 *)var.store;
- v2 txt_s;
- if (toggle) txt_s = measure_text(ui->font, text1);
- else txt_s = measure_text(ui->font, text0);
-
- Rect edit_rect = {
- .pos = {.x = r.pos.x + LISTING_LEFT_COLUMN_WIDTH, .y = r.pos.y},
- .size = {.x = txt_s.w + TEXT_BOX_EXTRA_X, .y = txt_s.h}
- };
+ Stream buf = arena_stream(&arena);
+ stream_append_variable(&buf, var);
+
+ v2 text_size = measure_text(ui->font, stream_to_s8(&buf));
+ Rect text_rect = {.pos = at, .size = text_size};
- if (hover_text(mouse, edit_rect, hover_t, 1))
+ if (hover_text(mouse, text_rect, hover_t, 1))
ui->interaction.hot = var;
Color colour = colour_from_normalized(lerp_v4(FG_COLOUR, HOVERED_COLOUR, *hover_t));
- draw_text(ui->font, prefix, r.pos, 0, colour_from_normalized(FG_COLOUR));
- draw_text(ui->font, toggle? text1: text0, edit_rect.pos, 0, colour);
+ draw_text(ui->font, stream_to_s8(&buf), text_rect.pos, colour);
- r.pos.y += txt_s.h + LISTING_LINE_PAD;
- r.size.y -= txt_s.h + LISTING_LINE_PAD;
-
- return r;
+ return text_size;
}
-static b32
-do_text_button(BeamformerUI *ui, s8 text, Rect r, v2 mouse, f32 *hover_t)
-{
- b32 hovering = hover_text(mouse, r, hover_t, 1);
- b32 pressed = 0;
- pressed |= (hovering && IsMouseButtonPressed(MOUSE_BUTTON_LEFT));
- pressed |= (hovering && IsMouseButtonPressed(MOUSE_BUTTON_RIGHT));
-
- f32 param = lerp(1, 1.04, *hover_t);
- v2 bscale = (v2){
- .x = param + RECT_BTN_BORDER_WIDTH / r.size.w,
- .y = param + RECT_BTN_BORDER_WIDTH / r.size.h,
- };
- Rect sr = scale_rect_centered(r, (v2){.x = param, .y = param});
- Rect sb = scale_rect_centered(r, bscale);
- DrawRectangleRounded(sb.rl, RECT_BTN_ROUNDNESS, 0, RECT_BTN_BORDER_COLOUR);
- DrawRectangleRounded(sr.rl, RECT_BTN_ROUNDNESS, 0, RECT_BTN_COLOUR);
+#define LISTING_CENTER_PAD 12.0f
+#define LISTING_INDENT 20.0f
+#define LISTING_LINE_PAD 6.0f
- v2 tpos = center_align_text_in_rect(r, text, ui->font);
- v2 spos = {.x = tpos.x + 1.75, .y = tpos.y + 2};
- v4 colour = lerp_v4(FG_COLOUR, HOVERED_COLOUR, *hover_t);
+static Rect
+draw_variable_list(BeamformerUI *ui, Variable *group, Rect r, v2 mouse)
+{
+ ASSERT(group->type == VT_GROUP);
+ r.pos.x += LISTING_INDENT;
+ r.size.x -= LISTING_INDENT;
+
+ Variable *var = group->u.group.first;
+ f32 x_off = group->u.group.max_name_width;
+ while (var) {
+ s8 suffix = {0};
+ v2 at = r.pos;
+ f32 advance = 0;
+ switch (var->type) {
+ case VT_BEAMFORMER_VARIABLE: {
+ BeamformerVariable *bv = (BeamformerVariable *)var;
+
+ suffix = bv->suffix;
+ advance = draw_text(ui->font, bv->base.name, at, NORMALIZED_FG_COLOUR).y;
+ at.x += x_off + LISTING_CENTER_PAD;
+
+ if (bv->flags & V_INPUT && bv->flags & V_TEXT) {
+ draw_text_beamformer_variable(ui, ui->arena, var, at, mouse, &bv->hover_t);
+ } else if (bv->flags & V_INPUT) {
+ draw_beamformer_variable(ui, ui->arena, var, at, mouse, &bv->hover_t);
+ } else {
+ Arena arena = ui->arena;
+ Stream buf = arena_stream(&arena);
+ stream_append_variable(&buf, var);
+ draw_text(ui->font, stream_to_s8(&buf), at, NORMALIZED_FG_COLOUR);
+ }
- draw_text(ui->font, text, spos, 0, fade(BLACK, 0.8));
- draw_text(ui->font, text, tpos, 0, colour_from_normalized(colour));
+ while (var) {
+ if (var->next) {
+ var = var->next;
+ break;
+ }
+ var = var->parent;
+ r.pos.x -= LISTING_INDENT;
+ r.size.x += LISTING_INDENT;
+ x_off += LISTING_INDENT;
+ }
+ } break;
+ case VT_GROUP: {
+ VariableGroup *g = &var->u.group;
+
+ advance = draw_beamformer_variable(ui, ui->arena, var, at, mouse, &g->hover_t).y;
+ at.x += x_off + LISTING_CENTER_PAD;
+ if (g->expanded) {
+ r.pos.x += LISTING_INDENT;
+ r.size.x -= LISTING_INDENT;
+ x_off -= LISTING_INDENT;
+ var = g->first;
+ } else {
+ BeamformerVariable *v = (BeamformerVariable *)g->first;
+
+ ASSERT(!v || v->base.type == VT_BEAMFORMER_VARIABLE);
+ /* NOTE(rnp): assume the suffix is the same for all elements */
+ if (v) suffix = v->suffix;
+
+ switch (g->type) {
+ case VG_LIST: break;
+ case VG_V2:
+ case VG_V4: {
+ at.x += draw_text(ui->font, s8("{"), at, NORMALIZED_FG_COLOUR).x;
+ while (v) {
+ at.x += draw_beamformer_variable(ui, ui->arena,
+ (Variable *)v, at,
+ mouse, &v->hover_t).x;
+ v = (BeamformerVariable *)v->base.next;
+ if (v) at.x += draw_text(ui->font, s8(", "), at,
+ NORMALIZED_FG_COLOUR).x;
+ }
+ at.x += draw_text(ui->font, s8("}"), at, NORMALIZED_FG_COLOUR).x;
+ } break;
+ }
+
+ var = var->next;
+ }
+ } break;
+ default: INVALID_CODE_PATH;
+ }
- return pressed;
-}
+ if (suffix.len) {
+ v2 suffix_s = measure_text(ui->font, suffix);
+ v2 suffix_p = {.x = r.pos.x + r.size.w - suffix_s.w, .y = r.pos.y};
+ draw_text(ui->font, suffix, suffix_p, NORMALIZED_FG_COLOUR);
+ }
-static void
-draw_settings_ui(BeamformerCtx *ctx, Rect r, v2 mouse)
-{
- BeamformerUI *ui = ctx->ui;
- BeamformerUIParameters *bp = &ui->params;
+ r.pos.y += advance + LISTING_LINE_PAD;
+ r.size.y -= advance + LISTING_LINE_PAD;
+ }
- f32 minx = bp->output_min_coordinate.x + 1e-6, maxx = bp->output_max_coordinate.x - 1e-6;
- f32 minz = bp->output_min_coordinate.z + 1e-6, maxz = bp->output_max_coordinate.z - 1e-6;
-
- Rect draw_r = r;
- draw_r.pos.y += 20;
- draw_r.pos.x += 20;
- draw_r.size.x -= 20;
- draw_r.size.y -= 20;
-
- draw_r = do_value_listing(s8("Sampling Frequency:"), s8("[MHz]"), ui->arena_for_frame,
- bp->sampling_frequency * 1e-6, ui->font, draw_r);
-
- static f32 hover_t[15];
- i32 idx = 0;
-
- Variable var;
-
- var.store = &bp->center_frequency;
- var.type = VT_F32;
- var.f32_limits = (v2){.y = 100e6};
- var.flags = V_CAUSES_COMPUTE;
- var.display_scale = 1e-6;
- var.scroll_scale = 1e5;
- draw_r = do_text_input_listing(s8("Center Frequency:"), s8("[MHz]"), var, ui, draw_r,
- mouse, hover_t + idx++);
-
- var.store = &bp->speed_of_sound;
- var.type = VT_F32;
- var.f32_limits = (v2){.y = 1e6};
- var.flags = V_CAUSES_COMPUTE;
- var.display_scale = 1;
- var.scroll_scale = 10;
- draw_r = do_text_input_listing(s8("Speed of Sound:"), s8("[m/s]"), var, ui, draw_r,
- mouse, hover_t + idx++);
-
- var.store = &bp->output_min_coordinate.x;
- var.type = VT_F32;
- var.f32_limits = (v2){.x = -1e3, .y = maxx};
- var.flags = V_CAUSES_COMPUTE;
- var.display_scale = 1e3;
- var.scroll_scale = 0.5e-3;
- draw_r = do_text_input_listing(s8("Min Lateral Point:"), s8("[mm]"), var, ui, draw_r,
- mouse, hover_t + idx++);
-
- var.store = &bp->output_max_coordinate.x;
- var.type = VT_F32;
- var.f32_limits = (v2){.x = minx, .y = 1e3};
- var.flags = V_CAUSES_COMPUTE;
- var.display_scale = 1e3;
- var.scroll_scale = 0.5e-3;
- draw_r = do_text_input_listing(s8("Max Lateral Point:"), s8("[mm]"), var, ui, draw_r,
- mouse, hover_t + idx++);
-
- var.store = &bp->output_min_coordinate.z;
- var.type = VT_F32;
- var.f32_limits = (v2){.y = maxz};
- var.flags = V_CAUSES_COMPUTE;
- var.display_scale = 1e3;
- var.scroll_scale = 0.5e-3;
- draw_r = do_text_input_listing(s8("Min Axial Point:"), s8("[mm]"), var, ui, draw_r,
- mouse, hover_t + idx++);
-
- var.store = &bp->output_max_coordinate.z;
- var.type = VT_F32;
- var.f32_limits = (v2){.x = minz, .y = 1e3};
- var.flags = V_CAUSES_COMPUTE;
- var.display_scale = 1e3;
- var.scroll_scale = 0.5e-3;
- draw_r = do_text_input_listing(s8("Max Axial Point:"), s8("[mm]"), var, ui, draw_r,
- mouse, hover_t + idx++);
-
- var.store = &bp->off_axis_pos;
- var.type = VT_F32;
- var.f32_limits = (v2){.x = minx, .y = maxx};
- var.flags = V_CAUSES_COMPUTE;
- var.display_scale = 1e3;
- var.scroll_scale = 0.5e-3;
- draw_r = do_text_input_listing(s8("Off Axis Position:"), s8("[mm]"), var, ui, draw_r,
- mouse, hover_t + idx++);
-
- var = (Variable){0};
- var.store = &bp->beamform_plane;
- var.type = VT_B32;
- var.flags = V_CAUSES_COMPUTE;
- draw_r = do_text_toggle_listing(s8("Beamform Plane:"), s8("XZ"), s8("YZ"), var, ui,
- draw_r, mouse, hover_t + idx++);
-
- var.store = &bp->f_number;
- var.type = VT_F32;
- var.f32_limits = (v2){.y = 1e3};
- var.flags = V_CAUSES_COMPUTE;
- var.display_scale = 1;
- var.scroll_scale = 0.1;
- draw_r = do_text_input_listing(s8("F#:"), s8(""), var, ui, draw_r, mouse, hover_t + idx++);
-
- var.store = &ctx->fsctx.db;
- var.type = VT_F32;
- var.f32_limits = (v2){.x = -120};
- var.flags = V_GEN_MIPMAPS;
- var.display_scale = 1;
- var.scroll_scale = 1;
- draw_r = do_text_input_listing(s8("Dynamic Range:"), s8("[dB]"), var, ui, draw_r,
- mouse, hover_t + idx++);
-
- var.store = &ctx->fsctx.threshold;
- var.type = VT_F32;
- var.f32_limits = (v2){.y = 240};
- var.flags = V_GEN_MIPMAPS;
- var.display_scale = 1;
- var.scroll_scale = 1;
- draw_r = do_text_input_listing(s8("Threshold:"), s8(""), var, ui, draw_r,
- mouse, hover_t + idx++);
-
- draw_r.pos.y += 2 * LISTING_LINE_PAD;
- draw_r.size.y -= 2 * LISTING_LINE_PAD;
-
- /* NOTE: if C compilers didn't suck this would be a static assert */
- ASSERT(idx <= ARRAY_COUNT(hover_t));
+ r.pos.x -= LISTING_INDENT;
+ r.size.x += LISTING_INDENT;
+ return r;
}
static void
@@ -705,26 +935,26 @@ draw_debug_overlay(BeamformerCtx *ctx, BeamformFrame *frame, Arena arena, Rect r
for (u32 i = 0; i < stages; i++) {
u32 index = ctx->params->compute_stages[i];
pos.y -= measure_text(ui->font, labels[index]).y;
- draw_text(ui->font, labels[index], pos, 0, colour_from_normalized(FG_COLOUR));
+ draw_text(ui->font, labels[index], pos, colour_from_normalized(FG_COLOUR));
buf.widx = 0;
stream_append_f64_e(&buf, frame->compute_times[index]);
stream_append_s8(&buf, s8(" [s]"));
v2 txt_fs = measure_text(ui->font, stream_to_s8(&buf));
v2 rpos = {.x = r.pos.x + r.size.w - txt_fs.w, .y = pos.y};
- draw_text(ui->font, stream_to_s8(&buf), rpos, 0, colour_from_normalized(FG_COLOUR));
+ draw_text(ui->font, stream_to_s8(&buf), rpos, colour_from_normalized(FG_COLOUR));
compute_time_sum += frame->compute_times[index];
}
pos.y -= ui->font.baseSize;
- draw_text(ui->font, s8("Compute Total:"), pos, 0, colour_from_normalized(FG_COLOUR));
+ draw_text(ui->font, s8("Compute Total:"), pos, colour_from_normalized(FG_COLOUR));
buf.widx = 0;
stream_append_f64_e(&buf, compute_time_sum);
stream_append_s8(&buf, s8(" [s]"));
v2 txt_fs = measure_text(ui->font, stream_to_s8(&buf));
v2 rpos = {.x = r.pos.x + r.size.w - txt_fs.w, .y = pos.y};
- draw_text(ui->font, stream_to_s8(&buf), rpos, 0, colour_from_normalized(FG_COLOUR));
+ draw_text(ui->font, stream_to_s8(&buf), rpos, colour_from_normalized(FG_COLOUR));
pos.y -= ui->font.baseSize;
if (ctx->csctx.processing_compute) ui->progress_display_t_velocity += 65 * dt_for_frame;
@@ -738,7 +968,7 @@ draw_debug_overlay(BeamformerCtx *ctx, BeamformFrame *frame, Arena arena, Rect r
s8 progress_text = s8("Compute Progress:");
v2 txt_s = measure_text(ui->font, progress_text);
rpos = (v2){.x = pos.x + txt_s.x + 8, .y = pos.y};
- draw_text(ui->font, progress_text, pos, 0,
+ draw_text(ui->font, progress_text, pos,
fade(colour_from_normalized(FG_COLOUR), ui->progress_display_t));
Rect prect = {.pos = rpos, .size = {.w = r.size.w - rpos.x, .h = txt_s.h}};
prect = scale_rect_centered(prect, (v2){.x = 1, .y = 0.7});
@@ -753,40 +983,123 @@ draw_debug_overlay(BeamformerCtx *ctx, BeamformFrame *frame, Arena arena, Rect r
}
static void
+draw_input_cursor(BeamformerUI *ui, v2 mouse)
+{
+ InputState *is = &ui->text_input_state;
+ Rect box = is->rect;
+
+ s8 buf = {.len = is->buf_len, .data = is->buf};
+ v2 ts = measure_text(ui->font, buf);
+ v2 pos = {.x = box.pos.x, .y = box.pos.y + (box.size.y - ts.y) / 2};
+
+ v4 bg = FOCUSED_COLOUR;
+ bg.a = 0;
+ Color cursor_colour = colour_from_normalized(lerp_v4(bg, FOCUSED_COLOUR,
+ CLAMP01(is->cursor_blink_t)));
+ buf.len = is->cursor;
+ v2 sts = measure_text(ui->font, buf);
+ f32 cursor_x = pos.x + sts.x;
+ f32 cursor_width;
+ if (is->cursor == is->buf_len) cursor_width = 20;
+ else cursor_width = 4;
+ Rect cursor_r = {
+ .pos = {.x = cursor_x, .y = pos.y},
+ .size = {.w = cursor_width, .h = ts.h},
+ };
+
+ DrawRectanglePro(cursor_r.rl, (Vector2){0}, 0, cursor_colour);
+}
+
+static void
+ui_store_variable_base(VariableType type, void *store, void *new_value, void *limits)
+{
+ switch (type) {
+ case VT_B32: {
+ *(b32 *)store = *(b32 *)new_value;
+ } break;
+ case VT_F32: {
+ v2 *lim = limits;
+ f32 val = *(f32 *)new_value;
+ *(f32 *)store = CLAMP(val, lim->x, lim->y);
+ } break;
+ default: INVALID_CODE_PATH;
+ }
+}
+
+static void
ui_store_variable(Variable *var, void *new_value)
{
- /* TODO: special cases (eg. power of 2) */
switch (var->type) {
case VT_F32: {
- f32 f32_val = *(f32 *)new_value;
- f32 *f32_var = var->store;
- *f32_var = CLAMP(f32_val, var->f32_limits.x, var->f32_limits.y);
+ v2 limits = {.x = -F32_INFINITY, .y = F32_INFINITY};
+ ui_store_variable_base(VT_F32, &var->u.f32, new_value, &limits);
} break;
- case VT_I32: {
- i32 i32_val = *(i32 *)new_value;
- i32 *i32_var = var->store;
- *i32_var = CLAMP(i32_val, var->i32_limits.x, var->i32_limits.y);
+ case VT_BEAMFORMER_VARIABLE: {
+ BeamformerVariable *bv = (BeamformerVariable *)var;
+ ui_store_variable_base(bv->subtype, var->u.generic, new_value, &bv->params.limits);
} break;
default: INVALID_CODE_PATH;
}
}
static void
-begin_text_input(InputState *is, Variable *var)
+scroll_interaction_base(VariableType type, void *store, f32 delta)
+{
+ switch (type) {
+ case VT_B32: { *(b32 *)store = !*(b32 *)store; } break;
+ case VT_F32: { *(f32 *)store += delta; } break;
+ case VT_I32: { *(i32 *)store += delta; } break;
+ default: INVALID_CODE_PATH;
+ }
+}
+
+static void
+scroll_interaction(Variable *var, f32 delta)
{
- ASSERT(var->store != NULL);
+ switch (var->type) {
+ case VT_BEAMFORMER_VARIABLE: {
+ BeamformerVariable *bv = (BeamformerVariable *)var;
+ scroll_interaction_base(bv->subtype, var->u.generic, delta * bv->params.scroll_scale);
+ ui_store_variable(var, var->u.generic);
+ } break;
+ default: scroll_interaction_base(var->type, &var->u, delta); break;
+ }
+}
+static void
+begin_text_input(InputState *is, Variable *var, v2 mouse)
+{
Stream s = {.cap = ARRAY_COUNT(is->buf), .data = is->buf};
stream_append_variable(&s, var);
- ASSERT(!s.errors);
is->buf_len = s.widx;
- is->cursor = -1;
+ is->rect = is->hot_rect;
+ is->font = is->hot_font;
+
+ /* NOTE: extra offset to help with putting a cursor at idx 0 */
+ #define TEXT_HALF_CHAR_WIDTH 10
+ f32 hover_p = CLAMP01((mouse.x - is->rect.pos.x) / is->rect.size.w);
+ f32 x_off = TEXT_HALF_CHAR_WIDTH, x_bounds = is->rect.size.w * hover_p;
+ i32 i;
+ for (i = 0; i < is->buf_len && x_off < x_bounds; i++) {
+ /* NOTE: assumes font glyphs are ordered ASCII */
+ i32 idx = is->buf[i] - 0x20;
+ x_off += is->font->glyphs[idx].advanceX;
+ if (is->font->glyphs[idx].advanceX == 0)
+ x_off += is->font->recs[idx].width;
+ }
+ is->cursor = i;
}
static void
end_text_input(InputState *is, Variable *var)
{
- f32 value = parse_f64((s8){.len = is->buf_len, .data = is->buf}) / var->display_scale;
+ f32 scale = 1;
+ if (var->type == VT_BEAMFORMER_VARIABLE) {
+ BeamformerVariable *bv = (BeamformerVariable *)var;
+ ASSERT(bv->subtype == VT_F32);
+ scale = bv->params.display_scale;
+ }
+ f32 value = parse_f64((s8){.len = is->buf_len, .data = is->buf}) / scale;
ui_store_variable(var, &value);
}
@@ -842,17 +1155,10 @@ update_text_input(InputState *is)
}
static void
-ui_gen_mipmaps(BeamformerCtx *ctx)
-{
- if (ctx->fsctx.output.texture.id)
- ctx->fsctx.gen_mipmaps = 1;
-}
-
-static void
display_interaction_end(BeamformerUI *ui)
{
- b32 is_hot = ui->interaction.hot_state == IS_DISPLAY;
- b32 is_active = ui->interaction.state == IS_DISPLAY;
+ b32 is_hot = ui->interaction.hot_type == IT_DISPLAY;
+ b32 is_active = ui->interaction.type == IT_DISPLAY;
if ((is_active && is_hot) || ui->ruler_state == RS_HOLD)
return;
ui->ruler_state = RS_NONE;
@@ -863,8 +1169,8 @@ display_interaction(BeamformerUI *ui, v2 mouse)
{
b32 mouse_left_pressed = IsMouseButtonPressed(MOUSE_BUTTON_LEFT);
b32 mouse_right_pressed = IsMouseButtonPressed(MOUSE_BUTTON_RIGHT);
- b32 is_hot = ui->interaction.hot_state == IS_DISPLAY;
- b32 is_active = ui->interaction.state == IS_DISPLAY;
+ b32 is_hot = ui->interaction.hot_type == IT_DISPLAY;
+ b32 is_active = ui->interaction.type == IT_DISPLAY;
if (mouse_left_pressed && is_active) {
ui->ruler_state++;
@@ -885,10 +1191,10 @@ scale_bar_interaction(BeamformerCtx *ctx, v2 mouse)
{
BeamformerUI *ui = ctx->ui;
InteractionState *is = &ui->interaction;
- ScaleBar *sb = is->active.store;
+ ScaleBar *sb = is->active->u.generic;
b32 mouse_left_pressed = IsMouseButtonPressed(MOUSE_BUTTON_LEFT);
b32 mouse_right_pressed = IsMouseButtonPressed(MOUSE_BUTTON_RIGHT);
- f32 mouse_wheel = GetMouseWheelMoveV().y * is->active.scroll_scale;
+ f32 mouse_wheel = GetMouseWheelMoveV().y;
if (mouse_left_pressed) {
if (sb->zoom_starting_point.x == F32_INFINITY) {
@@ -904,7 +1210,7 @@ scale_bar_interaction(BeamformerCtx *ctx, v2 mouse)
/* TODO(rnp): SLL_* macros */
v2_sll *savepoint = ui->scale_bar_savepoint_freelist;
- if (!savepoint) savepoint = push_struct(&ui->arena_for_frame, v2_sll);
+ if (!savepoint) savepoint = push_struct(&ui->arena, v2_sll);
ui->scale_bar_savepoint_freelist = savepoint->next;
savepoint->v.x = *sb->min_value;
@@ -912,8 +1218,8 @@ scale_bar_interaction(BeamformerCtx *ctx, v2 mouse)
savepoint->next = sb->savepoint_stack;
sb->savepoint_stack = savepoint;
- *sb->min_value = MAX(min, is->active.f32_limits.x);
- *sb->max_value = MIN(max, is->active.f32_limits.y);
+ *sb->min_value = MAX(min, sb->limits.x);
+ *sb->max_value = MIN(max, sb->limits.y);
sb->zoom_starting_point = (v2){.x = F32_INFINITY, .y = F32_INFINITY};
ui->flush_params = 1;
@@ -936,11 +1242,10 @@ scale_bar_interaction(BeamformerCtx *ctx, v2 mouse)
}
if (mouse_wheel) {
- v2 limits = is->active.f32_limits;
- *sb->min_value -= mouse_wheel * sb->scroll_both;
- *sb->max_value += mouse_wheel;
- *sb->min_value = MAX(limits.x, *sb->min_value);
- *sb->max_value = MIN(limits.y, *sb->max_value);
+ *sb->min_value += mouse_wheel * sb->scroll_scale.x;
+ *sb->max_value += mouse_wheel * sb->scroll_scale.y;
+ *sb->min_value = MAX(sb->limits.x, *sb->min_value);
+ *sb->max_value = MIN(sb->limits.y, *sb->max_value);
ui->flush_params = 1;
}
}
@@ -949,24 +1254,33 @@ static void
ui_begin_interact(BeamformerUI *ui, BeamformerInput *input, b32 scroll, b32 mouse_left_pressed)
{
InteractionState *is = &ui->interaction;
- if (is->hot_state != IS_NONE) {
- is->state = is->hot_state;
- } else {
- switch (is->hot.type) {
- case VT_NULL: is->state = IS_NOP; break;
- case VT_B32: is->state = IS_SET; break;
- case VT_GROUP: is->state = IS_SET; break;
+ if (is->hot_type != IT_NONE) {
+ is->type = is->hot_type;
+ } else if (is->hot) {
+ switch (is->hot->type) {
+ case VT_NULL: is->type = IT_NOP; break;
+ case VT_B32: is->type = IT_SET; break;
+ case VT_GROUP: is->type = IT_SET; break;
+ case VT_BEAMFORMER_VARIABLE: {
+ BeamformerVariable *bv = (BeamformerVariable *)is->hot;
+ if (bv->subtype == VT_B32) {
+ is->type = IT_SET;
+ break;
+ }
+ } /* FALLTHROUGH */
case VT_F32: {
if (scroll) {
- is->state = IS_SCROLL;
+ is->type = IT_SCROLL;
} else if (mouse_left_pressed) {
- is->state = IS_TEXT;
- begin_text_input(&ui->text_input_state, &is->hot);
+ /* TODO(rnp): not all F32 variables should be text input */
+ is->type = IT_TEXT;
+ begin_text_input(&ui->text_input_state, is->hot, input->mouse);
}
} break;
+ default: INVALID_CODE_PATH;
}
}
- if (is->state != IS_NONE) {
+ if (is->type != IT_NONE) {
is->active = is->hot;
}
}
@@ -976,50 +1290,43 @@ ui_end_interact(BeamformerCtx *ctx, v2 mouse)
{
BeamformerUI *ui = ctx->ui;
InteractionState *is = &ui->interaction;
- switch (is->state) {
- case IS_NONE: break;
- case IS_NOP: break;
- case IS_SET: {
- switch (is->active.type) {
- case VT_B32: {
- b32 *val = is->active.store;
- *val = !(*val);
+ switch (is->type) {
+ case IT_NONE: break;
+ case IT_NOP: break;
+ case IT_SET: {
+ switch (is->active->type) {
+ case VT_GROUP: {
+ is->active->u.group.expanded = !is->active->u.group.expanded;
} break;
- }
- } break;
- case IS_DISPLAY: display_interaction_end(ui); /* FALLTHROUGH */
- case IS_SCROLL: {
- f32 delta = GetMouseWheelMoveV().y * is->active.scroll_scale;
- switch (is->active.type) {
case VT_B32: {
- b32 *old_val = is->active.store;
- b32 new_val = !(*old_val);
- ui_store_variable(&is->active, &new_val);
- } break;
- case VT_F32: {
- f32 *old_val = is->active.store;
- f32 new_val = *old_val + delta;
- ui_store_variable(&is->active, &new_val);
+ is->active->u.b32 = !is->active->u.b32;
} break;
- case VT_I32: {
- i32 *old_val = is->active.store;
- i32 new_val = *old_val + delta;
- ui_store_variable(&is->active, &new_val);
+ case VT_BEAMFORMER_VARIABLE: {
+ BeamformerVariable *bv = (BeamformerVariable *)is->active;
+ ASSERT(bv->subtype == VT_B32);
+ b32 *val = is->active->u.generic;
+ *val = !(*val);
} break;
+ default: INVALID_CODE_PATH;
}
} break;
- case IS_SCALE_BAR: break;
- case IS_TEXT: end_text_input(&ui->text_input_state, &is->active); break;
+ case IT_DISPLAY: display_interaction_end(ui); /* FALLTHROUGH */
+ case IT_SCROLL: scroll_interaction(is->active, GetMouseWheelMoveV().y); break;
+ case IT_TEXT: end_text_input(&ui->text_input_state, is->active); break;
+ case IT_SCALE_BAR: break;
+ default: INVALID_CODE_PATH;
}
- if (is->active.flags & V_CAUSES_COMPUTE)
- ui->flush_params = 1;
-
- if (is->active.flags & V_GEN_MIPMAPS)
- ui_gen_mipmaps(ctx);
+ if (is->active->type == VT_BEAMFORMER_VARIABLE) {
+ BeamformerVariable *bv = (BeamformerVariable *)is->active;
+ if (bv->flags & V_CAUSES_COMPUTE)
+ ui->flush_params = 1;
+ if (bv->flags & V_GEN_MIPMAPS && ctx->fsctx.output.texture.id)
+ ctx->fsctx.gen_mipmaps = 1;
+ }
- is->state = IS_NONE;
- is->active = NULL_VARIABLE;
+ is->type = IT_NONE;
+ is->active = 0;
}
static void
@@ -1031,32 +1338,35 @@ ui_interact(BeamformerCtx *ctx, BeamformerInput *input)
b32 mouse_right_pressed = IsMouseButtonPressed(MOUSE_BUTTON_RIGHT);
b32 wheel_moved = GetMouseWheelMoveV().y != 0;
if (mouse_right_pressed || mouse_left_pressed || wheel_moved) {
- if (is->state != IS_NONE)
+ if (is->type != IT_NONE)
ui_end_interact(ctx, input->mouse);
ui_begin_interact(ui, input, wheel_moved, mouse_left_pressed);
}
- if (IsKeyPressed(KEY_ENTER) && is->state == IS_TEXT)
+ if (IsKeyPressed(KEY_ENTER) && is->type == IT_TEXT)
ui_end_interact(ctx, input->mouse);
- switch (is->state) {
- case IS_DISPLAY: display_interaction(ui, input->mouse); break;
- case IS_SCROLL: ui_end_interact(ctx, input->mouse); break;
- case IS_SET: ui_end_interact(ctx, input->mouse); break;
- case IS_TEXT: update_text_input(&ui->text_input_state); break;
- case IS_DRAG: {
+ switch (is->type) {
+ case IT_NONE: break;
+ case IT_NOP: break;
+ case IT_DISPLAY: display_interaction(ui, input->mouse); break;
+ case IT_SCROLL: ui_end_interact(ctx, input->mouse); break;
+ case IT_SET: ui_end_interact(ctx, input->mouse); break;
+ case IT_TEXT: update_text_input(&ui->text_input_state); break;
+ case IT_DRAG: {
if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) {
ui_end_interact(ctx, input->mouse);
} else {
- switch (is->active.type) {
+ switch (is->active->type) {
+ default: break;
}
}
} break;
- case IS_SCALE_BAR: scale_bar_interaction(ctx, input->mouse); break;
+ case IT_SCALE_BAR: scale_bar_interaction(ctx, input->mouse); break;
}
- is->hot_state = IS_NONE;
- is->hot = NULL_VARIABLE;
+ is->hot_type = IT_NONE;
+ is->hot = 0;
}
static void
@@ -1070,61 +1380,83 @@ ui_init(BeamformerCtx *ctx, Arena store)
* it to actually draw the ui. If we reload we may have changed it so we should
* rebuild it */
+ BeamformerUI *ui = ctx->ui;
+
/* NOTE: unload old fonts from the GPU */
- if (ctx->ui) {
- UnloadFont(ctx->ui->font);
- UnloadFont(ctx->ui->small_font);
+ if (ui) {
+ UnloadFont(ui->font);
+ UnloadFont(ui->small_font);
}
- BeamformerUI *ui = ctx->ui = alloc(&store, typeof(*ctx->ui), 1);
- ui->arena_for_frame = store;
- ui->frame_temporary_arena = begin_temp_arena(&ui->arena_for_frame);
+ ui = ctx->ui = push_struct(&store, typeof(*ui));
+ ui->arena = store;
+ ui->frame_temporary_arena = begin_temp_arena(&ui->arena);
/* TODO: build these into the binary */
ui->font = LoadFontEx("assets/IBMPlexSans-Bold.ttf", 28, 0, 0);
ui->small_font = LoadFontEx("assets/IBMPlexSans-Bold.ttf", 22, 0, 0);
- /* TODO: multiple views */
- ui->scale_bars[0][SB_LATERAL].min_value = &ui->params.output_min_coordinate.x;
- ui->scale_bars[0][SB_LATERAL].max_value = &ui->params.output_max_coordinate.x;
- ui->scale_bars[0][SB_AXIAL].min_value = &ui->params.output_min_coordinate.z;
- ui->scale_bars[0][SB_AXIAL].max_value = &ui->params.output_max_coordinate.z;
+ beamformer_parameters_view_init(ctx);
+
+ /* NOTE(rnp): by default we always have at least one view */
+ Variable *var = add_variable(&ui->beamform_views, &ui->arena, s8("Beamformed Views"), VT_BEAMFORMER_VIEW, ui->font);
+ BeamformerFrameView *bv = var->u.generic = beamformer_frame_view_new(ui, FVT_LATEST);
+ bv->lateral_scale_bar.min_value = &ui->params.output_min_coordinate.x;
+ bv->lateral_scale_bar.max_value = &ui->params.output_max_coordinate.x;
+ bv->axial_scale_bar.min_value = &ui->params.output_min_coordinate.z;
+ bv->axial_scale_bar.max_value = &ui->params.output_max_coordinate.z;
- ui->scale_bars[0][SB_LATERAL].scroll_both = 1;
- ui->scale_bars[0][SB_AXIAL].scroll_both = 0;
+ bv->lateral_scale_bar.limits = (v2){.x = -1, .y = 1};
+ bv->axial_scale_bar.limits = (v2){.x = 0, .y = 1};
- ui->scale_bars[0][SB_LATERAL].zoom_starting_point = (v2){.x = F32_INFINITY, .y = F32_INFINITY};
- ui->scale_bars[0][SB_AXIAL].zoom_starting_point = (v2){.x = F32_INFINITY, .y = F32_INFINITY};
+ bv->lateral_scale_bar.scroll_scale = (v2){.x = -0.5e-3, .y = 0.5e-3};
+ bv->axial_scale_bar.scroll_scale = (v2){.x = 0, .y = 1e-3};
- ui->read_params = 1;
+ bv->lateral_scale_bar.zoom_starting_point = (v2){.x = F32_INFINITY, .y = F32_INFINITY};
+ bv->axial_scale_bar.zoom_starting_point = (v2){.x = F32_INFINITY, .y = F32_INFINITY};
+
+ ctx->ui_read_params = 1;
+}
+
+static void
+validate_ui_parameters(BeamformerUIParameters *p)
+{
+ if (p->output_min_coordinate.x > p->output_max_coordinate.x)
+ SWAP(p->output_min_coordinate.x, p->output_max_coordinate.x)
+ if (p->output_min_coordinate.z > p->output_max_coordinate.z)
+ SWAP(p->output_min_coordinate.z, p->output_max_coordinate.z)
}
static void
draw_ui(BeamformerCtx *ctx, BeamformerInput *input, BeamformFrame *frame_to_draw)
{
BeamformerUI *ui = ctx->ui;
+ ui->latest_frame = frame_to_draw;
/* TODO(rnp): we need an ALLOC_END flag so that we can have permanent storage
* or we need a sub arena for the save point stack */
//end_temp_arena(ui->frame_temporary_arena);
- //ui->frame_temporary_arena = begin_temp_arena(&ui->arena_for_frame);
+ //ui->frame_temporary_arena = begin_temp_arena(&ui->arena);
/* TODO(rnp): there should be a better way of detecting this */
- if (ui->read_params) {
+ if (ctx->ui_read_params) {
mem_copy(&ctx->params->raw.output_min_coordinate, &ui->params, sizeof(ui->params));
- ui->flush_params = 0;
- ui->read_params = 0;
+ ui->flush_params = 0;
+ ctx->ui_read_params = 0;
}
/* NOTE: process interactions first because the user interacted with
* the ui that was presented last frame */
ui_interact(ctx, input);
- if (ui->flush_params && !ctx->csctx.processing_compute) {
- mem_copy(&ui->params, &ctx->params->raw.output_min_coordinate, sizeof(ui->params));
- ui->flush_params = 0;
- ctx->params->upload = 1;
- ctx->start_compute = 1;
+ if (ui->flush_params) {
+ validate_ui_parameters(&ui->params);
+ if (!ctx->csctx.processing_compute) {
+ mem_copy(&ui->params, &ctx->params->raw.output_min_coordinate, sizeof(ui->params));
+ ui->flush_params = 0;
+ ctx->params->upload = 1;
+ ctx->start_compute = 1;
+ }
}
BeginDrawing();
@@ -1137,9 +1469,20 @@ draw_ui(BeamformerCtx *ctx, BeamformerInput *input, BeamformFrame *frame_to_draw
rr.size.w = wr.size.w - lr.size.w;
rr.pos.x = lr.pos.x + lr.size.w;
- draw_settings_ui(ctx, lr, mouse);
- if (frame_to_draw->ready_to_present)
- draw_display_overlay(ctx, ui->arena_for_frame, mouse, rr, frame_to_draw);
- draw_debug_overlay(ctx, frame_to_draw, ui->arena_for_frame, lr);
+ {
+ Rect draw_r = lr;
+ draw_r.pos.y += 20;
+ draw_r.size.y -= 20;
+ draw_variable_list(ui, ui->parameters_view, draw_r, mouse);
+ }
+ if (ui->latest_frame->ready_to_present) {
+ BeamformerFrameView *view = ui->beamform_views.u.group.first->u.generic;
+ view->display_rect = rr;
+ draw_beamform_view(ctx, ui->arena, mouse, view);
+ }
+ draw_debug_overlay(ctx, frame_to_draw, ui->arena, lr);
+
+ if (ui->interaction.type == IT_TEXT)
+ draw_input_cursor(ui, mouse);
EndDrawing();
}
diff --git a/util.c b/util.c
@@ -310,22 +310,6 @@ stream_append_v2(Stream *s, v2 v)
stream_append_byte(s, '}');
}
-static void
-stream_append_variable(Stream *s, Variable *var)
-{
- switch (var->type) {
- case VT_F32: {
- f32 *f32_val = var->store;
- stream_append_f64(s, *f32_val * var->display_scale, 100);
- } break;
- case VT_I32: {
- i32 *i32_val = var->store;
- stream_append_i64(s, *i32_val * var->display_scale);
- } break;
- default: INVALID_CODE_PATH;
- }
-}
-
/* NOTE(rnp): FNV-1a hash */
static u64
s8_hash(s8 v)
diff --git a/util.h b/util.h
@@ -53,6 +53,7 @@
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define ORONE(x) ((x)? (x) : 1)
#define SIGN(x) ((x) < 0? -1 : 1)
+#define SWAP(a, b) {typeof(a) __tmp = (a); (a) = (b); (b) = __tmp;}
#define KB(a) ((a) << 10ULL)
#define MB(a) ((a) << 20ULL)
@@ -175,41 +176,6 @@ typedef struct {
b32 errors;
} Stream;
-enum variable_type {
- VT_NULL,
- VT_B32,
- VT_F32,
- VT_I32,
- VT_GROUP,
-};
-
-enum variable_group_type {
- VG_LISTING,
- VG_V2,
- VG_V4,
- VG_UV4,
-};
-
-typedef struct {
- void *store;
- union {
- v2 f32_limits;
- iv2 i32_limits;
- };
- f32 display_scale;
- f32 scroll_scale;
- u32 type;
- u32 flags;
-} Variable;
-
-typedef struct {
- Variable *first;
- Variable *last;
- u32 type;
-} VariableGroup;
-
-#define NULL_VARIABLE (Variable){.store = 0, .type = VT_NULL}
-
typedef struct Platform Platform;
typedef struct {