예제 #1
0
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;
}
예제 #2
0
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();
}
예제 #3
0
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;
}