<commit_before>// canvas.c

#include "ruby2d.h"

typedef SDL_FPoint vector_t;

static float vector_magnitude(const vector_t *vec)
{
  return sqrtf((vec->x * vec->x) + (vec->y * vec->y));
}

static vector_t *vector_normalize(vector_t *vec)
{
  float mag = vector_magnitude(vec);
  if (mag == 0)
    mag = INFINITY;
  vec->x /= mag;
  vec->y /= mag;
  return vec;
}

static vector_t *vector_perpendicular(vector_t *vec)
{
  float tmp_x = vec->x;
  vec->x = vec->y;
  vec->y = -tmp_x;
  return vec;
}

static vector_t *vector_times_scalar(vector_t *vec, float value)
{
  vec->x *= value;
  vec->y *= value;
  return vec;
}

static vector_t *vector_minus_vector(vector_t *vec, const vector_t *other)
{
  vec->x -= other->x;
  vec->y -= other->y;
  return vec;
}

static vector_t *vector_minus_xy(vector_t *vec, float other_x, float other_y)
{
  vec->x -= other_x;
  vec->y -= other_y;
  return vec;
}

static vector_t *vector_plus_vector(vector_t *vec, const vector_t *other)
{
  vec->x += other->x;
  vec->y += other->y;
  return vec;
}

static vector_t *vector_plus_xy(vector_t *vec, float other_x, float other_y)
{
  vec->x += other_x;
  vec->y += other_y;
  return vec;
}

typedef enum { CONCAVE = -1, INVALID, CONVEX } poly_type_e;

//
// Go through each corner and identify and count the number of concave
// ones, also noting down the last concave point.
//
// TODO: collect all the concave points for a later ear-clipper.
// @param [int *] pconcave_ix Pointer to array of slots for concave point
// indices
// @param [int] max_pconcave Max number of clots for concave point indices
//
// @return number of concave points; note this may be > +max_pconcave+ so caller
//                needs to check result carefully.
//
static int count_concave_corners(const SDL_FPoint *points, int num_points,
                                 int *pconcave_ix, const int max_pconcave)
{
  if (num_points < 3)
    return (0);

  int nconcave = 0;

  // pick first last and first points
  int x1 = points[0].x;
  int y1 = points[0].y;
  int x2 = points[1].x;
  int y2 = points[1].y;
  // start with second point and proceed point by point inspecting
  // each corner
  float z, zprev = 0;
  for (int i = 1; i < num_points; i++, zprev = z) {
    int j = (i + 1) % num_points;
    int x3 = points[j].x;
    int y3 = points[j].y;
    // cross product
    z = ((x1 - x2) * (y2 - y3)) - ((y1 - y2) * (x2 - x3));
    if (z < 0) {
      if (nconcave < max_pconcave)
        *pconcave_ix++ = i;
      nconcave++;
    }
    // shift points left so we
    x1 = x2;
    y1 = y2;
    x2 = x3;
    y2 = y3;
  }
  return nconcave;
}

//
// If a polygon is entirely conves, we triangulate trivially
// by using the first point as the anchor to fan out the triangles
static void fill_convex_polygon(SDL_Renderer *render, const SDL_FPoint *points,
                                int num_points, const SDL_Color *colors,
                                int num_colors)
{
  // Setup the vertices from incoming arguments
  SDL_Vertex verts[3];

  // use first vertex as anchor and draw a triangle with
  // subsequent pair of points.
  verts[0].position = points[0];
  verts[0].color = colors[0];
  for (int i = 2; i < num_points; i++) {
    verts[1].position = points[i - 1];
    verts[1].color = colors[(i - 1) % num_colors];
    verts[2].position = points[i];
    verts[2].color = colors[i % num_colors];
    SDL_RenderGeometry(render, NULL, verts, 3, NULL, 0);
  }
}

