Commit: 21f505a9556eb08be2e047f8bd2853143eefba5f
Parent: d0ef60ead1f1e3ab3163a433894e7193b12bff36
Author: Randy Palamar
Date: Fri, 10 Jan 2025 08:35:07 -0700
ui: scale bar click to zoom interaction
Diffstat:
M | beamformer.h | | | 15 | ++++++++------- |
M | ui.c | | | 122 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------ |
M | util.c | | | 3 | ++- |
3 files changed, 96 insertions(+), 44 deletions(-)
diff --git a/beamformer.h b/beamformer.h
@@ -90,17 +90,18 @@ typedef struct {
u32 state;
} InteractionState;
-typedef struct v2_SLL {
- struct v2_SLL *next;
+typedef struct v2_sll {
+ struct v2_sll *next;
v2 v;
-} v2_SLL;
+} v2_sll;
typedef struct {
f32 *min_value, *max_value;
- v2_SLL *save_point_stack;
- f32 zoom_starting_point;
+ v2_sll *savepoint_stack;
+ v2 zoom_starting_point;
+ v2 screen_offset;
+ v2 screen_space_to_value;
f32 hover_t;
- f32 pixels_to_value;
b32 scroll_both;
} ScaleBar;
@@ -117,7 +118,7 @@ typedef struct {
InputState text_input_state;
ScaleBar scale_bars[MAX_DISPLAYS][2];
- v2_SLL *scale_bar_savepoint_freelist;
+ v2_sll *scale_bar_savepoint_freelist;
v2 ruler_start_p;
v2 ruler_stop_p;
diff --git a/ui.c b/ui.c
@@ -115,8 +115,8 @@ hover_text(v2 mouse, Rect text_rect, f32 *hover_t, b32 can_advance)
* an orientation rather than force CCW/right-handed */
static void
draw_ruler(BeamformerUI *ui, Stream *buf, v2 start_point, v2 end_point,
- f32 start_value, f32 end_value, u32 segments, s8 suffix,
- Color ruler_colour, Color txt_colour)
+ f32 start_value, f32 end_value, f32 *markers, u32 marker_count,
+ u32 segments, s8 suffix, Color ruler_colour, Color txt_colour)
{
b32 draw_plus = SIGN(start_value) != SIGN(end_value);
@@ -147,6 +147,16 @@ draw_ruler(BeamformerUI *ui, Stream *buf, v2 start_point, v2 end_point,
ep.x += inc;
tp.x += inc;
}
+
+ ep.y += RULER_TICK_LENGTH;
+ for (u32 i = 0; i < marker_count; i++) {
+ if (markers[i] < F32_INFINITY) {
+ ep.x = sp.x = markers[i];
+ DrawLineEx(sp.rl, ep.rl, 3, colour_from_normalized(RULER_COLOUR));
+ DrawCircleV(ep.rl, 3, colour_from_normalized(RULER_COLOUR));
+ }
+ }
+
rlPopMatrix();
}
@@ -162,24 +172,39 @@ do_scale_bar(BeamformerUI *ui, Stream *buf, Variable var, v2 mouse, i32 directio
Rect tick_rect = draw_rect;
v2 start_pos = tick_rect.pos;
v2 end_pos = tick_rect.pos;
+ v2 relative_mouse = sub_v2(mouse, tick_rect.pos);
+
+ f32 markers[2];
+ u32 marker_count = 1;
+
u32 tick_count;
if (direction == SB_AXIAL) {
tick_rect.size.x = RULER_TEXT_PAD + RULER_TICK_LENGTH + txt_s.x;
tick_count = tick_rect.size.y / (1.5 * ui->small_font_height);
start_pos.y += tick_rect.size.y;
+ markers[0] = tick_rect.size.y - sb->zoom_starting_point.y;
+ markers[1] = tick_rect.size.y - relative_mouse.y;
+ sb->screen_offset = (v2){.y = tick_rect.pos.y};
+ sb->screen_space_to_value = (v2){.y = (*sb->max_value - *sb->min_value) / tick_rect.size.y};
} else {
tick_rect.size.y = RULER_TEXT_PAD + RULER_TICK_LENGTH + txt_s.x;
tick_count = tick_rect.size.x / (1.5 * ui->small_font_height);
end_pos.x += tick_rect.size.x;
+ markers[0] = sb->zoom_starting_point.x;
+ markers[1] = relative_mouse.x;
+ /* TODO(rnp): screen space to value space transformation helper */
+ sb->screen_offset = (v2){.x = tick_rect.pos.x};
+ sb->screen_space_to_value = (v2){.x = (*sb->max_value - *sb->min_value) / tick_rect.size.x};
}
if (hover_text(mouse, tick_rect, &sb->hover_t, 1)) {
is->hot_state = IS_SCALE_BAR;
is->hot = var;
+ marker_count = 2;
}
- draw_ruler(ui, buf, start_pos, end_pos, start_value, end_value, tick_count, suffix,
- colour_from_normalized(FG_COLOUR),
+ draw_ruler(ui, buf, start_pos, end_pos, start_value, end_value, markers, marker_count,
+ tick_count, suffix, colour_from_normalized(FG_COLOUR),
colour_from_normalized(lerp_v4(FG_COLOUR, HOVERED_COLOUR, sb->hover_t)));
}
@@ -226,7 +251,7 @@ draw_display_overlay(BeamformerCtx *ctx, Arena a, v2 mouse, Rect display_rect)
NPatchInfo tex_np = { tex_r, 0, 0, 0, 0, NPATCH_NINE_PATCH };
DrawTextureNPatch(*output, tex_np, vr.rl, (Vector2){0}, 0, WHITE);
- Variable var = {.flags = V_CAUSES_COMPUTE};
+ 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;
@@ -859,44 +884,66 @@ display_interaction(BeamformerUI *ui, v2 mouse)
}
static void
-scale_bar_interaction_end(BeamformerUI *ui)
+scale_bar_interaction(BeamformerCtx *ctx, v2 mouse)
{
+ BeamformerUI *ui = ctx->ui;
InteractionState *is = &ui->interaction;
ScaleBar *sb = is->active.store;
- b32 is_hot = is->hot_state == IS_SCALE_BAR;
+ 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;
- /* TODO: this should only happen on right click */
- sb->zoom_starting_point = F32_INFINITY;
+ if (mouse_left_pressed) {
+ if (sb->zoom_starting_point.x == F32_INFINITY) {
+ sb->zoom_starting_point = sub_v2(mouse, sb->screen_offset);
+ } else {
+ v2 relative_mouse = sub_v2(mouse, sb->screen_offset);
+ f32 min = magnitude_v2(mul_v2(sb->zoom_starting_point, sb->screen_space_to_value));
+ f32 max = magnitude_v2(mul_v2(relative_mouse, sb->screen_space_to_value));
+ if (min > max) { f32 tmp = min; min = max; max = tmp; }
+
+ min += *sb->min_value;
+ max += *sb->min_value;
+
+ /* TODO(rnp): SLL_* macros */
+ v2_sll *savepoint = ui->scale_bar_savepoint_freelist;
+ if (!savepoint) savepoint = push_struct(&ui->arena_for_frame, v2_sll);
+ ui->scale_bar_savepoint_freelist = savepoint->next;
- /* TODO: if right click load saved state */
- /* TODO: if left click do nothing */
+ savepoint->v.x = *sb->min_value;
+ savepoint->v.y = *sb->max_value;
+ savepoint->next = sb->savepoint_stack;
+ sb->savepoint_stack = savepoint;
- if (is_hot && mouse_wheel) {
+ *sb->min_value = MAX(min, is->active.f32_limits.x);
+ *sb->max_value = MIN(max, is->active.f32_limits.y);
+
+ sb->zoom_starting_point = (v2){.x = F32_INFINITY, .y = F32_INFINITY};
+ ui_start_compute(ctx);
+ }
+ }
+
+ if (mouse_right_pressed) {
+ v2_sll *savepoint = sb->savepoint_stack;
+ if (savepoint) {
+ *sb->min_value = savepoint->v.x;
+ *sb->max_value = savepoint->v.y;
+ ui_start_compute(ctx);
+
+ sb->savepoint_stack = savepoint->next;
+ savepoint->next = ui->scale_bar_savepoint_freelist;
+ ui->scale_bar_savepoint_freelist = savepoint;
+ }
+ sb->zoom_starting_point = (v2){.x = F32_INFINITY, .y = F32_INFINITY};
+ }
+
+ 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);
- }
-}
-
-static void
-scale_bar_interaction(BeamformerCtx *ctx, v2 mouse)
-{
- BeamformerUI *ui = ctx->ui;
- InteractionState *is = &ui->interaction;
- ScaleBar *sb = is->active.store;
- b32 is_hot = is->hot_state == IS_SCALE_BAR;
- b32 mouse_left_pressed = IsMouseButtonPressed(MOUSE_BUTTON_LEFT);
-
- /* TODO: convert mouse to coord_space */
- if (is_hot && mouse_left_pressed) {
- if (sb->zoom_starting_point == F32_INFINITY) {
- /* TODO: push new starting point */
- } else {
- /* TODO: push new savepoint and set values */
- }
+ ui_start_compute(ctx);
}
}
@@ -963,7 +1010,7 @@ ui_end_interact(BeamformerCtx *ctx, v2 mouse)
} break;
}
} break;
- case IS_SCALE_BAR: scale_bar_interaction_end(ui); break;
+ case IS_SCALE_BAR: break;
case IS_TEXT: end_text_input(&ui->text_input_state, &is->active); break;
}
@@ -1051,8 +1098,9 @@ ui_init(BeamformerCtx *ctx, Arena store)
ui->scale_bars[0][SB_LATERAL].scroll_both = 1;
ui->scale_bars[0][SB_AXIAL].scroll_both = 0;
- ui->scale_bars[0][SB_LATERAL].zoom_starting_point = F32_INFINITY;
- ui->scale_bars[0][SB_AXIAL].zoom_starting_point = F32_INFINITY;
+ 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};
+
}
static void
@@ -1060,8 +1108,10 @@ draw_ui(BeamformerCtx *ctx, BeamformerInput *input, b32 draw_display)
{
BeamformerUI *ui = ctx->ui;
- end_temp_arena(ui->frame_temporary_arena);
- ui->frame_temporary_arena = begin_temp_arena(&ui->arena_for_frame);
+ /* 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);
/* NOTE: process interactions first because the user interacted with
* the ui that was presented last frame */
diff --git a/util.c b/util.c
@@ -37,7 +37,8 @@ mem_move(u8 *src, u8 *dest, size n)
else while (n) { n--; dest[n] = src[n]; }
}
-#define alloc(a, t, n) (t *)alloc_(a, sizeof(t), _Alignof(t), n)
+#define alloc(a, t, n) (t *)alloc_(a, sizeof(t), _Alignof(t), n)
+#define push_struct(a, t) (t *)alloc_(a, sizeof(t), _Alignof(t), 1)
static void *
alloc_(Arena *a, size len, size align, size count)
{