static unsigned _PolygonToTriangles(const PT *points, unsigned num_points, GLushort *triangles, typename PT::scalar_type min_distance) { // no redundant start/end please if (num_points >= 1 && points[0] == points[num_points - 1]) num_points--; if (num_points < 3) return 0; assert(num_points < 65536); // next vertex pointer auto next = new GLushort[num_points]; // index of the first vertex GLushort start = 0; // initialize next pointer counterclockwise if (PolygonRotatesLeft(points, num_points)) { for (unsigned i = 0; i < num_points-1; i++) next[i] = i + 1; next[num_points - 1] = 0; } else { next[0] = num_points - 1; for (unsigned i = 1; i < num_points; i++) next[i] = i - 1; } // thinning if (min_distance > 0) { for (unsigned a = start, b = next[a], c = next[b], heat = 0; num_points > 3 && heat < num_points; a = b, b = c, c = next[c], heat++) { bool point_removeable = TriangleEmpty(points[a], points[b], points[c]); if (!point_removeable) { typename PT::scalar_type distance = ManhattanDistance(points[a], points[b]); if (distance < min_distance) { point_removeable = true; if (distance > 0) { for (unsigned p = next[c]; p != a; p = next[p]) { if (InsideTriangle(points[p], points[a], points[b], points[c])) { point_removeable = false; break; } } } } } if (point_removeable) { // remove node b from polygon if (b == start) // keep track of the smallest index start = std::min(a, c); next[a] = c; num_points--; // 'a' should stay the same in the next loop b = a; // reset heat heat = 0; } } //LogDebug(_T("polygon thinning (%u) removed %u of %u vertices"), // min_distance, orig_num_points-num_points, orig_num_points); } // triangulation auto t = triangles; for (unsigned a = start, b = next[a], c = next[b], heat = 0; num_points > 2; a = b, b = c, c = next[c]) { typename PT::product_type bendiness = LeftBend(points[a], points[b], points[c]); // left bend, spike or line with a redundant point in the middle bool ear_cuttable = (bendiness >= 0); if (bendiness > 0) { // left bend for (unsigned prev_p = c, p = next[c]; p != a; prev_p = p, p = next[p]) { typename PT::product_type ab = PointLeftOfLine(points[p], points[a], points[b]); typename PT::product_type bc = PointLeftOfLine(points[p], points[b], points[c]); typename PT::product_type ca = PointLeftOfLine(points[p], points[c], points[a]); if (ab > 0 && bc > 0 && ca > 0) { // p is inside a,b,c ear_cuttable = false; break; } else if (ab >= 0 && bc >= 0 && ca >= 0) { // p is on one or two edges of a,b,c bool outside_ab = (ab == 0) && PointLeftOfLine(points[prev_p], points[a], points[b]) <= 0; bool outside_bc = (bc == 0) && PointLeftOfLine(points[prev_p], points[b], points[c]) <= 0; bool outside_ca = (ca == 0) && PointLeftOfLine(points[prev_p], points[c], points[a]) <= 0; if (!(outside_ab || outside_bc || outside_ca)) { // line p,prev_p intersects with triangle a,b,c ear_cuttable = false; break; } outside_ab = (ab == 0) && PointLeftOfLine(points[next[p]], points[a], points[b]) <= 0; outside_bc = (bc == 0) && PointLeftOfLine(points[next[p]], points[b], points[c]) <= 0; outside_ca = (ca == 0) && PointLeftOfLine(points[next[p]], points[c], points[a]) <= 0; if (!(outside_ab || outside_bc || outside_ca)) { // line p,next[p] intersects with triangle a,b,c ear_cuttable = false; break; } } } if (ear_cuttable) { // save triangle indices *t++ = a; *t++ = b; *t++ = c; } } if (ear_cuttable) { // remove node b from polygon next[a] = c; num_points--; // 'a' should stay the same in the next loop b = a; // reset heat heat = 0; } if (heat++ > num_points) { // if polygon edges overlap we may loop endlessly //LogDebug(_T("polygon_to_triangle: bad polygon")); delete[] next; return 0; } } delete[] next; return t - triangles; }
unsigned LineToTriangles(const PT *points, unsigned num_points, AllocatedArray<PT> &strip, unsigned line_width, bool loop, bool tcap) { // A line has to have at least two points if (num_points < 2) return 0; // allocate memory for triangle vertices // max. size: 2*(num_points + (int)(loop || tcap)) strip.GrowDiscard(2 * (num_points + 1)); // A closed line path needs to have at least three points if (loop && num_points < 3) // .. otherwise don't close it loop = false; float half_line_width = line_width * 0.5f; // strip will point to the start of the output array // s is the working pointer PT *s = strip.begin(); // a, b and c point to three consecutive points which are used to iterate // through the line given in 'points'. Where b is the current position, // a the previous point and c the next point. const PT *a, *b, *c; // pointer to the end of the original points array // used for faster loop conditions const auto points_end = points + num_points; // initialize a, b and c vertices if (loop) { b = points + num_points - 1; a = b - 1; // skip identical points before b while (a >= points && *a == *b) a--; if (a < points) // all points in the array are identical return 0; c = points; } else { a = points; b = a + 1; // skip identical points after a while (b != points_end && *a == *b) b++; if (b == points_end) // all points in the array are identical return 0; c = b + 1; } // skip identical points after b while (c != points_end && *b == *c) c++; if (!loop) { // add flat or triangle cap at beginning of line PT ba = *a - *b; Normalize(&ba, half_line_width); if (tcap) // add triangle cap coordinate to the output array AppendPoint(s, a->x + ba.x, a->y + ba.y); // add flat cap coordinates to the output array PT p; p.x = ba.y; p.y = -ba.x; AppendPoint(s, a->x - p.x, a->y - p.y); AppendPoint(s, a->x + p.x, a->y + p.y); } // add points by calculating the angle bisector of ab and bc int sign = 1; if (num_points >= 3) { while (c != points_end) { // skip zero or 180 degree bends // TODO: support 180 degree bends! if (!TriangleEmpty(*a, *b, *c)) { PT g = *b - *a, h = *c - *b; Normalize(&g, 1000.); Normalize(&h, 1000.); typename PT::product_type bisector_x = -g.y - h.y; typename PT::product_type bisector_y = g.x + h.x; float projected_length = (-g.y * bisector_x + g.x * bisector_y) * (1.f / 1000.f); if (projected_length < 400.f) { // acute angle, use the normal of the bisector instead projected_length = (g.x * bisector_x + g.y * bisector_y) * (1.f / 1000.f); std::swap(bisector_x, bisector_y); bisector_y *= -1; // the order of the triangles switches. keep track with 'sign' sign *= -1; } float scale = half_line_width / projected_length; if(std::is_integral<typename PT::product_type>::value) { bisector_x = sign * (typename PT::product_type) lround(bisector_x * scale); bisector_y = sign * (typename PT::product_type) lround(bisector_y * scale); } else { bisector_x = sign * bisector_x * scale; bisector_y = sign * bisector_y * scale; } AppendPoint(s, b->x - bisector_x, b->y - bisector_y); AppendPoint(s, b->x + bisector_x, b->y + bisector_y); } a = b; b = c; c++; while (c != points_end && *b == *c) // skip identical points c++; } } if (loop) { // repeat first two points at the end if (sign == 1) { AppendPoint(s, strip[0].x, strip[0].y); AppendPoint(s, strip[1].x, strip[1].y); } else { AppendPoint(s, strip[1].x, strip[1].y); AppendPoint(s, strip[0].x, strip[0].y); } } else { // add flat or triangle cap at end of line PT ab = *b - *a; Normalize(&ab, half_line_width); PT p; p.x = sign * -ab.y; p.y = sign * ab.x; AppendPoint(s, b->x - p.x, b->y - p.y); AppendPoint(s, b->x + p.x, b->y + p.y); if (tcap) AppendPoint(s, b->x + ab.x, b->y + ab.y); } return s - strip.begin(); }
static unsigned _PolygonToTriangles(const PT *points, unsigned num_points, GLushort *triangles, unsigned min_distance) #endif { // no redundant start/end please if (num_points >= 1 && points[0].x == points[num_points - 1].x && points[0].y == points[num_points - 1].y) num_points--; if (num_points < 3) return 0; assert(num_points < 65536); // next vertex pointer GLushort *next = new GLushort[num_points]; // index of the first vertex GLushort start = 0; // initialize next pointer counterclockwise if (PolygonRotatesLeft(points, num_points)) { for (unsigned i = 0; i < num_points-1; i++) next[i] = i + 1; next[num_points - 1] = 0; } else { next[0] = num_points - 1; for (unsigned i = 1; i < num_points; i++) next[i] = i - 1; } // thinning if (min_distance > 0) { for (unsigned a = start, b = next[a], c = next[b], heat = 0; num_points > 3 && heat < num_points; a = b, b = c, c = next[c], heat++) { bool point_removeable = TriangleEmpty(points[a], points[b], points[c]); if (!point_removeable) { unsigned distance = manhattan_distance(points[a], points[b]); if (distance < min_distance) { point_removeable = true; if (distance != 0) { for (unsigned p = next[c]; p != a; p = next[p]) { if (InsideTriangle(points[p], points[a], points[b], points[c])) { point_removeable = false; break; } } } } } if (point_removeable) { // remove node b from polygon if (b == start) // keep track of the smallest index start = std::min(a, c); next[a] = c; num_points--; // 'a' should stay the same in the next loop b = a; // reset heat heat = 0; } } //LogDebug(_T("polygon thinning (%u) removed %u of %u vertices"), // min_distance, orig_num_points-num_points, orig_num_points); } // triangulation GLushort *t = triangles; for (unsigned a = start, b = next[a], c = next[b], heat = 0; num_points > 2; a = b, b = c, c = next[c]) { if (LeftBend(points[a], points[b], points[c])) { bool ear_cuttable = true; for (unsigned p = next[c]; p != a; p = next[p]) { if (InsideTriangle(points[p], points[a], points[b], points[c])) { ear_cuttable = false; break; } } if (ear_cuttable) { // save triangle indices *t++ = a; *t++ = b; *t++ = c; // remove node b from polygon next[a] = c; num_points--; // 'a' should stay the same in the next loop b = a; // reset heat heat = 0; } } if (heat++ > num_points) { // if polygon edges overlap we may loop endlessly //LogDebug(_T("polygon_to_triangle: bad polygon")); delete[] next; return 0; } } delete[] next; return t - triangles; }