summaryrefslogtreecommitdiff
path: root/src/screen/view.c
blob: 81d19a9d2be95087812551acc53daf2fa5d681d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include <al/lib.h>
#include <al/math.h>

#include "view.h"

#define ZOOM_MAX 100.0
#define ZOOM_MIN 0.02

#define PAN_BOUNDS 35.0

#define X_IN_BOUNDS(view, w, x) \
    ((x + PAN_BOUNDS <= w) && (PAN_BOUNDS - x <= view->width * view->zoom / view->stretch))
#define Y_IN_BOUNDS(view, h, y) \
    ((y + PAN_BOUNDS <= h) && (PAN_BOUNDS - y <= view->height * view->zoom * view->stretch))

#define FOV_MIN AL_TO_RADIANS(10.0)
#define FOV_MAX AL_TO_RADIANS(160.0)

#define VIEW_ON_ITS_SIDE(view) \
    (view->rotation % CAMU_VIEW_ROTATION_180 == CAMU_VIEW_ROTATION_90)

void camu_view_init(struct camu_view *view, u32 width, u32 height)
{
    view->mode = CAMU_DEFAULT_VIEW;
    view->rotation = CAMU_VIEW_ROTATION_0;
    view->width = width;
    view->height = height;
    view->zindex = 0;
    view->stretch = 1.0;
    view->fov = AL_TO_RADIANS(CAMU_DEFAULT_FOV);
}

void camu_view_calculate(struct camu_view *view, u32 window_width, u32 window_height)
{
    u32 width, height;
    if (VIEW_ON_ITS_SIDE(view)) {
        width = view->height;
        height = view->width;
        view->stretch = height / (f64)width;
    } else {
        width = view->width;
        height = view->height;
        view->stretch = 1.0;
    }
    f64 frame_ratio = width / (f64)height;
    f64 ratio = window_width / (f64)window_height;
    switch (view->mode) {
    case CAMU_VIEW_AUTOFIT:
        if (frame_ratio > ratio) {
            view->zoom = window_width / (f64)width;
        } else {
            view->zoom = window_height / (f64)height;
        }
        break;
    case CAMU_VIEW_ZOOM:
        if (frame_ratio < ratio) {
            view->zoom = window_width / (f64)width;
        } else {
            view->zoom = window_height / (f64)height;
        }
        break;
    default:
        return;
    }
    view->x_offset = (window_width - (width * view->zoom)) / 2.0;
    view->y_offset = (window_height - (height * view->zoom)) / 2.0;
}

bool camu_view_pan_simple(struct camu_view *view, u32 window_width, u32 window_height, f64 dx, f64 dy)
{
    f64 nx = view->x_offset + dx;
    f64 ny = view->y_offset + dy;
    bool view_changed = false;
    if (X_IN_BOUNDS(view, window_width, nx)) {
        view->x_offset = nx;
        view_changed = true;
    }
    if (Y_IN_BOUNDS(view, window_height, ny)) {
        view->y_offset = ny;
        view_changed = true;
    }
    return view_changed;
}

bool camu_view_zoom_simple(struct camu_view *view, u32 window_width, u32 window_height, f64 x, f64 y, f64 v)
{
    x -= view->x_offset;
    y -= view->y_offset;
    x /= view->zoom;
    y /= view->zoom;

    f64 prev_width = window_width * view->zoom;
    f64 prev_height = window_height * view->zoom;

    f64 x_ratio = x / (f64)window_width;
    f64 y_ratio = y / (f64)window_height;

    f64 zoom = view->zoom + (v * view->zoom);
    if (zoom > ZOOM_MAX || zoom < ZOOM_MIN) return false;

    x = view->x_offset - x_ratio * ((window_width * zoom) - prev_width);
    y = view->y_offset - y_ratio * ((window_height * zoom) - prev_height);
    if (!(X_IN_BOUNDS(view, window_width, x) && Y_IN_BOUNDS(view, window_height, y))) {
        return false;
    }

    view->x_offset = x;
    view->y_offset = y;
    view->zoom = zoom;

    return true;
}

bool camu_view_zoom_fov(struct camu_view *view, f64 v)
{
    f64 fov = view->fov - v;
    if (fov > FOV_MAX || fov < FOV_MIN) return false;
    view->fov = fov;
    return true;
}