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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
|
/**
* test_glamac_view.c - Unit tests for view management
*
* Copyright (C) 2025 https://optics-design.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*/
#include "../test_framework.h"
#include "../../include/glamac_view.h"
#include "../../include/glass_data.h"
// Test view initialization
int test_view_initialization() {
TEST_START("View Initialization");
ViewState view;
init_view_state(&view, 800, 600);
// Check initial values
TEST_ASSERT_FLOAT_EQ(0.0f, view.offsetX, 0.001f, "Initial X offset should be 0");
TEST_ASSERT_FLOAT_EQ(0.0f, view.offsetY, 0.001f, "Initial Y offset should be 0");
TEST_ASSERT_FLOAT_EQ(1.0f, view.zoomLevel, 0.001f, "Initial zoom should be 1.0");
TEST_ASSERT_EQ(800, view.windowWidth, "Window width should be set correctly");
TEST_ASSERT_EQ(600, view.windowHeight, "Window height should be set correctly");
TEST_END();
}
// Test coordinate transformations
int test_coordinate_transformations() {
TEST_START("Coordinate Transformations");
ViewState view;
init_view_state(&view, 800, 600);
// Initialize glass data for proper coordinate system
initialize_glass_data();
// Test glass to screen coordinate conversion
i32 screenX, screenY;
data_to_screen_coords(60.0f, 1.5f, &view, &screenX, &screenY);
// Screen coordinates should be within window bounds for typical glass values
TEST_ASSERT(screenX >= 0 && screenX <= view.windowWidth,
"Screen X coordinate should be within window bounds");
TEST_ASSERT(screenY >= 0 && screenY <= view.windowHeight,
"Screen Y coordinate should be within window bounds");
// Test screen to glass coordinate conversion (inverse operation)
f32 abbeNumber, refractiveIndex;
screen_to_data_coords(screenX, screenY, &view, &abbeNumber, &refractiveIndex);
// The inverse transformation should approximately recover original values
TEST_ASSERT_FLOAT_EQ(60.0f, abbeNumber, 1.0f,
"Inverse transformation should recover Abbe number");
TEST_ASSERT_FLOAT_EQ(1.5f, refractiveIndex, 0.01f,
"Inverse transformation should recover refractive index");
cleanup_glass_data();
TEST_END();
}
// Test zoom functionality
int test_zoom_functionality() {
TEST_START("Zoom Functionality");
ViewState view;
init_view_state(&view, 800, 600);
// Test zoom in
f32 initial_zoom = view.zoomLevel;
handle_mouse_wheel_zoom(1, 400, 300, &view); // Zoom in at center
TEST_ASSERT(view.zoomLevel > initial_zoom, "Zoom should increase");
TEST_ASSERT(view.zoomLevel <= MAX_ZOOM, "Zoom should not exceed maximum");
// Test zoom out
f32 zoomed_in = view.zoomLevel;
handle_mouse_wheel_zoom(-1, 400, 300, &view); // Zoom out at center
TEST_ASSERT(view.zoomLevel < zoomed_in, "Zoom should decrease");
TEST_ASSERT(view.zoomLevel >= MIN_ZOOM, "Zoom should not go below minimum");
// Test zoom limits
for (int i = 0; i < 20; i++) {
handle_mouse_wheel_zoom(1, 400, 300, &view); // Zoom in a lot
}
TEST_ASSERT_FLOAT_EQ(MAX_ZOOM, view.zoomLevel, 0.001f, "Should be clamped to max zoom");
for (int i = 0; i < 20; i++) {
handle_mouse_wheel_zoom(-1, 400, 300, &view); // Zoom out a lot
}
TEST_ASSERT_FLOAT_EQ(MIN_ZOOM, view.zoomLevel, 0.001f, "Should be clamped to min zoom");
TEST_END();
}
// Test panning functionality
int test_panning_functionality() {
TEST_START("Panning Functionality");
ViewState view;
init_view_state(&view, 800, 600);
// Test panning
f32 initial_x = view.offsetX;
f32 initial_y = view.offsetY;
pan_view(&view, 100, 50); // Pan right and up
TEST_ASSERT(view.offsetX != initial_x, "X offset should change with panning");
TEST_ASSERT(view.offsetY != initial_y, "Y offset should change with panning");
// Test panning in opposite direction
pan_view(&view, -100, -50); // Pan left and down
TEST_ASSERT_FLOAT_EQ(initial_x, view.offsetX, 0.1f,
"Should return to approximately initial X after opposite panning");
TEST_ASSERT_FLOAT_EQ(initial_y, view.offsetY, 0.1f,
"Should return to approximately initial Y after opposite panning");
TEST_END();
}
// Test view fitting
int test_view_fitting() {
TEST_START("View Fitting");
ViewState view;
init_view_state(&view, 800, 600);
// Initialize glass data for fitting
initialize_glass_data();
// Test fit to data
fit_view_to_data(&view);
// After fitting, the view should be adjusted to show all data
// The exact values depend on the glass data, but zoom and offsets should be reasonable
TEST_ASSERT(view.zoomLevel > 0.0f, "Zoom should be positive after fitting");
TEST_ASSERT(view.zoomLevel <= MAX_ZOOM, "Zoom should not exceed maximum after fitting");
// Offsets should be set to center the data
TEST_ASSERT(view.offsetX != 0.0f || view.offsetY != 0.0f,
"At least one offset should be non-zero after fitting (unless data is perfectly centered)");
cleanup_glass_data();
TEST_END();
}
// Test clustering functionality
int test_clustering() {
TEST_START("Clustering");
ViewState view;
init_view_state(&view, 800, 600);
// Initialize glass data for clustering
initialize_glass_data();
// Update clustering
update_clustering(&view);
// Check that clustering was performed
TEST_ASSERT(view.clusterCount >= 0, "Cluster count should be non-negative");
TEST_ASSERT(view.clusterCount <= MAX_CLUSTERS, "Cluster count should not exceed maximum");
// If we have clusters, test their properties
for (u32 i = 0; i < view.clusterCount; i++) {
const GlassCluster* cluster = &view.clusters[i];
TEST_ASSERT(cluster->count > 0, "Each cluster should have at least one glass");
TEST_ASSERT(cluster->count <= MAX_CLUSTER_SIZE, "Cluster size should not exceed maximum");
// Center coordinates should be reasonable
TEST_ASSERT(cluster->centerX >= 0 && cluster->centerX < view.windowWidth,
"Cluster center X should be within window bounds");
TEST_ASSERT(cluster->centerY >= 0 && cluster->centerY < view.windowHeight,
"Cluster center Y should be within window bounds");
}
cleanup_glass_data();
TEST_END();
}
// Test view reset
int test_view_reset() {
TEST_START("View Reset");
ViewState view;
init_view_state(&view, 800, 600);
// Modify the view
view.offsetX = 100.0f;
view.offsetY = 50.0f;
view.zoomLevel = 2.0f;
// Reset view
reset_view(&view);
// Check that view is back to initial state
TEST_ASSERT_FLOAT_EQ(0.0f, view.offsetX, 0.001f, "X offset should be reset to 0");
TEST_ASSERT_FLOAT_EQ(0.0f, view.offsetY, 0.001f, "Y offset should be reset to 0");
TEST_ASSERT_FLOAT_EQ(1.0f, view.zoomLevel, 0.001f, "Zoom should be reset to 1.0");
TEST_END();
}
// Test window resize handling
int test_window_resize() {
TEST_START("Window Resize");
ViewState view;
init_view_state(&view, 800, 600);
// Test resize
resize_view(&view, 1024, 768);
TEST_ASSERT_EQ(1024, view.windowWidth, "Window width should be updated");
TEST_ASSERT_EQ(768, view.windowHeight, "Window height should be updated");
// Test with zero dimensions (should be handled gracefully)
resize_view(&view, 0, 0);
TEST_ASSERT(view.windowWidth > 0, "Window width should remain positive");
TEST_ASSERT(view.windowHeight > 0, "Window height should remain positive");
TEST_END();
}
// Main test runner
int main() {
printf(BLUE "=== View Management Unit Tests ===" RESET "\n\n");
RUN_TEST(test_view_initialization);
RUN_TEST(test_coordinate_transformations);
RUN_TEST(test_zoom_functionality);
RUN_TEST(test_panning_functionality);
RUN_TEST(test_view_fitting);
RUN_TEST(test_clustering);
RUN_TEST(test_view_reset);
RUN_TEST(test_window_resize);
TEST_SUMMARY();
}
|