Branch data Line data Source code
1 : : /* ______ ___ ___
2 : : * /\ _ \ /\_ \ /\_ \
3 : : * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
4 : : * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
5 : : * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
6 : : * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7 : : * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8 : : * /\____/
9 : : * \_/__/
10 : : *
11 : : * Some high level routines, provided for user's convinience.
12 : : *
13 : : *
14 : : * By Pavel Sountsov.
15 : : *
16 : : *
17 : : * Bezier spline plotter By Seymour Shlien.
18 : : *
19 : : * Optimised version by Sven Sandberg.
20 : : *
21 : : * I'm not sure wether or not we still use the Castelau Algorithm
22 : : * described in the book :o)
23 : : *
24 : : * Interactive Computer Graphics
25 : : * by Peter Burger and Duncan Gillies
26 : : * Addison-Wesley Publishing Co 1989
27 : : * ISBN 0-201-17439-1
28 : : *
29 : : * The 4 th order Bezier curve is a cubic curve passing
30 : : * through the first and fourth point. The curve does
31 : : * not pass through the middle two points. They are merely
32 : : * guide points which control the shape of the curve. The
33 : : * curve is tangent to the lines joining points 1 and 2
34 : : * and points 3 and 4.
35 : : *
36 : : * See readme.txt for copyright information.
37 : : */
38 : :
39 : :
40 : : #include "allegro5/allegro_primitives.h"
41 : : #ifdef ALLEGRO_CFG_OPENGL
42 : : #include "allegro5/allegro_opengl.h"
43 : : #endif
44 : : #include "allegro5/internal/aintern_bitmap.h"
45 : : #include <math.h>
46 : :
47 : : #ifdef ALLEGRO_MSVC
48 : : #define hypotf(x, y) _hypotf((x), (y))
49 : : #endif
50 : :
51 : : #define LOCAL_VERTEX_CACHE ALLEGRO_VERTEX vertex_cache[ALLEGRO_VERTEX_CACHE_SIZE]
52 : :
53 : : /*
54 : : * Make an estimate of the scale of the current transformation.
55 : : */
56 : 124 : static float get_scale(void)
57 : : {
58 : 124 : const ALLEGRO_TRANSFORM* t = al_get_current_transform();
59 : 124 : return (hypotf(t->m[0][0], t->m[0][1]) + hypotf(t->m[1][0], t->m[1][1])) / 2;
60 : : }
61 : :
62 : : /* Function: al_draw_line
63 : : */
64 : 4144 : void al_draw_line(float x1, float y1, float x2, float y2,
65 : : ALLEGRO_COLOR color, float thickness)
66 : : {
67 [ + + ]: 4144 : if (thickness > 0) {
68 : : int ii;
69 : : float tx, ty;
70 : 34 : float len = hypotf(x2 - x1, y2 - y1);
71 : :
72 : : ALLEGRO_VERTEX vtx[4];
73 : :
74 [ + - ]: 34 : if (len == 0)
75 : 4144 : return;
76 : :
77 : 34 : tx = 0.5f * thickness * (y2 - y1) / len;
78 : 34 : ty = 0.5f * thickness * -(x2 - x1) / len;
79 : :
80 : 34 : vtx[0].x = x1 + tx; vtx[0].y = y1 + ty;
81 : 34 : vtx[1].x = x1 - tx; vtx[1].y = y1 - ty;
82 : 34 : vtx[2].x = x2 - tx; vtx[2].y = y2 - ty;
83 : 34 : vtx[3].x = x2 + tx; vtx[3].y = y2 + ty;
84 : :
85 [ + + ]: 170 : for (ii = 0; ii < 4; ii++) {
86 : 136 : vtx[ii].color = color;
87 : 136 : vtx[ii].z = 0;
88 : : }
89 : :
90 : 34 : al_draw_prim(vtx, 0, 0, 0, 4, ALLEGRO_PRIM_TRIANGLE_FAN);
91 : :
92 : : } else {
93 : : ALLEGRO_VERTEX vtx[2];
94 : :
95 : 4110 : vtx[0].x = x1; vtx[0].y = y1;
96 : 4110 : vtx[1].x = x2; vtx[1].y = y2;
97 : :
98 : 4110 : vtx[0].color = color;
99 : 4110 : vtx[1].color = color;
100 : 4110 : vtx[0].z = 0;
101 : 4110 : vtx[1].z = 0;
102 : :
103 : 4110 : al_draw_prim(vtx, 0, 0, 0, 2, ALLEGRO_PRIM_LINE_LIST);
104 : : }
105 : : }
106 : :
107 : : /* Function: al_draw_triangle
108 : : */
109 : 16 : void al_draw_triangle(float x1, float y1, float x2, float y2,
110 : : float x3, float y3, ALLEGRO_COLOR color, float thickness)
111 : : {
112 [ + + ]: 16 : if (thickness > 0) {
113 : 14 : int ii = 0;
114 : : float side1, side2, side3;
115 : : float perimeter, semi_perimeter;
116 : : float outer_frac, inner_frac;
117 : : float incenter_x, incenter_y;
118 : : float incircle_rad;
119 : 14 : int idx = 0;
120 : : ALLEGRO_VERTEX vtx[5];
121 : 14 : float x[3] = {x1, x2, x3};
122 : 14 : float y[3] = {y1, y2, y3};
123 : : ALLEGRO_VERTEX first_inner_vtx;
124 : : ALLEGRO_VERTEX first_outer_vtx;
125 : : ALLEGRO_VERTEX ini_vtx;
126 : 14 : float cross = (x[1] - x[0]) * (y[2] - y[0]) - (x[2] - x[0]) * (y[1] - y[0]);
127 : :
128 : 14 : ini_vtx.x = ini_vtx.y = ini_vtx.z = ini_vtx.u = ini_vtx.v = 0;
129 : 14 : ini_vtx.color = color;
130 : 14 : first_inner_vtx = ini_vtx;
131 : 14 : first_outer_vtx = ini_vtx;
132 : :
133 : : /*
134 : : * If the triangle is very flat, draw it as a line
135 : : */
136 [ - + ]: 14 : if(fabs(cross) < 0.0001f) {
137 : : float tx, ty, lx, ly;
138 : : float len;
139 : : /*
140 : : * Find the obtuse vertex via two dot products
141 : : */
142 : 0 : float dot = (x[1] - x[0]) * (x[2] - x[0]) + (y[1] - y[0]) * (y[2] - y[0]);
143 [ # # ]: 0 : if(dot < 0) {
144 : : x1 = x[1]; y1 = y[1];
145 : : x2 = x[2]; y2 = y[2];
146 : : } else {
147 : 0 : dot = (x[0] - x[1]) * (x[2] - x[1]) + (y[0] - y[1]) * (y[2] - y[1]);
148 [ # # ]: 0 : if(dot < 0) {
149 : : x1 = x[0]; y1 = y[0];
150 : : x2 = x[2]; y2 = y[2];
151 : : } else {
152 : 0 : x1 = x[0]; y1 = y[0];
153 : 0 : x2 = x[1]; y2 = y[1];
154 : : }
155 : : }
156 : 0 : len = hypotf(x2 - x1, y2 - y1);
157 : :
158 [ # # ]: 0 : if (len == 0)
159 : : return;
160 : :
161 : 0 : tx = 0.5f * thickness * (y2 - y1) / len;
162 : 0 : ty = 0.5f * thickness * -(x2 - x1) / len;
163 : 0 : lx = 0.5f * thickness * (x2 - x1) / len;
164 : 0 : ly = 0.5f * thickness * (y2 - y1) / len;
165 : :
166 : 0 : vtx[0].x = x1 + tx - lx; vtx[0].y = y1 + ty - ly;
167 : 0 : vtx[1].x = x1 - tx - lx; vtx[1].y = y1 - ty - ly;
168 : 0 : vtx[2].x = x2 - tx + lx; vtx[2].y = y2 - ty + ly;
169 : 0 : vtx[3].x = x2 + tx + lx; vtx[3].y = y2 + ty + ly;
170 : :
171 [ # # ]: 0 : for (ii = 0; ii < 4; ii++) {
172 : 0 : vtx[ii].color = color;
173 : 0 : vtx[ii].z = 0;
174 : : }
175 : :
176 : 0 : al_draw_prim(vtx, 0, 0, 0, 4, ALLEGRO_PRIM_TRIANGLE_FAN);
177 : : return;
178 : : }
179 [ - + ]: 14 : else if(cross > 0) {
180 : : /*
181 : : * Points need to be wound correctly for the algorithm to work
182 : : */
183 : : float t;
184 : 0 : t = x[1];
185 : 0 : x[1] = x[2];
186 : 0 : x[2] = t;
187 : :
188 : 0 : t = y[1];
189 : 0 : y[1] = y[2];
190 : 0 : y[2] = t;
191 : : }
192 : :
193 : 14 : side1 = hypotf(x[1] - x[0], y[1] - y[0]);
194 : 14 : side2 = hypotf(x[2] - x[0], y[2] - y[0]);
195 : 14 : side3 = hypotf(x[2] - x[1], y[2] - y[1]);
196 : :
197 : 14 : perimeter = side1 + side2 + side3;
198 : 14 : semi_perimeter = perimeter / 2.0f;
199 [ + - ]: 14 : if (semi_perimeter < 0.00001f)
200 : : return;
201 : :
202 : 14 : incircle_rad = sqrtf((semi_perimeter - side1) * (semi_perimeter - side2) * (semi_perimeter - side3) / semi_perimeter);
203 : :
204 [ + - ]: 14 : if (incircle_rad < 0.00001f)
205 : : return;
206 : :
207 : 14 : outer_frac = (incircle_rad + thickness / 2) / incircle_rad;
208 : 14 : inner_frac = (incircle_rad - thickness / 2) / incircle_rad;
209 : :
210 [ - + ]: 14 : if(inner_frac < 0)
211 : 0 : inner_frac = 0;
212 : :
213 : 14 : incenter_x = (side1 * x[2] + side2 * x[1] + side3 * x[0]) / perimeter;
214 : 14 : incenter_y = (side1 * y[2] + side2 * y[1] + side3 * y[0]) / perimeter;
215 : :
216 : : #define DRAW \
217 : : if(ii != 0) { \
218 : : vtx[idx++] = outer_vtx; \
219 : : vtx[idx++] = inner_vtx; \
220 : : \
221 : : al_draw_prim(vtx, 0, 0, 0, idx, ALLEGRO_PRIM_TRIANGLE_FAN); \
222 : : \
223 : : idx = 0; \
224 : : }
225 : :
226 : : /*
227 : : * Iterate across the vertices, and draw each side of the triangle separately
228 : : */
229 [ + + ]: 56 : for(ii = 0; ii < 3; ii++)
230 : : {
231 : 42 : float vert_x = x[ii] - incenter_x;
232 : 42 : float vert_y = y[ii] - incenter_y;
233 : :
234 : 42 : float o_dx = vert_x * outer_frac;
235 : 42 : float o_dy = vert_y * outer_frac;
236 : :
237 : 42 : float i_dx = vert_x * inner_frac;
238 : 42 : float i_dy = vert_y * inner_frac;
239 : :
240 : 42 : float tdx = o_dx - i_dx;
241 : 42 : float tdy = o_dy - i_dy;
242 : :
243 : 42 : ALLEGRO_VERTEX inner_vtx = ini_vtx;
244 : 42 : ALLEGRO_VERTEX outer_vtx = ini_vtx;
245 : :
246 [ - + ]: 42 : if(tdx * tdx + tdy * tdy > 16 * thickness * thickness) {
247 : 0 : float x_pos = x[(ii + 1) % 3];
248 : 0 : float y_pos = y[(ii + 1) % 3];
249 : :
250 : 0 : float x_neg = x[(ii + 2) % 3];
251 : 0 : float y_neg = y[(ii + 2) % 3];
252 : :
253 : 0 : float x1_x2 = x[ii] - x_pos;
254 : 0 : float y1_y2 = y[ii] - y_pos;
255 : :
256 : 0 : float x1_x3 = x[ii] - x_neg;
257 : 0 : float y1_y3 = y[ii] - y_neg;
258 : :
259 : 0 : float mag_1_2 = hypotf(x1_x2, y1_y2);
260 : 0 : float mag_1_3 = hypotf(x1_x3, y1_y3);
261 : :
262 : 0 : ALLEGRO_VERTEX next_vtx = ini_vtx;
263 : :
264 : 0 : x1_x2 *= thickness / 2 / mag_1_2;
265 : 0 : y1_y2 *= thickness / 2 / mag_1_2;
266 : :
267 : 0 : x1_x3 *= thickness / 2 / mag_1_3;
268 : 0 : y1_y3 *= thickness / 2 / mag_1_3;
269 : :
270 : 0 : outer_vtx.x = x[ii] + x1_x3 - y1_y3; outer_vtx.y = y[ii] + y1_y3 + x1_x3;
271 : 0 : inner_vtx.x = incenter_x + i_dx; inner_vtx.y = incenter_y + i_dy;
272 : 0 : next_vtx.x = x[ii] + x1_x2 + y1_y2; next_vtx.y = y[ii] + y1_y2 - x1_x2;
273 : :
274 [ # # ]: 0 : DRAW
275 : :
276 : 0 : vtx[idx++] = inner_vtx;
277 : 0 : vtx[idx++] = outer_vtx;
278 : 0 : vtx[idx++] = next_vtx;
279 : : } else {
280 : 42 : inner_vtx.x = incenter_x + i_dx; inner_vtx.y = incenter_y + i_dy;
281 : 42 : outer_vtx.x = incenter_x + o_dx; outer_vtx.y = incenter_y + o_dy;
282 : :
283 [ + + ]: 42 : DRAW
284 : :
285 : 42 : vtx[idx++] = inner_vtx;
286 : 42 : vtx[idx++] = outer_vtx;
287 : : }
288 : :
289 [ + + ]: 42 : if(ii == 0) {
290 : 14 : first_inner_vtx = inner_vtx;
291 : 14 : first_outer_vtx = outer_vtx;
292 : : }
293 : : }
294 : :
295 : 14 : vtx[idx++] = first_outer_vtx;
296 : 14 : vtx[idx++] = first_inner_vtx;
297 : :
298 : 14 : al_draw_prim(vtx, 0, 0, 0, idx, ALLEGRO_PRIM_TRIANGLE_FAN);
299 : :
300 : : #undef DRAW
301 : : } else {
302 : : ALLEGRO_VERTEX vtx[3];
303 : :
304 : 2 : vtx[0].x = x1; vtx[0].y = y1;
305 : 2 : vtx[1].x = x2; vtx[1].y = y2;
306 : 2 : vtx[2].x = x3; vtx[2].y = y3;
307 : :
308 : 2 : vtx[0].color = color;
309 : 2 : vtx[1].color = color;
310 : 2 : vtx[2].color = color;
311 : :
312 : 2 : vtx[0].z = 0;
313 : 2 : vtx[1].z = 0;
314 : 2 : vtx[2].z = 0;
315 : :
316 : 16 : al_draw_prim(vtx, 0, 0, 0, 3, ALLEGRO_PRIM_LINE_LOOP);
317 : : }
318 : : }
319 : :
320 : : /* Function: al_draw_filled_triangle
321 : : */
322 : 10 : void al_draw_filled_triangle(float x1, float y1, float x2, float y2,
323 : : float x3, float y3, ALLEGRO_COLOR color)
324 : : {
325 : : ALLEGRO_VERTEX vtx[3];
326 : :
327 : 10 : vtx[0].x = x1; vtx[0].y = y1;
328 : 10 : vtx[1].x = x2; vtx[1].y = y2;
329 : 10 : vtx[2].x = x3; vtx[2].y = y3;
330 : :
331 : 10 : vtx[0].color = color;
332 : 10 : vtx[1].color = color;
333 : 10 : vtx[2].color = color;
334 : :
335 : 10 : vtx[0].z = 0;
336 : 10 : vtx[1].z = 0;
337 : 10 : vtx[2].z = 0;
338 : :
339 : 10 : al_draw_prim(vtx, 0, 0, 0, 3, ALLEGRO_PRIM_TRIANGLE_LIST);
340 : 10 : }
341 : :
342 : : /* Function: al_draw_rectangle
343 : : */
344 : 46 : void al_draw_rectangle(float x1, float y1, float x2, float y2,
345 : : ALLEGRO_COLOR color, float thickness)
346 : : {
347 : : int ii;
348 : :
349 [ + + ]: 46 : if (thickness > 0) {
350 : 38 : float t = thickness / 2;
351 : : ALLEGRO_VERTEX vtx[10];
352 : :
353 : 38 : vtx[0].x = x1 - t; vtx[0].y = y1 - t;
354 : 38 : vtx[1].x = x1 + t; vtx[1].y = y1 + t;
355 : 38 : vtx[2].x = x2 + t; vtx[2].y = y1 - t;
356 : 38 : vtx[3].x = x2 - t; vtx[3].y = y1 + t;
357 : 38 : vtx[4].x = x2 + t; vtx[4].y = y2 + t;
358 : 38 : vtx[5].x = x2 - t; vtx[5].y = y2 - t;
359 : 38 : vtx[6].x = x1 - t; vtx[6].y = y2 + t;
360 : 38 : vtx[7].x = x1 + t; vtx[7].y = y2 - t;
361 : 38 : vtx[8].x = x1 - t; vtx[8].y = y1 - t;
362 : 38 : vtx[9].x = x1 + t; vtx[9].y = y1 + t;
363 : :
364 [ + + ]: 418 : for (ii = 0; ii < 10; ii++) {
365 : 380 : vtx[ii].color = color;
366 : 380 : vtx[ii].z = 0;
367 : : }
368 : :
369 : 38 : al_draw_prim(vtx, 0, 0, 0, 10, ALLEGRO_PRIM_TRIANGLE_STRIP);
370 : : } else {
371 : : ALLEGRO_VERTEX vtx[4];
372 : :
373 : 8 : vtx[0].x = x1; vtx[0].y = y1;
374 : 8 : vtx[1].x = x2; vtx[1].y = y1;
375 : 8 : vtx[2].x = x2; vtx[2].y = y2;
376 : 8 : vtx[3].x = x1; vtx[3].y = y2;
377 : :
378 [ + + ]: 40 : for (ii = 0; ii < 4; ii++) {
379 : 32 : vtx[ii].color = color;
380 : 32 : vtx[ii].z = 0;
381 : : }
382 : :
383 : 8 : al_draw_prim(vtx, 0, 0, 0, 4, ALLEGRO_PRIM_LINE_LOOP);
384 : : }
385 : 46 : }
386 : :
387 : : /* Function: al_draw_filled_rectangle
388 : : */
389 : 24 : void al_draw_filled_rectangle(float x1, float y1, float x2, float y2,
390 : : ALLEGRO_COLOR color)
391 : : {
392 : : ALLEGRO_VERTEX vtx[4];
393 : : int ii;
394 : :
395 : 24 : vtx[0].x = x1; vtx[0].y = y1;
396 : 24 : vtx[1].x = x1; vtx[1].y = y2;
397 : 24 : vtx[2].x = x2; vtx[2].y = y2;
398 : 24 : vtx[3].x = x2; vtx[3].y = y1;
399 : :
400 [ + + ]: 120 : for (ii = 0; ii < 4; ii++) {
401 : 96 : vtx[ii].color = color;
402 : 96 : vtx[ii].z = 0;
403 : : }
404 : :
405 : 24 : al_draw_prim(vtx, 0, 0, 0, 4, ALLEGRO_PRIM_TRIANGLE_FAN);
406 : 24 : }
407 : :
408 : : /* Function: al_calculate_arc
409 : : */
410 : 102 : void al_calculate_arc(float* dest, int stride, float cx, float cy,
411 : : float rx, float ry, float start_theta, float delta_theta, float thickness,
412 : : int num_segments)
413 : : {
414 : : float theta;
415 : : float c;
416 : : float s;
417 : : float x, y, t;
418 : : int ii;
419 : :
420 [ - + ][ # # ]: 102 : ASSERT(dest);
[ # # ]
421 [ - + ][ # # ]: 102 : ASSERT(num_segments > 1);
[ # # ]
422 [ - + ][ # # ]: 102 : ASSERT(rx >= 0);
[ # # ]
423 [ - + ][ # # ]: 102 : ASSERT(ry >= 0);
[ # # ]
424 : :
425 [ + + ]: 102 : if (thickness > 0.0f) {
426 : 62 : theta = delta_theta / ((float)(num_segments) - 1);
427 : 62 : c = cosf(theta);
428 : 62 : s = sinf(theta);
429 : 62 : x = cosf(start_theta);
430 : 62 : y = sinf(start_theta);
431 : :
432 [ + + ]: 62 : if (rx == ry) {
433 : : /*
434 : : The circle case is particularly simple
435 : : */
436 : 24 : float r1 = rx - thickness / 2.0f;
437 : 24 : float r2 = rx + thickness / 2.0f;
438 [ + + ]: 1600 : for (ii = 0; ii < num_segments; ii ++) {
439 : 1576 : *dest = r2 * x + cx;
440 : 1576 : *(dest + 1) = r2 * y + cy;
441 : 1576 : dest = (float*)(((char*)dest) + stride);
442 : 1576 : *dest = r1 * x + cx;
443 : 1576 : *(dest + 1) = r1 * y + cy;
444 : 1576 : dest = (float*)(((char*)dest) + stride);
445 : :
446 : 1576 : t = x;
447 : 1576 : x = c * x - s * y;
448 : 1576 : y = s * t + c * y;
449 : : }
450 : : } else {
451 [ + - ]: 38 : if (rx != 0 && !ry == 0) {
452 [ + + ]: 2450 : for (ii = 0; ii < num_segments; ii++) {
453 : 2412 : float denom = hypotf(ry * x, rx * y);
454 : 2412 : float nx = thickness / 2 * ry * x / denom;
455 : 2412 : float ny = thickness / 2 * rx * y / denom;
456 : :
457 : 2412 : *dest = rx * x + cx + nx;
458 : 2412 : *(dest + 1) = ry * y + cy + ny;
459 : 2412 : dest = (float*)(((char*)dest) + stride);
460 : 2412 : *dest = rx * x + cx - nx;
461 : 2412 : *(dest + 1) = ry * y + cy - ny;
462 : 2412 : dest = (float*)(((char*)dest) + stride);
463 : :
464 : 2412 : t = x;
465 : 2412 : x = c * x - s * y;
466 : 2412 : y = s * t + c * y;
467 : : }
468 : : }
469 : : }
470 : : } else {
471 : 40 : theta = delta_theta / ((float)num_segments - 1);
472 : 40 : c = cosf(theta);
473 : 40 : s = sinf(theta);
474 : 40 : x = cosf(start_theta);
475 : 40 : y = sinf(start_theta);
476 : :
477 [ + + ]: 1886 : for (ii = 0; ii < num_segments; ii++) {
478 : 1846 : *dest = rx * x + cx;
479 : 1846 : *(dest + 1) = ry * y + cy;
480 : 1846 : dest = (float*)(((char*)dest) + stride);
481 : :
482 : 1846 : t = x;
483 : 1846 : x = c * x - s * y;
484 : 1846 : y = s * t + c * y;
485 : : }
486 : : }
487 : 102 : }
488 : :
489 : : /* Function: al_draw_pieslice
490 : : */
491 : 4 : void al_draw_pieslice(float cx, float cy, float r, float start_theta,
492 : : float delta_theta, ALLEGRO_COLOR color, float thickness)
493 : : {
494 : : LOCAL_VERTEX_CACHE;
495 : 4 : float scale = get_scale();
496 : : int num_segments, ii;
497 : :
498 [ - + ][ # # ]: 4 : ASSERT(r >= 0);
[ # # ]
499 : :
500 : : /* Just makes things a bit easier */
501 [ - + ]: 4 : if(delta_theta < 0) {
502 : 0 : delta_theta = -delta_theta;
503 : 0 : start_theta -= delta_theta;
504 : : }
505 : :
506 [ - + ]: 4 : if (thickness <= 0) {
507 : 0 : num_segments = fabs(delta_theta / (2 * ALLEGRO_PI) * ALLEGRO_PRIM_QUALITY * scale * sqrtf(r));
508 : :
509 [ # # ]: 0 : if (num_segments < 2)
510 : 0 : num_segments = 2;
511 : :
512 [ # # ]: 0 : if (num_segments + 1 >= ALLEGRO_VERTEX_CACHE_SIZE) {
513 : 0 : num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1 - 1;
514 : : }
515 : :
516 : 0 : al_calculate_arc(&(vertex_cache[1].x), sizeof(ALLEGRO_VERTEX), cx, cy, r, r, start_theta, delta_theta, 0, num_segments);
517 : 0 : vertex_cache[0].x = cx; vertex_cache[0].y = cy;
518 : :
519 [ # # ]: 0 : for (ii = 0; ii < num_segments + 1; ii++) {
520 : 0 : vertex_cache[ii].color = color;
521 : 0 : vertex_cache[ii].z = 0;
522 : : }
523 : :
524 : 0 : al_draw_prim(vertex_cache, 0, 0, 0, num_segments + 1, ALLEGRO_PRIM_LINE_LOOP);
525 : : } else {
526 : 4 : float ht = thickness / 2;
527 : 4 : float inner_side_angle = asinf(ht / (r - ht));
528 : 4 : float outer_side_angle = asinf(ht / (r + ht));
529 : 4 : float central_angle = delta_theta - 2 * inner_side_angle;
530 : 4 : bool inverted_winding = ((int)(delta_theta / ALLEGRO_PI)) % 2 == 1;
531 : 4 : float midangle = start_theta + (fmodf(delta_theta + ALLEGRO_PI, 2 * ALLEGRO_PI) - ALLEGRO_PI) / 2;
532 : 4 : float midpoint_dir_x = cosf(midangle);
533 : 4 : float midpoint_dir_y = sinf(midangle);
534 : 4 : float side_dir_x = cosf(start_theta);
535 : 4 : float side_dir_y = sinf(start_theta);
536 : 4 : float sine_half_delta = fabs(side_dir_x * midpoint_dir_y - side_dir_y * midpoint_dir_x); /* Cross product */
537 : 4 : float connect_len = ht / sine_half_delta;
538 : 4 : bool blunt_tip = connect_len > 2 * thickness;
539 : :
540 : : /* The angle is big enough for there to be a hole in the middle */
541 [ + - ]: 4 : if (central_angle > 0) {
542 : 4 : float central_start_angle = start_theta + inner_side_angle;
543 : : size_t vtx_id;
544 : : int vtx_delta;
545 : : /* Two inner hole vertices and the apex (2 vertices if the apex is blunt) */
546 [ - + ]: 4 : int extra_vtx = blunt_tip ? 4 : 3;
547 : :
548 : 4 : al_draw_arc(cx, cy, r, central_start_angle, central_angle, color, thickness);
549 : :
550 : 4 : vertex_cache[0].x = cx + (r - thickness / 2) * cosf(central_start_angle);
551 : 4 : vertex_cache[0].y = cy + (r - thickness / 2) * sinf(central_start_angle);
552 : :
553 : 4 : num_segments = (inner_side_angle + outer_side_angle) / (2 * ALLEGRO_PI) * ALLEGRO_PRIM_QUALITY * scale * sqrtf(r + ht);
554 : :
555 [ + - ]: 4 : if (num_segments < 2)
556 : 4 : num_segments = 2;
557 : :
558 [ - + ]: 4 : if (num_segments + extra_vtx >= ALLEGRO_VERTEX_CACHE_SIZE)
559 : 0 : num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1 - extra_vtx;
560 : :
561 : 4 : al_calculate_arc(&(vertex_cache[1].x), sizeof(ALLEGRO_VERTEX), cx, cy, r + ht, r + ht, central_start_angle, -(outer_side_angle + inner_side_angle), 0, num_segments);
562 : :
563 : : /* Do the tip */
564 [ + - ]: 4 : vtx_id = num_segments + 1 + (inverted_winding ? (1 + (blunt_tip ? 1 : 0)) : 0);
565 [ - + ]: 4 : vtx_delta = inverted_winding ? -1 : 1;
566 [ + - ]: 4 : if (blunt_tip) {
567 [ - + ]: 4 : float vx = ht * (side_dir_y * (inverted_winding ? -1 : 1) - side_dir_x);
568 [ - + ]: 4 : float vy = ht * (-side_dir_x * (inverted_winding ? -1 : 1) - side_dir_y);
569 : 4 : float dot = vx * midpoint_dir_x + vy * midpoint_dir_y;
570 : :
571 : 4 : vertex_cache[vtx_id].x = cx + vx;
572 : 4 : vertex_cache[vtx_id].y = cy + vy;
573 : 4 : vtx_id += vtx_delta;
574 : :
575 : 4 : vertex_cache[vtx_id].x = cx + dot * midpoint_dir_x;
576 : 4 : vertex_cache[vtx_id].y = cy + dot * midpoint_dir_y;
577 : : } else {
578 : 0 : vertex_cache[vtx_id].x = cx - connect_len * midpoint_dir_x;
579 : 0 : vertex_cache[vtx_id].y = cy - connect_len * midpoint_dir_y;
580 : : }
581 : 4 : vtx_id += vtx_delta;
582 : :
583 [ - + ]: 4 : if(connect_len > r - ht)
584 : 0 : connect_len = r - ht;
585 : 4 : vertex_cache[vtx_id].x = cx + connect_len * midpoint_dir_x;
586 : 4 : vertex_cache[vtx_id].y = cy + connect_len * midpoint_dir_y;
587 : :
588 [ + + ]: 28 : for (ii = 0; ii < num_segments + extra_vtx; ii++) {
589 : 24 : vertex_cache[ii].color = color;
590 : 24 : vertex_cache[ii].z = 0;
591 : : }
592 : :
593 : 4 : al_draw_prim(vertex_cache, 0, 0, 0, num_segments + extra_vtx, ALLEGRO_PRIM_TRIANGLE_FAN);
594 : :
595 : : /* Mirror the vertices and draw them again */
596 [ + + ]: 28 : for (ii = 0; ii < num_segments + extra_vtx; ii++) {
597 : 24 : float dot = (vertex_cache[ii].x - cx) * midpoint_dir_x + (vertex_cache[ii].y - cy) * midpoint_dir_y;
598 : 24 : vertex_cache[ii].x = 2 * cx + 2 * dot * midpoint_dir_x - vertex_cache[ii].x;
599 : 24 : vertex_cache[ii].y = 2 * cy + 2 * dot * midpoint_dir_y - vertex_cache[ii].y;
600 : : }
601 : :
602 : 4 : al_draw_prim(vertex_cache, 0, 0, 0, num_segments + extra_vtx, ALLEGRO_PRIM_TRIANGLE_FAN);
603 : : } else {
604 : : /* Apex: 2 vertices if the apex is blunt) */
605 [ # # ]: 0 : int extra_vtx = blunt_tip ? 2 : 1;
606 : :
607 : 0 : num_segments = (2 * outer_side_angle) / (2 * ALLEGRO_PI) * ALLEGRO_PRIM_QUALITY * scale * sqrtf(r + ht);
608 : :
609 [ # # ]: 0 : if (num_segments < 2)
610 : 0 : num_segments = 2;
611 : :
612 [ # # ]: 0 : if (num_segments + extra_vtx >= ALLEGRO_VERTEX_CACHE_SIZE)
613 : 0 : num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1 - extra_vtx;
614 : :
615 : 0 : al_calculate_arc(&(vertex_cache[1].x), sizeof(ALLEGRO_VERTEX), cx, cy, r + ht, r + ht, start_theta - outer_side_angle, 2 * outer_side_angle + delta_theta, 0, num_segments);
616 : :
617 [ # # ]: 0 : if (blunt_tip) {
618 : 0 : float vx = ht * (side_dir_y - side_dir_x);
619 : 0 : float vy = ht * (-side_dir_x - side_dir_y);
620 : 0 : float dot = vx * midpoint_dir_x + vy * midpoint_dir_y;
621 : :
622 : 0 : vertex_cache[0].x = cx + vx;
623 : 0 : vertex_cache[0].y = cy + vy;
624 : :
625 : 0 : vx = 2 * dot * midpoint_dir_x - vx;
626 : 0 : vy = 2 * dot * midpoint_dir_y - vy;
627 : :
628 : 0 : vertex_cache[num_segments + 1].x = cx + vx;
629 : 0 : vertex_cache[num_segments + 1].y = cy + vy;
630 : : } else {
631 : 0 : vertex_cache[0].x = cx - connect_len * midpoint_dir_x;
632 : 0 : vertex_cache[0].y = cy - connect_len * midpoint_dir_y;
633 : : }
634 : :
635 [ # # ]: 0 : for (ii = 0; ii < num_segments + extra_vtx; ii++) {
636 : 0 : vertex_cache[ii].color = color;
637 : 0 : vertex_cache[ii].z = 0;
638 : : }
639 : :
640 : 0 : al_draw_prim(vertex_cache, 0, 0, 0, num_segments + extra_vtx, ALLEGRO_PRIM_TRIANGLE_FAN);
641 : : }
642 : : }
643 : 4 : }
644 : :
645 : : /* Function: al_draw_filled_pieslice
646 : : */
647 : 6 : void al_draw_filled_pieslice(float cx, float cy, float r, float start_theta,
648 : : float delta_theta, ALLEGRO_COLOR color)
649 : : {
650 : : LOCAL_VERTEX_CACHE;
651 : 6 : float scale = get_scale();
652 : : int num_segments, ii;
653 : :
654 [ - + ][ # # ]: 6 : ASSERT(r >= 0);
[ # # ]
655 : :
656 : 6 : num_segments = fabs(delta_theta / (2 * ALLEGRO_PI) * ALLEGRO_PRIM_QUALITY * scale * sqrtf(r));
657 : :
658 [ - + ]: 6 : if (num_segments < 2)
659 : 0 : num_segments = 2;
660 : :
661 [ - + ]: 6 : if (num_segments + 1 >= ALLEGRO_VERTEX_CACHE_SIZE) {
662 : 0 : num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1 - 1;
663 : : }
664 : :
665 : 6 : al_calculate_arc(&(vertex_cache[1].x), sizeof(ALLEGRO_VERTEX), cx, cy, r, r, start_theta, delta_theta, 0, num_segments);
666 : 6 : vertex_cache[0].x = cx; vertex_cache[0].y = cy;
667 : :
668 [ + + ]: 210 : for (ii = 0; ii < num_segments + 1; ii++) {
669 : 204 : vertex_cache[ii].color = color;
670 : 204 : vertex_cache[ii].z = 0;
671 : : }
672 : :
673 : 6 : al_draw_prim(vertex_cache, 0, 0, 0, num_segments + 1, ALLEGRO_PRIM_TRIANGLE_FAN);
674 : 6 : }
675 : :
676 : : /* Function: al_draw_ellipse
677 : : */
678 : 22 : void al_draw_ellipse(float cx, float cy, float rx, float ry,
679 : : ALLEGRO_COLOR color, float thickness)
680 : : {
681 : : LOCAL_VERTEX_CACHE;
682 : 22 : float scale = get_scale();
683 : :
684 [ - + ][ # # ]: 22 : ASSERT(rx >= 0);
[ # # ]
685 [ - + ][ # # ]: 22 : ASSERT(ry >= 0);
[ # # ]
686 : :
687 [ + + ]: 22 : if (thickness > 0) {
688 : 20 : int num_segments = ALLEGRO_PRIM_QUALITY * scale * sqrtf((rx + ry) / 2.0f);
689 : : int ii;
690 : :
691 : : /* In case rx and ry are both 0. */
692 [ + + ]: 20 : if (num_segments < 2)
693 : : return;
694 : :
695 [ + + ]: 18 : if (2 * num_segments >= ALLEGRO_VERTEX_CACHE_SIZE) {
696 : 2 : num_segments = (ALLEGRO_VERTEX_CACHE_SIZE - 1) / 2;
697 : : }
698 : :
699 : 18 : al_calculate_arc(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), cx, cy, rx, ry, 0, ALLEGRO_PI * 2, thickness, num_segments);
700 [ + + ]: 4062 : for (ii = 0; ii < 2 * num_segments; ii++) {
701 : 4044 : vertex_cache[ii].color = color;
702 : 4044 : vertex_cache[ii].z = 0;
703 : : }
704 : :
705 : 18 : al_draw_prim(vertex_cache, 0, 0, 0, 2 * num_segments, ALLEGRO_PRIM_TRIANGLE_STRIP);
706 : : } else {
707 : 2 : int num_segments = ALLEGRO_PRIM_QUALITY * scale * sqrtf((rx + ry) / 2.0f);
708 : : int ii;
709 : :
710 : : /* In case rx and ry are both 0. */
711 [ + - ]: 2 : if (num_segments < 2)
712 : : return;
713 : :
714 [ - + ]: 2 : if (num_segments >= ALLEGRO_VERTEX_CACHE_SIZE) {
715 : 0 : num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1;
716 : : }
717 : :
718 : 2 : al_calculate_arc(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), cx, cy, rx, ry, 0, ALLEGRO_PI * 2, 0, num_segments);
719 [ + + ]: 226 : for (ii = 0; ii < num_segments; ii++) {
720 : 224 : vertex_cache[ii].color = color;
721 : 224 : vertex_cache[ii].z = 0;
722 : : }
723 : :
724 : 22 : al_draw_prim(vertex_cache, 0, 0, 0, num_segments - 1, ALLEGRO_PRIM_LINE_LOOP);
725 : : }
726 : : }
727 : :
728 : : /* Function: al_draw_filled_ellipse
729 : : */
730 : 12 : void al_draw_filled_ellipse(float cx, float cy, float rx, float ry,
731 : : ALLEGRO_COLOR color)
732 : : {
733 : : LOCAL_VERTEX_CACHE;
734 : : int num_segments, ii;
735 : 12 : float scale = get_scale();
736 : :
737 [ - + ][ # # ]: 12 : ASSERT(rx >= 0);
[ # # ]
738 [ - + ][ # # ]: 12 : ASSERT(ry >= 0);
[ # # ]
739 : :
740 : 12 : num_segments = ALLEGRO_PRIM_QUALITY * scale * sqrtf((rx + ry) / 2.0f);
741 : :
742 : : /* In case rx and ry are both close to 0. If al_calculate_arc is passed
743 : : * 0 or 1 it will assert.
744 : : */
745 [ + - ]: 12 : if (num_segments < 2)
746 : 12 : return;
747 : :
748 [ - + ]: 12 : if (num_segments >= ALLEGRO_VERTEX_CACHE_SIZE) {
749 : 0 : num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1;
750 : : }
751 : :
752 : 12 : al_calculate_arc(&(vertex_cache[1].x), sizeof(ALLEGRO_VERTEX), cx, cy, rx, ry, 0, ALLEGRO_PI * 2, 0, num_segments);
753 : 12 : vertex_cache[0].x = cx; vertex_cache[0].y = cy;
754 : :
755 [ + + ]: 1026 : for (ii = 0; ii < num_segments + 1; ii++) {
756 : 1014 : vertex_cache[ii].color = color;
757 : 1014 : vertex_cache[ii].z = 0;
758 : : }
759 : :
760 : 12 : al_draw_prim(vertex_cache, 0, 0, 0, num_segments + 1, ALLEGRO_PRIM_TRIANGLE_FAN);
761 : : }
762 : :
763 : : /* Function: al_draw_circle
764 : : */
765 : 4 : void al_draw_circle(float cx, float cy, float r, ALLEGRO_COLOR color,
766 : : float thickness)
767 : : {
768 : 4 : al_draw_ellipse(cx, cy, r, r, color, thickness);
769 : 4 : }
770 : :
771 : : /* Function: al_draw_filled_circle
772 : : */
773 : 2 : void al_draw_filled_circle(float cx, float cy, float r, ALLEGRO_COLOR color)
774 : : {
775 : 2 : al_draw_filled_ellipse(cx, cy, r, r, color);
776 : 2 : }
777 : :
778 : : /* Function: al_draw_elliptical_arc
779 : : */
780 : 34 : void al_draw_elliptical_arc(float cx, float cy, float rx, float ry, float start_theta,
781 : : float delta_theta, ALLEGRO_COLOR color, float thickness)
782 : : {
783 : : LOCAL_VERTEX_CACHE;
784 : 34 : float scale = get_scale();
785 : :
786 [ + - ][ - + ]: 34 : ASSERT(rx >= 0 && ry >= 0);
[ # # ][ # # ]
[ # # ]
787 [ + + ]: 34 : if (thickness > 0) {
788 : 30 : int num_segments = fabs(delta_theta / (2 * ALLEGRO_PI) * ALLEGRO_PRIM_QUALITY * scale * sqrtf((rx + ry) / 2.0f));
789 : : int ii;
790 : :
791 [ + + ]: 30 : if (num_segments < 2)
792 : 2 : num_segments = 2;
793 : :
794 [ - + ]: 30 : if (2 * num_segments >= ALLEGRO_VERTEX_CACHE_SIZE) {
795 : 0 : num_segments = (ALLEGRO_VERTEX_CACHE_SIZE - 1) / 2;
796 : : }
797 : :
798 : 30 : al_calculate_arc(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), cx, cy, rx, ry, start_theta, delta_theta, thickness, num_segments);
799 : :
800 [ + + ]: 3514 : for (ii = 0; ii < 2 * num_segments; ii++) {
801 : 3484 : vertex_cache[ii].color = color;
802 : 3484 : vertex_cache[ii].z = 0;
803 : : }
804 : :
805 : 30 : al_draw_prim(vertex_cache, 0, 0, 0, 2 * num_segments, ALLEGRO_PRIM_TRIANGLE_STRIP);
806 : : } else {
807 : 4 : int num_segments = fabs(delta_theta / (2 * ALLEGRO_PI) * ALLEGRO_PRIM_QUALITY * scale * sqrtf((rx + ry) / 2.0f));
808 : : int ii;
809 : :
810 [ - + ]: 4 : if (num_segments < 2)
811 : 0 : num_segments = 2;
812 : :
813 [ - + ]: 4 : if (num_segments >= ALLEGRO_VERTEX_CACHE_SIZE) {
814 : 0 : num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1;
815 : : }
816 : :
817 : 4 : al_calculate_arc(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), cx, cy, rx, ry, start_theta, delta_theta, 0, num_segments);
818 : :
819 [ + + ]: 234 : for (ii = 0; ii < num_segments; ii++) {
820 : 230 : vertex_cache[ii].color = color;
821 : 230 : vertex_cache[ii].z = 0;
822 : : }
823 : :
824 : 4 : al_draw_prim(vertex_cache, 0, 0, 0, num_segments, ALLEGRO_PRIM_LINE_STRIP);
825 : : }
826 : 34 : }
827 : :
828 : : /* Function: al_draw_arc
829 : : */
830 : 22 : void al_draw_arc(float cx, float cy, float r, float start_theta,
831 : : float delta_theta, ALLEGRO_COLOR color, float thickness)
832 : : {
833 : 22 : al_draw_elliptical_arc(cx, cy, r, r, start_theta, delta_theta, color, thickness);
834 : 22 : }
835 : :
836 : : /* Function: al_draw_rounded_rectangle
837 : : */
838 : 18 : void al_draw_rounded_rectangle(float x1, float y1, float x2, float y2,
839 : : float rx, float ry, ALLEGRO_COLOR color, float thickness)
840 : : {
841 : : LOCAL_VERTEX_CACHE;
842 : 18 : float scale = get_scale();
843 : :
844 [ - + ][ # # ]: 18 : ASSERT(rx >= 0);
[ # # ]
845 [ - + ][ # # ]: 18 : ASSERT(ry >= 0);
[ # # ]
846 : :
847 [ + + ]: 18 : if (thickness > 0) {
848 : 16 : int num_segments = ALLEGRO_PRIM_QUALITY * scale * sqrtf((rx + ry) / 2.0f) / 4;
849 : : int ii;
850 : :
851 : : /* In case rx and ry are both 0. */
852 [ + + ]: 16 : if (num_segments < 2) {
853 : 2 : al_draw_rectangle(x1, y1, x2, y2, color, thickness);
854 : : return;
855 : : }
856 : :
857 [ - + ]: 14 : if (8 * num_segments + 2 >= ALLEGRO_VERTEX_CACHE_SIZE) {
858 : 0 : num_segments = (ALLEGRO_VERTEX_CACHE_SIZE - 3) / 8;
859 : : }
860 : :
861 : 14 : al_calculate_arc(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), 0, 0, rx, ry, 0, ALLEGRO_PI / 2, thickness, num_segments);
862 : :
863 [ + + ]: 238 : for (ii = 0; ii < 2 * num_segments; ii += 2) {
864 : 224 : vertex_cache[ii + 2 * num_segments + 1].x = x1 + rx - vertex_cache[2 * num_segments - 1 - ii].x;
865 : 224 : vertex_cache[ii + 2 * num_segments + 1].y = y1 + ry - vertex_cache[2 * num_segments - 1 - ii].y;
866 : 224 : vertex_cache[ii + 2 * num_segments].x = x1 + rx - vertex_cache[2 * num_segments - 1 - ii - 1].x;
867 : 224 : vertex_cache[ii + 2 * num_segments].y = y1 + ry - vertex_cache[2 * num_segments - 1 - ii - 1].y;
868 : :
869 : 224 : vertex_cache[ii + 4 * num_segments].x = x1 + rx - vertex_cache[ii].x;
870 : 224 : vertex_cache[ii + 4 * num_segments].y = y2 - ry + vertex_cache[ii].y;
871 : 224 : vertex_cache[ii + 4 * num_segments + 1].x = x1 + rx - vertex_cache[ii + 1].x;
872 : 224 : vertex_cache[ii + 4 * num_segments + 1].y = y2 - ry + vertex_cache[ii + 1].y;
873 : :
874 : 224 : vertex_cache[ii + 6 * num_segments + 1].x = x2 - rx + vertex_cache[2 * num_segments - 1 - ii].x;
875 : 224 : vertex_cache[ii + 6 * num_segments + 1].y = y2 - ry + vertex_cache[2 * num_segments - 1 - ii].y;
876 : 224 : vertex_cache[ii + 6 * num_segments].x = x2 - rx + vertex_cache[2 * num_segments - 1 - ii - 1].x;
877 : 224 : vertex_cache[ii + 6 * num_segments].y = y2 - ry + vertex_cache[2 * num_segments - 1 - ii - 1].y;
878 : : }
879 [ + + ]: 238 : for (ii = 0; ii < 2 * num_segments; ii += 2) {
880 : 224 : vertex_cache[ii].x = x2 - rx + vertex_cache[ii].x;
881 : 224 : vertex_cache[ii].y = y1 + ry - vertex_cache[ii].y;
882 : 224 : vertex_cache[ii + 1].x = x2 - rx + vertex_cache[ii + 1].x;
883 : 224 : vertex_cache[ii + 1].y = y1 + ry - vertex_cache[ii + 1].y;
884 : : }
885 : 14 : vertex_cache[8 * num_segments] = vertex_cache[0];
886 : 14 : vertex_cache[8 * num_segments + 1] = vertex_cache[1];
887 : :
888 [ + + ]: 1834 : for (ii = 0; ii < 8 * num_segments + 2; ii++) {
889 : 1820 : vertex_cache[ii].color = color;
890 : 1820 : vertex_cache[ii].z = 0;
891 : : }
892 : :
893 : 14 : al_draw_prim(vertex_cache, 0, 0, 0, 8 * num_segments + 2, ALLEGRO_PRIM_TRIANGLE_STRIP);
894 : : } else {
895 : 2 : int num_segments = ALLEGRO_PRIM_QUALITY * scale * sqrtf((rx + ry) / 2.0f) / 4;
896 : : int ii;
897 : :
898 : : /* In case rx and ry are both 0. */
899 [ - + ]: 2 : if (num_segments < 2) {
900 : 0 : al_draw_rectangle(x1, y1, x2, y2, color, thickness);
901 : : return;
902 : : }
903 : :
904 [ - + ]: 2 : if (num_segments * 4 >= ALLEGRO_VERTEX_CACHE_SIZE) {
905 : 0 : num_segments = (ALLEGRO_VERTEX_CACHE_SIZE - 1) / 4;
906 : : }
907 : :
908 : 2 : al_calculate_arc(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), 0, 0, rx, ry, 0, ALLEGRO_PI / 2, 0, num_segments + 1);
909 : :
910 [ + + ]: 34 : for (ii = 0; ii < num_segments; ii++) {
911 : 32 : vertex_cache[ii + 1 * num_segments].x = x1 + rx - vertex_cache[num_segments - 1 - ii].x;
912 : 32 : vertex_cache[ii + 1 * num_segments].y = y1 + ry - vertex_cache[num_segments - 1 - ii].y;
913 : :
914 : 32 : vertex_cache[ii + 2 * num_segments].x = x1 + rx - vertex_cache[ii].x;
915 : 32 : vertex_cache[ii + 2 * num_segments].y = y2 - ry + vertex_cache[ii].y;
916 : :
917 : 32 : vertex_cache[ii + 3 * num_segments].x = x2 - rx + vertex_cache[num_segments - 1 - ii].x;
918 : 32 : vertex_cache[ii + 3 * num_segments].y = y2 - ry + vertex_cache[num_segments - 1 - ii].y;
919 : : }
920 [ + + ]: 34 : for (ii = 0; ii < num_segments; ii++) {
921 : 32 : vertex_cache[ii].x = x2 - rx + vertex_cache[ii].x;
922 : 32 : vertex_cache[ii].y = y1 + ry - vertex_cache[ii].y;
923 : : }
924 : :
925 [ + + ]: 130 : for (ii = 0; ii < 4 * num_segments; ii++) {
926 : 128 : vertex_cache[ii].color = color;
927 : 128 : vertex_cache[ii].z = 0;
928 : : }
929 : :
930 : 18 : al_draw_prim(vertex_cache, 0, 0, 0, 4 * num_segments, ALLEGRO_PRIM_LINE_LOOP);
931 : : }
932 : : }
933 : :
934 : : /* Function: al_draw_filled_rounded_rectangle
935 : : */
936 : 12 : void al_draw_filled_rounded_rectangle(float x1, float y1, float x2, float y2,
937 : : float rx, float ry, ALLEGRO_COLOR color)
938 : : {
939 : : LOCAL_VERTEX_CACHE;
940 : : int ii;
941 : 12 : float scale = get_scale();
942 : 12 : int num_segments = ALLEGRO_PRIM_QUALITY * scale * sqrtf((rx + ry) / 2.0f) / 4;
943 : :
944 [ - + ][ # # ]: 12 : ASSERT(rx >= 0);
[ # # ]
945 [ - + ][ # # ]: 12 : ASSERT(ry >= 0);
[ # # ]
946 : :
947 : : /* In case rx and ry are both 0. */
948 [ + + ]: 12 : if (num_segments < 2) {
949 : 2 : al_draw_filled_rectangle(x1, y1, x2, y2, color);
950 : 12 : return;
951 : : }
952 : :
953 [ - + ]: 10 : if (num_segments * 4 >= ALLEGRO_VERTEX_CACHE_SIZE) {
954 : 0 : num_segments = (ALLEGRO_VERTEX_CACHE_SIZE - 1) / 4;
955 : : }
956 : :
957 : 10 : al_calculate_arc(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), 0, 0, rx, ry, 0, ALLEGRO_PI / 2, 0, num_segments + 1);
958 : :
959 [ + + ]: 150 : for (ii = 0; ii < num_segments; ii++) {
960 : 140 : vertex_cache[ii + 1 * num_segments].x = x1 + rx - vertex_cache[num_segments - 1 - ii].x;
961 : 140 : vertex_cache[ii + 1 * num_segments].y = y1 + ry - vertex_cache[num_segments - 1 - ii].y;
962 : :
963 : 140 : vertex_cache[ii + 2 * num_segments].x = x1 + rx - vertex_cache[ii].x;
964 : 140 : vertex_cache[ii + 2 * num_segments].y = y2 - ry + vertex_cache[ii].y;
965 : :
966 : 140 : vertex_cache[ii + 3 * num_segments].x = x2 - rx + vertex_cache[num_segments - 1 - ii].x;
967 : 140 : vertex_cache[ii + 3 * num_segments].y = y2 - ry + vertex_cache[num_segments - 1 - ii].y;
968 : : }
969 [ + + ]: 150 : for (ii = 0; ii < num_segments; ii++) {
970 : 140 : vertex_cache[ii].x = x2 - rx + vertex_cache[ii].x;
971 : 140 : vertex_cache[ii].y = y1 + ry - vertex_cache[ii].y;
972 : : }
973 : :
974 [ + + ]: 570 : for (ii = 0; ii < 4 * num_segments; ii++) {
975 : 560 : vertex_cache[ii].color = color;
976 : 560 : vertex_cache[ii].z = 0;
977 : : }
978 : :
979 : : /*
980 : : TODO: Doing this as a triangle fan just doesn't sound all that great, perhaps shuffle the vertices somehow to at least make it a strip
981 : : */
982 : 10 : al_draw_prim(vertex_cache, 0, 0, 0, 4 * num_segments, ALLEGRO_PRIM_TRIANGLE_FAN);
983 : : }
984 : :
985 : : /* Function: al_calculate_spline
986 : : */
987 : 16 : void al_calculate_spline(float* dest, int stride, float points[8],
988 : : float thickness, int num_segments)
989 : : {
990 : : /* Derivatives of x(t) and y(t). */
991 : : float x, dx, ddx, dddx;
992 : : float y, dy, ddy, dddy;
993 : 16 : int ii = 0;
994 : :
995 : : /* Temp variables used in the setup. */
996 : : float dt, dt2, dt3;
997 : : float xdt2_term, xdt3_term;
998 : : float ydt2_term, ydt3_term;
999 : :
1000 : : /* This is enough to avoid malloc in ex_prim, which I take as a reasonable
1001 : : * guess to what a common number of segments might be. To be honest, it
1002 : : * probably makes no difference.
1003 : : */
1004 : : float cache_point_buffer_storage[150];
1005 : 16 : float* cache_point_buffer = cache_point_buffer_storage;
1006 : :
1007 [ - + ][ # # ]: 16 : ASSERT(num_segments > 1);
[ # # ]
1008 [ - + ][ # # ]: 16 : ASSERT(points);
[ # # ]
1009 : :
1010 [ - + ]: 16 : if (num_segments > (int)(sizeof(cache_point_buffer_storage) / sizeof(float) / 2)) {
1011 : 0 : cache_point_buffer = al_malloc(2 * sizeof(float) * num_segments);
1012 : : }
1013 : :
1014 : 16 : dt = 1.0 / (num_segments - 1);
1015 : 16 : dt2 = (dt * dt);
1016 : 16 : dt3 = (dt2 * dt);
1017 : :
1018 : : /* x coordinates. */
1019 : 16 : xdt2_term = 3 * (points[4] - 2 * points[2] + points[0]);
1020 : 16 : xdt3_term = points[6] + 3 * (-points[4] + points[2]) - points[0];
1021 : :
1022 : 16 : xdt2_term = dt2 * xdt2_term;
1023 : 16 : xdt3_term = dt3 * xdt3_term;
1024 : :
1025 : 16 : dddx = 6 * xdt3_term;
1026 : 16 : ddx = -6 * xdt3_term + 2 * xdt2_term;
1027 : 16 : dx = xdt3_term - xdt2_term + 3 * dt * (points[2] - points[0]);
1028 : 16 : x = points[0];
1029 : :
1030 : : /* y coordinates. */
1031 : 16 : ydt2_term = 3 * (points[5] - 2 * points[3] + points[1]);
1032 : 16 : ydt3_term = points[7] + 3 * (-points[5] + points[3]) - points[1];
1033 : :
1034 : 16 : ydt2_term = dt2 * ydt2_term;
1035 : 16 : ydt3_term = dt3 * ydt3_term;
1036 : :
1037 : 16 : dddy = 6 * ydt3_term;
1038 : 16 : ddy = -6 * ydt3_term + 2 * ydt2_term;
1039 : 16 : dy = ydt3_term - ydt2_term + dt * 3 * (points[3] - points[1]);
1040 : 16 : y = points[1];
1041 : :
1042 : 16 : cache_point_buffer[2 * ii] = x;
1043 : 16 : cache_point_buffer[2 * ii + 1] = y;
1044 : :
1045 [ + + ]: 848 : for (ii = 1; ii < num_segments; ii++) {
1046 : 832 : ddx += dddx;
1047 : 832 : dx += ddx;
1048 : 832 : x += dx;
1049 : :
1050 : 832 : ddy += dddy;
1051 : 832 : dy += ddy;
1052 : 832 : y += dy;
1053 : :
1054 : 832 : cache_point_buffer[2 * ii] = x;
1055 : 832 : cache_point_buffer[2 * ii + 1] = y;
1056 : : }
1057 : 16 : al_calculate_ribbon(dest, stride, cache_point_buffer, 2 * sizeof(float), thickness, num_segments);
1058 : :
1059 [ - + ]: 16 : if (cache_point_buffer != cache_point_buffer_storage) {
1060 : 0 : al_free(cache_point_buffer);
1061 : : }
1062 : 16 : }
1063 : :
1064 : : /* Function: al_draw_spline
1065 : : */
1066 : 16 : void al_draw_spline(float points[8], ALLEGRO_COLOR color, float thickness)
1067 : : {
1068 : : int ii;
1069 : 16 : float scale = get_scale();
1070 : 64 : int num_segments = (int)(sqrtf(hypotf(points[2] - points[0], points[3] - points[1]) +
1071 : 16 : hypotf(points[4] - points[2], points[5] - points[3]) +
1072 : 32 : hypotf(points[6] - points[4], points[7] - points[5])) *
1073 : 16 : 1.2 * ALLEGRO_PRIM_QUALITY * scale / 10);
1074 : : LOCAL_VERTEX_CACHE;
1075 : :
1076 [ - + ]: 16 : if(num_segments < 2)
1077 : 0 : num_segments = 2;
1078 : :
1079 [ + + ]: 16 : if (thickness > 0) {
1080 [ - + ]: 14 : if (2 * num_segments >= ALLEGRO_VERTEX_CACHE_SIZE) {
1081 : 0 : num_segments = (ALLEGRO_VERTEX_CACHE_SIZE - 1) / 2;
1082 : : }
1083 : :
1084 : 14 : al_calculate_spline(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), points, thickness, num_segments);
1085 : :
1086 [ + + ]: 1498 : for (ii = 0; ii < 2 * num_segments; ii++) {
1087 : 1484 : vertex_cache[ii].color = color;
1088 : 1484 : vertex_cache[ii].z = 0;
1089 : : }
1090 : :
1091 : 14 : al_draw_prim(vertex_cache, 0, 0, 0, 2 * num_segments, ALLEGRO_PRIM_TRIANGLE_STRIP);
1092 : : } else {
1093 [ - + ]: 2 : if (num_segments >= ALLEGRO_VERTEX_CACHE_SIZE) {
1094 : 0 : num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1;
1095 : : }
1096 : :
1097 : 2 : al_calculate_spline(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), points, thickness, num_segments);
1098 : :
1099 [ + + ]: 108 : for (ii = 0; ii < num_segments; ii++) {
1100 : 106 : vertex_cache[ii].color = color;
1101 : 106 : vertex_cache[ii].z = 0;
1102 : : }
1103 : :
1104 : 2 : al_draw_prim(vertex_cache, 0, 0, 0, num_segments, ALLEGRO_PRIM_LINE_STRIP);
1105 : : }
1106 : 16 : }
1107 : :
1108 : : /* Function: al_calculate_ribbon
1109 : : */
1110 : 16 : void al_calculate_ribbon(float* dest, int dest_stride, const float *points,
1111 : : int points_stride, float thickness, int num_segments)
1112 : : {
1113 [ - + ][ # # ]: 16 : ASSERT(points);
[ # # ]
1114 [ - + ][ # # ]: 16 : ASSERT(num_segments >= 2);
[ # # ]
1115 : :
1116 [ + + ]: 16 : if (thickness > 0) {
1117 : 14 : int ii = 0;
1118 : : float x, y;
1119 : :
1120 : : float cur_dir_x;
1121 : : float cur_dir_y;
1122 : 14 : float prev_dir_x = 0;
1123 : 14 : float prev_dir_y = 0;
1124 : 14 : float t = thickness / 2;
1125 : : float tx, ty;
1126 : : float nx, ny;
1127 : 14 : float sign = 1;
1128 : :
1129 [ + + ]: 742 : for (ii = 0; ii < 2 * num_segments - 2; ii += 2) {
1130 : : float dir_len;
1131 : 728 : x = *points;
1132 : 728 : y = *(points + 1);
1133 : :
1134 : 728 : points = (float*)(((char*)points) + points_stride);
1135 : :
1136 : 728 : cur_dir_x = *(points) - x;
1137 : 728 : cur_dir_y = *(points + 1) - y;
1138 : :
1139 : 728 : dir_len = hypotf(cur_dir_x, cur_dir_y);
1140 : :
1141 [ + - ]: 728 : if(dir_len > 0.000001f) {
1142 : 728 : cur_dir_x /= dir_len;
1143 : 728 : cur_dir_y /= dir_len;
1144 [ # # ]: 0 : } else if (ii == 0) {
1145 : : cur_dir_x = 1;
1146 : : cur_dir_y = 0;
1147 : : } else {
1148 : 0 : cur_dir_x = prev_dir_x;
1149 : 0 : cur_dir_y = prev_dir_y;
1150 : : }
1151 : :
1152 [ + + ]: 728 : if (ii == 0) {
1153 : 14 : tx = -t * cur_dir_y;
1154 : 14 : ty = t * cur_dir_x;
1155 : 14 : nx = 0;
1156 : 14 : ny = 0;
1157 : : } else {
1158 : 714 : float dot = cur_dir_x * prev_dir_x + cur_dir_y * prev_dir_y;
1159 : : float norm_len, cosine;
1160 [ - + ]: 714 : if(dot < 0) {
1161 : : /*
1162 : : * This is by no means exact, but seems to produce acceptable results
1163 : : */
1164 : : float tx_;
1165 : 0 : tx = cur_dir_x - prev_dir_x;
1166 : 0 : ty = cur_dir_y - prev_dir_y;
1167 : 0 : norm_len = hypotf(tx, ty);
1168 : :
1169 : 0 : tx /= norm_len;
1170 : 0 : ty /= norm_len;
1171 : :
1172 : 0 : cosine = tx * cur_dir_x + ty * cur_dir_y;
1173 : :
1174 : 0 : nx = -t * tx / cosine;
1175 : 0 : ny = -t * ty / cosine;
1176 : 0 : tx_ = tx;
1177 : 0 : tx = -t * ty * cosine;
1178 : 0 : ty = t * tx_ * cosine;
1179 : 0 : sign = -sign;
1180 : : } else {
1181 : : float new_norm_len;
1182 : 714 : tx = cur_dir_y + prev_dir_y;
1183 : 714 : ty = -(cur_dir_x + prev_dir_x);
1184 : 714 : norm_len = hypotf(tx, ty);
1185 : :
1186 : 714 : tx /= norm_len;
1187 : 714 : ty /= norm_len;
1188 : 714 : cosine = tx * (-cur_dir_y) + ty * (cur_dir_x);
1189 : 714 : new_norm_len = t / cosine;
1190 : :
1191 : 714 : tx *= new_norm_len;
1192 : 714 : ty *= new_norm_len;
1193 : 714 : nx = 0;
1194 : 714 : ny = 0;
1195 : : }
1196 : : }
1197 : :
1198 : 728 : *dest = x - sign * tx + nx;
1199 : 728 : *(dest + 1) = y - sign * ty + ny;
1200 : 728 : dest = (float*)(((char*)dest) + dest_stride);
1201 : 728 : *dest = x + sign * tx + nx;
1202 : 728 : *(dest + 1) = y + sign * ty + ny;
1203 : 728 : dest = (float*)(((char*)dest) + dest_stride);
1204 : :
1205 : 728 : prev_dir_x = cur_dir_x;
1206 : 728 : prev_dir_y = cur_dir_y;
1207 : : }
1208 : 14 : tx = -t * prev_dir_y;
1209 : 14 : ty = t * prev_dir_x;
1210 : :
1211 : 14 : x = *points;
1212 : 14 : y = *(points + 1);
1213 : :
1214 : 14 : *dest = x - sign * tx;
1215 : 14 : *(dest + 1) = y - sign * ty;
1216 : 14 : dest = (float*)(((char*)dest) + dest_stride);
1217 : 14 : *dest = x + sign * tx;
1218 : 14 : *(dest + 1) = y + sign * ty;
1219 : : } else {
1220 : : int ii;
1221 [ + + ]: 108 : for (ii = 0; ii < num_segments; ii++) {
1222 : 106 : *dest = *points;
1223 : 106 : *(dest + 1) = *(points + 1);
1224 : 106 : dest = (float*)(((char*)dest) + dest_stride);
1225 : 106 : points = (float*)(((char*)points) + points_stride);
1226 : : }
1227 : : }
1228 : 16 : }
1229 : :
1230 : : /* Function: al_draw_ribbon
1231 : : */
1232 : 0 : void al_draw_ribbon(const float *points, int points_stride, ALLEGRO_COLOR color,
1233 : : float thickness, int num_segments)
1234 : : {
1235 : : LOCAL_VERTEX_CACHE;
1236 : : int ii;
1237 : :
1238 : 0 : al_calculate_ribbon(&(vertex_cache[0].x), sizeof(ALLEGRO_VERTEX), points, points_stride, thickness, num_segments);
1239 : :
1240 [ # # ]: 0 : if (thickness > 0) {
1241 [ # # ]: 0 : for (ii = 0; ii < 2 * num_segments; ii++) {
1242 : 0 : vertex_cache[ii].color = color;
1243 : 0 : vertex_cache[ii].z = 0;
1244 : : }
1245 : :
1246 : 0 : al_draw_prim(vertex_cache, 0, 0, 0, 2 * num_segments, ALLEGRO_PRIM_TRIANGLE_STRIP);
1247 : : } else {
1248 [ # # ]: 0 : for (ii = 0; ii < num_segments; ii++) {
1249 : 0 : vertex_cache[ii].color = color;
1250 : 0 : vertex_cache[ii].z = 0;
1251 : : }
1252 : :
1253 : 0 : al_draw_prim(vertex_cache, 0, 0, 0, num_segments, ALLEGRO_PRIM_LINE_STRIP);
1254 : : }
1255 : 0 : }
1256 : :
1257 : : /* vim: set sts=3 sw=3 et: */
|