//
// If a polygon has one concave corner, we can still do trivial
// triangulation as we do for convex polygons using the
// concave corner as the anchor point for the fan-out.
static void fill_concave1pt_polygon(SDL_Renderer *render,
                                    const SDL_FPoint *points, int num_points,
                                    int anchor_ix, const SDL_Color *colors,
                                    int num_colors)
{
  // Setup the vertices from incoming arguments
  SDL_Vertex verts[3];

  verts[0].position = points[anchor_ix];
  verts[0].color = colors[anchor_ix];
  anchor_ix = (anchor_ix + 1) % num_points;
  for (int i = 2; i < num_points; i++) {
    verts[1].position = points[anchor_ix];
    verts[1].color = colors[anchor_ix % num_colors];
    anchor_ix = (anchor_ix + 1) % num_points;
    verts[2].position = points[anchor_ix];
    verts[2].color = colors[anchor_ix % num_colors];
    SDL_RenderGeometry(render, NULL, verts, 3, NULL, 0);
  }
}

#define MAX_CONCAVE_POINT_INDICES 32

/*
 * Fill a polygon with a single colour.
 * @param points Array of points
 * @param num_points Number of points
 * @param colors Array of colours
 * @param num_colors Number of colors, must be at least 1, and may be less than
 *                      +num_points+ in which case the colours will be repeated
 *                      via modulo.
 * @note Currently supports only: convex polygons or simple polygons with one
 * concave corner. For now.
 */
void R2D_Canvas_FillPolygon(SDL_Renderer *render, SDL_FPoint *points,
                            int num_points, SDL_Color *colors, int num_colors)
{
  // poly_type_e type = polygon_type(points, num_points);
  int concave_point_indices[MAX_CONCAVE_POINT_INDICES];
  int nconcave = count_concave_corners(
      points, num_points, concave_point_indices, MAX_CONCAVE_POINT_INDICES);

  if (nconcave == 0) {
    // convex
    fill_convex_polygon(render, points, num_points, colors, num_colors);
  }
  else if (nconcave == 1) {
    // use concave point as the origin if only one concave vertex
    fill_concave1pt_polygon(render, points, num_points,
                            concave_point_indices[0], colors, num_colors);
  }
  // else
  //   printf("TODO: Non-convex polygon with %d concave corners\n", nconcave);
}<commit_msg>Detect triangle as convex, and fix concave poly coloring<commit_after>// canvas.c

#include "ruby2d.h"

typedef SDL_FPoint vector_t;

static float vector_magnitude(const vector_t *vec)
{
  return sqrtf((vec->x * vec->x) + (vec->y * vec->y));
}

static vector_t *vector_normalize(vector_t *vec)
{
  float mag = vector_magnitude(vec);
  if (mag == 0)
    mag = INFINITY;
  vec->x /= mag;
  vec->y /= mag;
  return vec;
}

static vector_t *vector_perpendicular(vector_t *vec)
{
  float tmp_x = vec->x;
  vec->x = vec->y;
  vec->y = -tmp_x;
  return vec;
}

static vector_t *vector_times_scalar(vector_t *vec, float value)
{
  vec->x *= value;
  vec->y *= value;
  return vec;
}

static vector_t *vector_minus_vector(vector_t *vec, const vector_t *other)
{
  vec->x -= other->x;
  vec->y -= other->y;
  return vec;
}

static vector_t *vector_minus_xy(vector_t *vec, float other_x, float other_y)
{
  vec->x -= other_x;
  vec->y -= other_y;
  return vec;
}

static vector_t *vector_plus_vector(vector_t *vec, const vector_t *other)
{
  vec->x += other->x;
  vec->y += other->y;
  return vec;
}

static vector_t *vector_plus_xy(vector_t *vec, float other_x, float other_y)
{
  vec->x += other_x;
  vec->y += other_y;
  return vec;
}

typedef enum { CONCAVE = -1, INVALID, CONVEX } poly_type_e;

//
// Go through each corner and identify and count the number of concave
// ones, also noting down the last concave point.
//
// TODO: collect all the concave points for a later ear-clipper.
// @param [int *] pconcave_ix Pointer to array of slots for concave point
// indices
// @param [int] max_pconcave Max number of clots for concave point indices
//
// @return number of concave points; note this may be > +max_pconcave+ so caller
//                needs to check result carefully.
//
static int count_concave_corners(const SDL_FPoint *points, int num_points,
                                 int *pconcave_ix, const int max_pconcave)
{
  // triangles are always convex
  if (num_points <= 3)
    return (0);

  int nconcave = 0;

  // pick first last and first points
  int x1 = points[0].x;
  int y1 = points[0].y;
  int x2 = points[1].x;
  int y2 = points[1].y;
  // start with second point and proceed point by point inspecting
  // each corner
  float z, zprev = 0;
  for (int i = 1; i < num_points; i++, zprev = z) {
    int j = (i + 1) % num_points;
    int x3 = points[j].x;
    int y3 = points[j].y;
    // cross product
    z = ((x1 - x2) * (y2 - y3)) - ((y1 - y2) * (x2 - x3));
    if (z < 0) {
      if (nconcave < max_pconcave)
        *pconcave_ix++ = i;
      nconcave++;
    }
    // shift points left so we
    x1 = x2;
    y1 = y2;
    x2 = x3;
    y2 = y3;
  }
  return nconcave;
}

