/* * Emits square cap. * * Square cap is an rectangle with edges lengths equal to radius * and double radius, first along direction second along normal * direction. */ static void emit_square_end_cap(ALLEGRO_PRIM_VERTEX_CACHE* cache, const float* pivot, const float* dir, const float* normal, float radius) { /* Prepare all four vertices of the rectangle. */ float v0[2] = { pivot[0] + normal[0] * radius, pivot[1] + normal[1] * radius }; float v1[2] = { pivot[0] - normal[0] * radius, pivot[1] - normal[1] * radius }; float v2[2] = { v0[0] + dir[0] * radius, v0[1] + dir[1] * radius }; float v3[2] = { v1[0] + dir[0] * radius, v1[1] + dir[1] * radius }; /* Emit. */ _al_prim_cache_push_triangle(cache, v0, v2, v3); _al_prim_cache_push_triangle(cache, v0, v3, v1); }
static void polygon_push_triangle_callback(int i0, int i1, int i2, void* user_data) { ALLEGRO_PRIM_VERTEX_CACHE* cache = (ALLEGRO_PRIM_VERTEX_CACHE*)user_data; const float* vertices = (const float*)cache->user_data; const float* v0 = vertices + (i0 * 2); const float* v1 = vertices + (i1 * 2); const float* v2 = vertices + (i2 * 2); _al_prim_cache_push_triangle(cache, v0, v1, v2); }
/* * Emits miter join. */ static void emit_miter_join(ALLEGRO_PRIM_VERTEX_CACHE* cache, const float* pivot, const float* p0, const float* p1, float radius, const float* middle, float angle, float miter_distance, float max_miter_distance) { /* XXX delete this parameter? */ (void)radius; if (miter_distance > max_miter_distance) { float normal[2] = { -middle[1], middle[0] }; float offset = (miter_distance - max_miter_distance) * tanf((ALLEGRO_PI - fabsf(angle)) * 0.5f); float v0[2] = { pivot[0] + middle[0] * max_miter_distance + normal[0] * offset, pivot[1] + middle[1] * max_miter_distance + normal[1] * offset }; float v1[2] = { pivot[0] + middle[0] * max_miter_distance - normal[0] * offset, pivot[1] + middle[1] * max_miter_distance - normal[1] * offset }; _al_prim_cache_push_triangle(cache, pivot, v0, v1); _al_prim_cache_push_triangle(cache, pivot, p0, v0); _al_prim_cache_push_triangle(cache, pivot, p1, v1); } else { float miter[2] = { pivot[0] + middle[0] * miter_distance, pivot[1] + middle[1] * miter_distance, }; _al_prim_cache_push_triangle(cache, pivot, p0, miter); _al_prim_cache_push_triangle(cache, pivot, miter, p1); } }
/* * Emits filled arc. * * Arc is defined by pivot point, radius, start and end angle. * Starting and ending angle are wrapped to two pi range. */ static void emit_arc(ALLEGRO_PRIM_VERTEX_CACHE* cache, const float* pivot, float start, float end, float radius, int segments) { float step, angle, arc; float v0[2]; float v1[2]; int i; /* This is very small arc, we will draw nothing. */ if (fabsf(end - start) < 0.001f) return; /* Make sure start both start angle is located in the * range [0, 2 * pi) and end angle is greater than * start angle. */ start = fmodf(start, ALLEGRO_PI * 2.0f); end = fmodf(end, ALLEGRO_PI * 2.0f); if (end <= start) end += ALLEGRO_PI * 2.0f; arc = end - start; segments = (int)(segments * arc / ALLEGRO_PI * 2.0f); if (segments < 1) segments = 1; step = arc / segments; angle = start; v0[0] = pivot[0] + cosf(angle) * radius; v0[1] = pivot[1] + sinf(angle) * radius; for (i = 0; i < segments; ++i, angle += step) { v1[0] = pivot[0] + cosf(angle + step) * radius; v1[1] = pivot[1] + sinf(angle + step) * radius; _al_prim_cache_push_triangle(cache, v0, pivot, v1); v0[0] = v1[0]; v0[1] = v1[1]; } }
static void emit_polyline(ALLEGRO_PRIM_VERTEX_CACHE* cache, const float* vertices, int vertex_stride, int vertex_count, int join_style, int cap_style, float thickness, float miter_limit) { # define VERTEX(index) ((const float*)(((uint8_t*)vertices) + vertex_stride * ((vertex_count + (index)) % vertex_count))) float l0[2], l1[2]; float r0[2], r1[2]; float p0[2], p1[2]; float radius; int steps; int i; ASSERT(thickness > 0.0f); /* Discard invalid lines. */ if (vertex_count < 2) return; radius = 0.5f * thickness; /* Single line cannot be closed. If user forgot to explicitly specify * most desired alternative cap style, we just disable capping at all. */ if (vertex_count == 2 && cap_style == ALLEGRO_LINE_CAP_CLOSED) cap_style = ALLEGRO_LINE_CAP_NONE; /* Prepare initial set of vertices. */ if (cap_style != ALLEGRO_LINE_CAP_CLOSED) { /* We can emit ending caps right now. * * VERTEX(-2) and similar are safe, because it at this place * it is guaranteed that there are at least two vertices * in the buffer. */ emit_end_cap(cache, cap_style, VERTEX(1), VERTEX(0), radius); emit_end_cap(cache, cap_style, VERTEX(-2), VERTEX(-1), radius); /* Compute points on the left side of the very first segment. */ compute_end_cross_points(VERTEX(1), VERTEX(0), radius, p1, p0); /* For non-closed line we have N - 1 steps, but since we iterate * from one, N is right value. */ steps = vertex_count; } else { /* Compute points on the left side of the very first segment. */ compute_cross_points(VERTEX(-1), VERTEX(0), VERTEX(1), radius, l0, l1, p0, p1, NULL, NULL, NULL); /* Closed line use N steps, because last vertex have to be * connected with first one. */ steps = vertex_count + 1; } /* Process segments. */ for (i = 1; i < steps; ++i) { /* Pick vertex and their neighbors. */ const float* v0 = VERTEX(i - 1); const float* v1 = VERTEX(i); const float* v2 = VERTEX(i + 1); /* Choose correct cross points. */ if ((cap_style == ALLEGRO_LINE_CAP_CLOSED) || (i < steps - 1)) { float middle[2]; float miter_distance; float angle; /* Compute cross points. */ compute_cross_points(v0, v1, v2, radius, l0, l1, r0, r1, middle, &angle, &miter_distance); /* Emit join. */ if (angle >= 0.0f) emit_join(cache, join_style, v1, l0, r0, radius, middle, angle, miter_distance, miter_limit); else emit_join(cache, join_style, v1, r1, l1, radius, middle, angle, miter_distance, miter_limit); } else compute_end_cross_points(v0, v1, radius, l0, l1); /* Emit triangles. */ _al_prim_cache_push_triangle(cache, v0, v1, l1); _al_prim_cache_push_triangle(cache, v0, l1, p1); _al_prim_cache_push_triangle(cache, v0, p0, l0); _al_prim_cache_push_triangle(cache, v0, l0, v1); /* Save current most right vertices. */ memcpy(p0, r0, sizeof(float) * 2); memcpy(p1, r1, sizeof(float) * 2); } # undef VERTEX }
/* * Emits bevel join. */ static void emit_bevel_join(ALLEGRO_PRIM_VERTEX_CACHE* cache, const float* pivot, const float* p0, const float* p1) { _al_prim_cache_push_triangle(cache, pivot, p0, p1); }