1 | #include <locale.h> |
2 | |
3 | #include <gtk/gtk.h> |
4 | #include "../../gtk/gtkconstrainttypesprivate.h" |
5 | #include "../../gtk/gtkconstraintsolverprivate.h" |
6 | #include "../../gtk/gtkconstraintexpressionprivate.h" |
7 | |
8 | static void |
9 | constraint_solver_simple (void) |
10 | { |
11 | GtkConstraintSolver *solver = gtk_constraint_solver_new (); |
12 | |
13 | GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, name: "x" , value: 167.0); |
14 | GtkConstraintVariable *y = gtk_constraint_solver_create_variable (solver, NULL, name: "y" , value: 2.0); |
15 | |
16 | GtkConstraintExpression *e = gtk_constraint_expression_new_from_variable (variable: y); |
17 | |
18 | gtk_constraint_solver_add_constraint (solver, |
19 | variable: x, relation: GTK_CONSTRAINT_RELATION_EQ, expression: e, |
20 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED); |
21 | |
22 | double x_value = gtk_constraint_variable_get_value (variable: x); |
23 | double y_value = gtk_constraint_variable_get_value (variable: y); |
24 | |
25 | g_assert_cmpfloat_with_epsilon (x_value, y_value, 0.001); |
26 | g_assert_cmpfloat_with_epsilon (x_value, 0.0, 0.001); |
27 | g_assert_cmpfloat_with_epsilon (y_value, 0.0, 0.001); |
28 | |
29 | gtk_constraint_variable_unref (variable: y); |
30 | gtk_constraint_variable_unref (variable: x); |
31 | |
32 | g_object_unref (object: solver); |
33 | } |
34 | |
35 | static void |
36 | constraint_solver_stay (void) |
37 | { |
38 | GtkConstraintSolver *solver = gtk_constraint_solver_new (); |
39 | |
40 | GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, name: "x" , value: 5.0); |
41 | GtkConstraintVariable *y = gtk_constraint_solver_create_variable (solver, NULL, name: "y" , value: 10.0); |
42 | |
43 | gtk_constraint_solver_add_stay_variable (solver, variable: x, strength: GTK_CONSTRAINT_STRENGTH_WEAK); |
44 | gtk_constraint_solver_add_stay_variable (solver, variable: y, strength: GTK_CONSTRAINT_STRENGTH_WEAK); |
45 | |
46 | double x_value = gtk_constraint_variable_get_value (variable: x); |
47 | double y_value = gtk_constraint_variable_get_value (variable: y); |
48 | |
49 | g_assert_cmpfloat_with_epsilon (x_value, 5.0, 0.001); |
50 | g_assert_cmpfloat_with_epsilon (y_value, 10.0, 0.001); |
51 | |
52 | gtk_constraint_variable_unref (variable: x); |
53 | gtk_constraint_variable_unref (variable: y); |
54 | |
55 | g_object_unref (object: solver); |
56 | } |
57 | |
58 | static void |
59 | constraint_solver_variable_geq_constant (void) |
60 | { |
61 | GtkConstraintSolver *solver = gtk_constraint_solver_new (); |
62 | |
63 | GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, name: "x" , value: 10.0); |
64 | GtkConstraintExpression *e = gtk_constraint_expression_new (constant: 100.0); |
65 | |
66 | gtk_constraint_solver_add_constraint (solver, |
67 | variable: x, relation: GTK_CONSTRAINT_RELATION_GE, expression: e, |
68 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED); |
69 | |
70 | double x_value = gtk_constraint_variable_get_value (variable: x); |
71 | |
72 | g_assert_cmpfloat (x_value, >=, 100.0); |
73 | |
74 | gtk_constraint_variable_unref (variable: x); |
75 | |
76 | g_object_unref (object: solver); |
77 | } |
78 | |
79 | static void |
80 | constraint_solver_variable_leq_constant (void) |
81 | { |
82 | GtkConstraintSolver *solver = gtk_constraint_solver_new (); |
83 | |
84 | GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, name: "x" , value: 100.0); |
85 | GtkConstraintExpression *e = gtk_constraint_expression_new (constant: 10.0); |
86 | |
87 | gtk_constraint_solver_add_constraint (solver, |
88 | variable: x, relation: GTK_CONSTRAINT_RELATION_LE, expression: e, |
89 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED); |
90 | |
91 | double x_value = gtk_constraint_variable_get_value (variable: x); |
92 | |
93 | g_assert_cmpfloat (x_value, <=, 10.0); |
94 | |
95 | gtk_constraint_variable_unref (variable: x); |
96 | |
97 | g_object_unref (object: solver); |
98 | } |
99 | |
100 | static void |
101 | constraint_solver_variable_eq_constant (void) |
102 | { |
103 | GtkConstraintSolver *solver = gtk_constraint_solver_new (); |
104 | |
105 | GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, name: "x" , value: 10.0); |
106 | GtkConstraintExpression *e = gtk_constraint_expression_new (constant: 100.0); |
107 | |
108 | gtk_constraint_solver_add_constraint (solver, |
109 | variable: x, relation: GTK_CONSTRAINT_RELATION_EQ, expression: e, |
110 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED); |
111 | |
112 | double x_value = gtk_constraint_variable_get_value (variable: x); |
113 | |
114 | g_assert_cmpfloat_with_epsilon (x_value, 100.0, 0.001); |
115 | |
116 | gtk_constraint_variable_unref (variable: x); |
117 | |
118 | g_object_unref (object: solver); |
119 | } |
120 | |
121 | static void |
122 | constraint_solver_eq_with_stay (void) |
123 | { |
124 | GtkConstraintSolver *solver = gtk_constraint_solver_new (); |
125 | |
126 | GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, name: "x" , value: 10.0); |
127 | GtkConstraintVariable *width = gtk_constraint_solver_create_variable (solver, NULL, name: "width" , value: 10.0); |
128 | GtkConstraintVariable *right_min = gtk_constraint_solver_create_variable (solver, NULL, name: "rightMin" , value: 100.0); |
129 | |
130 | GtkConstraintExpressionBuilder builder; |
131 | gtk_constraint_expression_builder_init (builder: &builder, solver); |
132 | gtk_constraint_expression_builder_term (builder: &builder, term: x); |
133 | gtk_constraint_expression_builder_plus (builder: &builder); |
134 | gtk_constraint_expression_builder_term (builder: &builder, term: width); |
135 | GtkConstraintExpression *right = gtk_constraint_expression_builder_finish (builder: &builder); |
136 | |
137 | gtk_constraint_solver_add_stay_variable (solver, variable: width, strength: GTK_CONSTRAINT_STRENGTH_WEAK); |
138 | gtk_constraint_solver_add_stay_variable (solver, variable: right_min, strength: GTK_CONSTRAINT_STRENGTH_WEAK); |
139 | gtk_constraint_solver_add_constraint (solver, |
140 | variable: right_min, relation: GTK_CONSTRAINT_RELATION_EQ, expression: right, |
141 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED); |
142 | |
143 | double x_value = gtk_constraint_variable_get_value (variable: x); |
144 | double width_value = gtk_constraint_variable_get_value (variable: width); |
145 | |
146 | g_assert_cmpfloat_with_epsilon (x_value, 90.0, 0.001); |
147 | g_assert_cmpfloat_with_epsilon (width_value, 10.0, 0.001); |
148 | |
149 | gtk_constraint_variable_unref (variable: right_min); |
150 | gtk_constraint_variable_unref (variable: width); |
151 | gtk_constraint_variable_unref (variable: x); |
152 | |
153 | g_object_unref (object: solver); |
154 | } |
155 | |
156 | static void |
157 | constraint_solver_cassowary (void) |
158 | { |
159 | GtkConstraintSolver *solver = gtk_constraint_solver_new (); |
160 | |
161 | GtkConstraintVariable *x = gtk_constraint_solver_create_variable (solver, NULL, name: "x" , value: 0.0); |
162 | GtkConstraintVariable *y = gtk_constraint_solver_create_variable (solver, NULL, name: "y" , value: 0.0); |
163 | |
164 | GtkConstraintExpression *e; |
165 | |
166 | e = gtk_constraint_expression_new_from_variable (variable: y); |
167 | gtk_constraint_solver_add_constraint (solver, |
168 | variable: x, relation: GTK_CONSTRAINT_RELATION_LE, expression: e, |
169 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED); |
170 | |
171 | e = gtk_constraint_expression_plus_constant (expression: gtk_constraint_expression_new_from_variable (variable: x), constant: 3.0); |
172 | gtk_constraint_solver_add_constraint (solver, |
173 | variable: y, relation: GTK_CONSTRAINT_RELATION_EQ, expression: e, |
174 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED); |
175 | |
176 | e = gtk_constraint_expression_new (constant: 10.0); |
177 | gtk_constraint_solver_add_constraint (solver, |
178 | variable: x, relation: GTK_CONSTRAINT_RELATION_EQ, expression: e, |
179 | strength: GTK_CONSTRAINT_STRENGTH_WEAK); |
180 | |
181 | e = gtk_constraint_expression_new (constant: 10.0); |
182 | gtk_constraint_solver_add_constraint (solver, |
183 | variable: y, relation: GTK_CONSTRAINT_RELATION_EQ, expression: e, |
184 | strength: GTK_CONSTRAINT_STRENGTH_WEAK); |
185 | |
186 | double x_val = gtk_constraint_variable_get_value (variable: x); |
187 | double y_val = gtk_constraint_variable_get_value (variable: y); |
188 | |
189 | g_test_message (format: "x = %g, y = %g" , x_val, y_val); |
190 | |
191 | /* The system is unstable and has two possible solutions we need to test */ |
192 | g_assert_true ((G_APPROX_VALUE (x_val, 10.0, 1e-8) && |
193 | G_APPROX_VALUE (y_val, 13.0, 1e-8)) || |
194 | (G_APPROX_VALUE (x_val, 7.0, 1e-8) && |
195 | G_APPROX_VALUE (y_val, 10.0, 1e-8))); |
196 | |
197 | gtk_constraint_variable_unref (variable: x); |
198 | gtk_constraint_variable_unref (variable: y); |
199 | |
200 | g_object_unref (object: solver); |
201 | } |
202 | |
203 | static void |
204 | constraint_solver_edit_var_required (void) |
205 | { |
206 | GtkConstraintSolver *solver = gtk_constraint_solver_new (); |
207 | |
208 | GtkConstraintVariable *a = gtk_constraint_solver_create_variable (solver, NULL, name: "a" , value: 0.0); |
209 | gtk_constraint_solver_add_stay_variable (solver, variable: a, strength: GTK_CONSTRAINT_STRENGTH_STRONG); |
210 | |
211 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 0.0, 0.001); |
212 | |
213 | gtk_constraint_solver_add_edit_variable (solver, variable: a, strength: GTK_CONSTRAINT_STRENGTH_REQUIRED); |
214 | gtk_constraint_solver_begin_edit (solver); |
215 | gtk_constraint_solver_suggest_value (solver, variable: a, value: 2.0); |
216 | gtk_constraint_solver_resolve (solver); |
217 | |
218 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 2.0, 0.001); |
219 | |
220 | gtk_constraint_solver_suggest_value (solver, variable: a, value: 10.0); |
221 | gtk_constraint_solver_resolve (solver); |
222 | |
223 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 10.0, 0.001); |
224 | |
225 | gtk_constraint_solver_end_edit (solver); |
226 | |
227 | gtk_constraint_variable_unref (variable: a); |
228 | |
229 | g_object_unref (object: solver); |
230 | } |
231 | |
232 | static void |
233 | constraint_solver_edit_var_suggest (void) |
234 | { |
235 | GtkConstraintSolver *solver = gtk_constraint_solver_new (); |
236 | |
237 | GtkConstraintVariable *a = gtk_constraint_solver_create_variable (solver, NULL, name: "a" , value: 0.0); |
238 | GtkConstraintVariable *b = gtk_constraint_solver_create_variable (solver, NULL, name: "b" , value: 0.0); |
239 | |
240 | gtk_constraint_solver_add_stay_variable (solver, variable: a, strength: GTK_CONSTRAINT_STRENGTH_STRONG); |
241 | |
242 | GtkConstraintExpression *e = gtk_constraint_expression_new_from_variable (variable: b); |
243 | gtk_constraint_solver_add_constraint (solver, |
244 | variable: a, relation: GTK_CONSTRAINT_RELATION_EQ, expression: e, |
245 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED); |
246 | |
247 | gtk_constraint_solver_resolve (solver); |
248 | |
249 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 0.0, 0.001); |
250 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (b), 0.0, 0.001); |
251 | |
252 | gtk_constraint_solver_add_edit_variable (solver, variable: a, strength: GTK_CONSTRAINT_STRENGTH_REQUIRED); |
253 | gtk_constraint_solver_begin_edit (solver); |
254 | |
255 | gtk_constraint_solver_suggest_value (solver, variable: a, value: 2.0); |
256 | gtk_constraint_solver_resolve (solver); |
257 | |
258 | g_test_message (format: "Check values after first edit" ); |
259 | |
260 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 2.0, 0.001); |
261 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (b), 2.0, 0.001); |
262 | |
263 | gtk_constraint_solver_suggest_value (solver, variable: a, value: 10.0); |
264 | gtk_constraint_solver_resolve (solver); |
265 | |
266 | g_test_message (format: "Check values after second edit" ); |
267 | |
268 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 10.0, 0.001); |
269 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (b), 10.0, 0.001); |
270 | |
271 | gtk_constraint_solver_suggest_value (solver, variable: a, value: 12.0); |
272 | gtk_constraint_solver_resolve (solver); |
273 | |
274 | g_test_message (format: "Check values after third edit" ); |
275 | |
276 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (a), 12.0, 0.001); |
277 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (b), 12.0, 0.001); |
278 | |
279 | gtk_constraint_variable_unref (variable: a); |
280 | gtk_constraint_variable_unref (variable: b); |
281 | |
282 | g_object_unref (object: solver); |
283 | } |
284 | |
285 | static void |
286 | constraint_solver_paper (void) |
287 | { |
288 | GtkConstraintSolver *solver = gtk_constraint_solver_new (); |
289 | |
290 | GtkConstraintVariable *left = gtk_constraint_solver_create_variable (solver, NULL, name: "left" , value: 0.0); |
291 | GtkConstraintVariable *middle = gtk_constraint_solver_create_variable (solver, NULL, name: "middle" , value: 0.0); |
292 | GtkConstraintVariable *right = gtk_constraint_solver_create_variable (solver, NULL, name: "right" , value: 0.0); |
293 | |
294 | GtkConstraintExpressionBuilder builder; |
295 | GtkConstraintExpression *expr; |
296 | |
297 | gtk_constraint_expression_builder_init (builder: &builder, solver); |
298 | gtk_constraint_expression_builder_term (builder: &builder, term: left); |
299 | gtk_constraint_expression_builder_plus (builder: &builder); |
300 | gtk_constraint_expression_builder_term (builder: &builder, term: right); |
301 | gtk_constraint_expression_builder_divide_by (builder: &builder); |
302 | gtk_constraint_expression_builder_constant (builder: &builder, value: 2.0); |
303 | expr = gtk_constraint_expression_builder_finish (builder: &builder); |
304 | gtk_constraint_solver_add_constraint (solver, |
305 | variable: middle, relation: GTK_CONSTRAINT_RELATION_EQ, expression: expr, |
306 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED); |
307 | |
308 | gtk_constraint_expression_builder_init (builder: &builder, solver); |
309 | gtk_constraint_expression_builder_term (builder: &builder, term: left); |
310 | gtk_constraint_expression_builder_plus (builder: &builder); |
311 | gtk_constraint_expression_builder_constant (builder: &builder, value: 10.0); |
312 | expr = gtk_constraint_expression_builder_finish (builder: &builder); |
313 | gtk_constraint_solver_add_constraint (solver, |
314 | variable: right, relation: GTK_CONSTRAINT_RELATION_EQ, expression: expr, |
315 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED); |
316 | |
317 | expr = gtk_constraint_expression_new (constant: 100.0); |
318 | gtk_constraint_solver_add_constraint (solver, |
319 | variable: right, relation: GTK_CONSTRAINT_RELATION_LE, expression: expr, |
320 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED); |
321 | |
322 | expr = gtk_constraint_expression_new (constant: 0.0); |
323 | gtk_constraint_solver_add_constraint (solver, |
324 | variable: left, relation: GTK_CONSTRAINT_RELATION_GE, expression: expr, |
325 | strength: GTK_CONSTRAINT_STRENGTH_REQUIRED); |
326 | |
327 | g_test_message (format: "Check constraints hold" ); |
328 | |
329 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (middle), |
330 | (gtk_constraint_variable_get_value (left) + gtk_constraint_variable_get_value (right)) / 2.0, |
331 | 0.001); |
332 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (right), |
333 | gtk_constraint_variable_get_value (left) + 10.0, |
334 | 0.001); |
335 | g_assert_cmpfloat (gtk_constraint_variable_get_value (right), <=, 100.0); |
336 | g_assert_cmpfloat (gtk_constraint_variable_get_value (left), >=, 0.0); |
337 | |
338 | gtk_constraint_variable_set_value (variable: middle, value: 45.0); |
339 | gtk_constraint_solver_add_stay_variable (solver, variable: middle, strength: GTK_CONSTRAINT_STRENGTH_WEAK); |
340 | |
341 | g_test_message (format: "Check constraints hold after setting middle" ); |
342 | |
343 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (middle), |
344 | (gtk_constraint_variable_get_value (left) + gtk_constraint_variable_get_value (right)) / 2.0, |
345 | 0.001); |
346 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (right), |
347 | gtk_constraint_variable_get_value (left) + 10.0, |
348 | 0.001); |
349 | g_assert_cmpfloat (gtk_constraint_variable_get_value (right), <=, 100.0); |
350 | g_assert_cmpfloat (gtk_constraint_variable_get_value (left), >=, 0.0); |
351 | |
352 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (left), 40.0, 0.001); |
353 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (middle), 45.0, 0.001); |
354 | g_assert_cmpfloat_with_epsilon (gtk_constraint_variable_get_value (right), 50.0, 0.001); |
355 | |
356 | gtk_constraint_variable_unref (variable: left); |
357 | gtk_constraint_variable_unref (variable: middle); |
358 | gtk_constraint_variable_unref (variable: right); |
359 | |
360 | g_object_unref (object: solver); |
361 | } |
362 | |
363 | int |
364 | main (int argc, char *argv[]) |
365 | { |
366 | (g_test_init) (argc: &argc, argv: &argv, NULL); |
367 | setlocale (LC_ALL, locale: "C" ); |
368 | |
369 | g_test_add_func (testpath: "/constraint-solver/paper" , test_func: constraint_solver_paper); |
370 | g_test_add_func (testpath: "/constraint-solver/simple" , test_func: constraint_solver_simple); |
371 | g_test_add_func (testpath: "/constraint-solver/constant/eq" , test_func: constraint_solver_variable_eq_constant); |
372 | g_test_add_func (testpath: "/constraint-solver/constant/ge" , test_func: constraint_solver_variable_geq_constant); |
373 | g_test_add_func (testpath: "/constraint-solver/constant/le" , test_func: constraint_solver_variable_leq_constant); |
374 | g_test_add_func (testpath: "/constraint-solver/stay/simple" , test_func: constraint_solver_stay); |
375 | g_test_add_func (testpath: "/constraint-solver/stay/eq" , test_func: constraint_solver_eq_with_stay); |
376 | g_test_add_func (testpath: "/constraint-solver/cassowary" , test_func: constraint_solver_cassowary); |
377 | g_test_add_func (testpath: "/constraint-solver/edit/required" , test_func: constraint_solver_edit_var_required); |
378 | g_test_add_func (testpath: "/constraint-solver/edit/suggest" , test_func: constraint_solver_edit_var_suggest); |
379 | |
380 | return g_test_run (); |
381 | } |
382 | |