//
// If a polygon is entirely conves, we triangulate trivially
// by using the first point as the anchor to fan out the triangles
static void fill_convex_polygon(SDL_Renderer *render, const SDL_FPoint *points,
                                int num_points, const SDL_Color *colors,
                                int num_colors)
{
  // Setup the vertices from incoming arguments
  SDL_Vertex verts[3];

  // use first vertex as anchor and draw a triangle with
  // subsequent pair of points.
  verts[0].position = points[0];
  verts[0].color = colors[0];
  for (int i = 2; i < num_points; i++) {
    verts[1].position = points[i - 1];
    verts[1].color = colors[(i - 1) % num_colors];
    verts[2].position = points[i];
    verts[2].color = colors[i % num_colors];
    SDL_RenderGeometry(render, NULL, verts, 3, NULL, 0);
  }
}

//
// If a polygon has one concave corner, we can still do trivial
// triangulation as we do for convex polygons using the
// concave corner as the anchor point for the fan-out.
static void fill_concave1pt_polygon(SDL_Renderer *render,
                                    const SDL_FPoint *points, int num_points,
                                    int anchor_ix, const SDL_Color *colors,
                                    int num_colors)
{
  // Setup the vertices from incoming arguments
  SDL_Vertex verts[3];

  verts[0].position = points[anchor_ix];
  verts[0].color = colors[anchor_ix % num_colors];
  anchor_ix = (anchor_ix + 1) % num_points;
  for (int i = 2; i < num_points; i++) {
    verts[1].position = points[anchor_ix];
    verts[1].color = colors[anchor_ix % num_colors];
    anchor_ix = (anchor_ix + 1) % num_points;
    verts[2].position = points[anchor_ix];
    verts[2].color = colors[anchor_ix % num_colors];
    SDL_RenderGeometry(render, NULL, verts, 3, NULL, 0);
  }
}

#define MAX_CONCAVE_POINT_INDICES 32

/*
 * Fill a polygon with a single colour.
 * @param points Array of points
 * @param num_points Number of points
 * @param colors Array of colours
 * @param num_colors Number of colors, must be at least 1, and may be less than
 *                      +num_points+ in which case the colours will be repeated
 *                      via modulo.
 * @note Currently supports only: convex polygons or simple polygons with one
 * concave corner. For now.
 */
void R2D_Canvas_FillPolygon(SDL_Renderer *render, SDL_FPoint *points,
                            int num_points, SDL_Color *colors, int num_colors)
{
  if (num_points < 3)
    return;

  // poly_type_e type = polygon_type(points, num_points);
  int concave_point_indices[MAX_CONCAVE_POINT_INDICES];

  int nconcave =
      num_points == 3
          ? 0 // triangles are always convex
          : count_concave_corners(points, num_points, concave_point_indices,
                                  MAX_CONCAVE_POINT_INDICES);

  if (nconcave == 0) {
    // convex
    fill_convex_polygon(render, points, num_points, colors, num_colors);
  }
  else if (nconcave == 1) {
    // use concave point as the origin if only one concave vertex
    fill_concave1pt_polygon(render, points, num_points,
                            concave_point_indices[0], colors, num_colors);
  }
  // else
  //   printf("TODO: Non-convex polygon with %d concave corners\n", nconcave);
}
{'fraction_non_alphanumeric': 0.11343461932400137, 'fraction_numerical': 0.012376237623762377, 'mean_word_length': 2.7067383739323, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 13, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3783809', 'n_tokens_mistral': 4132, 'n_tokens_neox': 3907, 'n_words': 1233}