#include "vector.h"
#if INTERFACE
#include <stdint.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#ifndef DISTRIBUTION_BUILD
#include "triangle.h"
#endif

typedef unsigned int uint;
typedef struct {
  float r,g,b,a;
} Color4;

typedef struct { float x, y; uint32_t id; } edge_pt;
typedef struct { edge_pt p1, p2; } Edge;
typedef struct { Edge edge; float lx, ly, rx,ry; uint16_t t; int8_t e; } side;
typedef struct { 
  float x,  y,  z;
  uint8_t r,  g,  b, a;
  int8_t nx, ny;
  int8_t u,  v;
} strkvec;

typedef struct {
  uint8_t ends;
  uint8_t group;
  float size;
  Color4 color;
  float* line;
} stroke;


typedef struct stroke_prop_t {
  uint vBufferN, vBufferSize, length, ends;
  bool onePixel, lineWidthOverride, writeSegs, preview;
  float size, wobble, lineWidth;
} stroke_prop_t;

uint genstroke(
  strkvec* vBuffer,
  uint verts[][2],
  float line[][6],
  float segs[][24],
  float color[6],
  stroke_prop_t props 
);

typedef struct triangle_params{
  int numpoints;
  int numsegs;
  int numholes;
  bool render;
}triangle_params;

#endif

#define max(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a > _b ? _a : _b; })
#define min(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a < _b ? _a : _b; })

#define PI 3.141592653589793238462643383279502884197169399375105820974944592308
#define setBezPt(_x,_y,_w,_a) {\
  b++; \
    /*printf("b, bsize: %i, %i \n",b,bsize);*/\
    if (b> bsize) {printf("OOB1 %u %u \n",b,bsize); exit(1);};\
    /*printf("%i, %f, %f, %f, %f\n", b,_x,_y,_w,_a);*/\
  buffer[b][1] =  _x;\
  buffer[b][2] =  _y;\
  buffer[b][3] =  _w;\
  buffer[b][4] =  _a;\
}
#define OR ||
#define AND &&
#define vBufInsert(_x,_y,_z,_w, _u,_v, _r,_g,_b,_a, _nx,_ny,_nz) {\
  props.vBufferN++; \
  if (props.vBufferN> props.vBufferSize) {printf("OOB2 %u %u \n",props.vBufferN,props.vBufferSize); exit(1);};\
  /*printf("props.vBufferN %i %f,%f,%f,%f, %f,%f, %u,%u,%u,%f, %f,%f,%f \n", props.vBufferN,_x,_y,_z,_w, _u,_v, _r,_g,_b,_a, _nx,_ny,_nz);*/\
  vBuffer[props.vBufferN].x  = _x ;\
  vBuffer[props.vBufferN].y  = _y ;\
  vBuffer[props.vBufferN].z  = _z +props.vBufferN /5000;\
  vBuffer[props.vBufferN].r  = _r*254;\
  vBuffer[props.vBufferN].g  = _g*254;\
  vBuffer[props.vBufferN].b  = _b*254;\
  vBuffer[props.vBufferN].a  = _a*254;\
  vBuffer[props.vBufferN].u  = 127*fmax(-0.99,fmin(0.99,_u));\
  vBuffer[props.vBufferN].v  = 127*fmax(-0.99,fmin(0.99,_v));\
  vBuffer[props.vBufferN].nx = 127*fmax(-0.99,fmin(0.99,_nx));\
  vBuffer[props.vBufferN].ny = 127*fmax(-0.99,fmin(0.99,_ny));\
}

// int on_extract_entry(const char *filename, void *arg) {
//     static int i = 0;
//     int n = *(int *)arg;
//     printf("Extracted: %s (%d of %d)\n", filename, ++i, n);
//     return 0;
// }

float frand(float a) {
  return (float)rand()/(float)RAND_MAX;
}
int pnpoly(int nvert, float verts[][6], float testx, float testy) {
  int i, j, c = 0;
  for (i = 1, j = nvert; i < nvert+1; j = i++) {
    if ( ((verts[i][2]>testy) != (verts[j][2]>testy)) &&
     (testx < (verts[j][1]-verts[i][1]) * (testy-verts[i][2]) / (verts[j][2]-verts[i][2]) + verts[i][1]) )
       c = !c;
  }
  return c;
}

int pnpoly2(int nvert, float verts[][10], float testx, float testy) {
  int i, j, c = 0;
  for (i = 1, j = nvert; i < nvert+1; j = i++) {
    if ( ((verts[i][2]>testy) != (verts[j][2]>testy)) &&
     (testx < (verts[j][1]-verts[i][1]) * (testy-verts[i][2]) / (verts[j][2]-verts[i][2]) + verts[i][1]) )
       c = !c;
  }
  return c;
}

float lerp(float a, float b, float f) {
    return (a * (1.0 - f)) + (b * f);
}

float dist(float x1,float y1,float x2,float y2){
  return sqrtf((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}

void evaluateBezier(float curve[10],float t,float* x,float* y){
  float tmpBez1[10]={0};
  float tmpBez2[10]={0};
  tmpBez1[1]= lerp(curve[1],curve[5],t);        tmpBez1[2] = lerp(curve[2],curve[6],t); // E
  tmpBez1[3]= lerp(curve[5],curve[7],t);        tmpBez1[4] = lerp(curve[6],curve[8],t); // F
  tmpBez1[5]= lerp(curve[7],curve[3],t);        tmpBez1[6] = lerp(curve[8],curve[4],t); // G
  tmpBez1[7]= lerp(tmpBez1[3],tmpBez1[5],t);    tmpBez1[8] = lerp(tmpBez1[4],tmpBez1[6],t); // J

  tmpBez2[1]= curve[1];                         tmpBez2[2] = curve[2]; // A
  tmpBez2[5]= tmpBez1[1];                       tmpBez2[6] = tmpBez1[2]; // E
  tmpBez2[7]= lerp(tmpBez1[1],tmpBez1[3],t);    tmpBez2[8] = lerp(tmpBez1[2],tmpBez1[4],t); // H

  *x=lerp(tmpBez2[7],tmpBez1[7],t);
  *y=lerp(tmpBez2[8],tmpBez1[8],t); // K
}

int makebez(float buffer[][6], float line[][10], uint16_t detail ){
  size_t b = 0;
  size_t bsize = (size_t)buffer[0][1];
  if (detail==0 ) { 
    for (size_t l = 1; l<=line[0][0]; l++) {
      setBezPt (line[l][1],line[l][2],line[l][7],line[l][8] );
    }
  }else {
    float curve[10] = {0};
    size_t end = (size_t)line[0][0];
    for (size_t l = 2; l<= end; l++) {
      float ax=line[l-1][1], ay=line[l-1][2];
      
      uint16_t steps = ceil(((float)detail)*0.03*(10+dist(line[l-1][1],line[l-1][2],line[l][1],line[l][2])));

      bool shouldCurve  = (line[l-1][5]!=0 && line[l][3]!=0 && steps>0);
      setBezPt (ax,ay, line[l-1][7], line[l-1][8]);

      if (shouldCurve) { 
        float
          bx=line[l-1][5], by=line[l-1][6],
          cx=line[l][3],   cy=line[l][4],
          dx=line[l][1],   dy=line[l][2];

        curve[1]=ax; curve[2]=ay;
        curve[3]=dx; curve[4]=dy;
        curve[5]=bx; curve[6]=by;
        curve[7]=cx; curve[8]=cy;

        int t1 = 1;
        int t2 = steps;
        // printf("\nl %i %i %i\n",l,t1,t2);

        for (size_t i = t1; i<=t2; i++) { 
          float t = ((float)i)/((float)(steps+1));
          float x,y;
          // printf("  %f %f %f\n",t,x,y);
          evaluateBezier(curve,t,&x,&y);
          setBezPt (x,y, lerp(line[l-1][7],line[l][7],t) ,lerp(line[l-1][8],line[l][8],t) );
        }
      }else{
        // printf("dont curve\n");
      }
    }
    setBezPt (line[end][1], line[end][2], line[end][7], line[end][8]);
  }
  float minx=buffer[1][1];float miny=buffer[1][2];
  float maxx=buffer[1][1];float maxy=buffer[1][2];
  for (size_t i = 1; i<= b; i++) {
    minx = fmin(minx,buffer[b][1]); miny = fmin(miny,buffer[b][2]);
    maxx = fmax(maxx,buffer[b][1]); maxy = fmax(maxy,buffer[b][2]);
  }
  buffer[0][2] = minx; buffer[0][3] = miny;
  buffer[0][4] = maxx; buffer[0][5] = maxy;

  buffer[0][0] = (float)b;
  return 1;
}

double integer;
float frandom(float x){
  return modf(
    sin(100*pow(x,2))
    *cos(pow(x,4))
    *sin(pow((8*x),2))
    *cos(8*pow(x,3))
    *3+0.5,&integer);
}

uint genstroke(
  strkvec* vBuffer,
  uint verts[][2],
  float line[][6],
  float segs[][24],
  float color[6],
  stroke_prop_t props 
){
  // puts("gay");
  uint vn = 0;

  float mx=0,    my=0;
  float vx=0,    vy=0;
  float nx=0,    ny=0;
  float normx=0, normy=0;
  float w1=0, w2=0;
  float a1=0, a2=0;
  float len=0;
  float texu = 0;
  float mul = (false) ? 2 : 1;

  // puts("testing array access");
  float r=color[0],g=color[1],b=color[2],a=color[3];
  // float r=0,g=0,b=0,a=1;
  // puts("line");
  float w=color[4];
  float z=color[5];

  float ax1=0,ay1=0,ax2=0,ay2=0;
  float bx1=0,by1=0,bx2=0,by2=0;
  float cx1=0,cy1=0,cx2=0,cy2=0;
  float dx1=0,dy1=0,dx2=0,dy2=0;
  // puts("imgay");
  float first_mx=0,first_my=0;
  for (size_t i = 1; i < props.length; i++){
    
    float ax= line[i][1]    ,ay=line[i][2];
    float bx= line[i+1][1]  ,by=line[i+1][2];
 // printf("line1\n");
    
    // if (props.wobble>0) {
    //   time_t now = time(0);
    //   ax+=frandom(i  *now)*props.wobble*3;
    //   ay+=frandom(i+1*now)*props.wobble*3;
    //   bx+=frandom(i+2*now)*props.wobble*3;
    //   by+=frandom(i+3*now)*props.wobble*3;
    // }

    vx=(bx-ax); vy =(by-ay);
    len = fmax(0.00000001,sqrtf(vx * vx + vy * vy));
    vx/=len; vy/=len;

    normx=fmin(1,fmax(-1,-(vy))); normy=fmin(1,fmax(-1,(vx)));

    float texu2 = texu + len/8;
    w1=w2 = w;
    a1=a2 = a;

    w1=fmax(line[i][3],0)*mul;  w2 =fmax(line[i+1][3],0)*mul;
    if (props.preview){
      a1=a2=1;
    }else{
      a1=fmax(line[i][4],0)*a  ;  a2 =fmax(line[i+1][4],0)*a;
    }
    // puts("imverygay");
 // printf("line2\n");

    w1 = props.onePixel ? 0 : (props.lineWidth * (props.lineWidthOverride ? 1 : w1*w));
    w2 = props.onePixel ? 0 : (props.lineWidth * (props.lineWidthOverride ? 1 : w2*w));

    // if (props.ends == 2) { 
    //   w1 *= 1-fabs((i-props.length)/props.length);
    //   w2 *= 1-fabs((i+1-props.length)/props.length);
    // }

    ax1 = ax + (vy * w1); ay1= ay - (vx * w1);
    ax2 = ax - (vy * w1); ay2= ay + (vx * w1);

    bx1 = bx + (vy * w2); by1 =by - (vx * w2); 
    bx2 = bx - (vy * w2); by2 =by + (vx * w2);
    
    mx =(ax1+ax2)/2; my=(ay1+ay2)/2;

    if (i==1) {
      first_mx=mx;
      first_my=my;
    }

    if (i>1) {
      nx = (bx-line[i-1][1]); ny=(by-line[i-1][2]);
      float px= (ax-line[i-1][1]), py= (ay-line[i-1][2]);
      // printf("")
       // printf("line3\n");
      float plen = sqrtf(px*px+py*py);
            px /=plen; py/=plen;
      float dot  = (-vy) * px + vx * py;
      float dot2 = (vx) * px + vy * py;
      // if (fabs(dot)<0.5) { 
      //   vBuffer[props.vBufferN-1].x=ax1;  vBuffer[props.vBufferN-1].y=ay1;
      //   vBuffer[props.vBufferN-2].x=ax2;  vBuffer[props.vBufferN-2].y=ay2;
      //   vBuffer[props.vBufferN-3].x=ax2;  vBuffer[props.vBufferN-3].y=ay2;
      // else
      // printf(" %f %f, %f %f %f\n",vx,vy,px,py,dot2);
      float threshold = (props.ends==1 && props.preview) ? 0.75 : ((props.writeSegs) ? 0.5 : -1);
      if (dot2>threshold) { 
        float len2 = fmax(0.00000001,sqrtf(nx * nx + ny * ny));
        float nvx  = (nx/len2), nvy  = (ny/len2);
        float nvxw = nvx*w1,    nvyw = nvy*w1;
        if (dot<=0) { 
          float dx = dx1-ax1, dy = dy1-ay1;
          float texu3 = texu + sqrtf(dx * dx + dy * dy)/8;
          float vvx = ax-ax1, vvy = ay-ay1; 
          float vlen = sqrtf(vvx*vvx+vvy*vvy); 
          vvx = vvx/vlen; vvy = vvy/vlen;
          ax2=ax-nvyw; ay2=ay+nvxw;

          vBuffer[props.vBufferN-2].x=ax-nvyw; vBuffer[props.vBufferN-2].y = ay+nvxw;
          vBuffer[props.vBufferN-3].x=ax-nvyw; vBuffer[props.vBufferN-3].y = ay+nvxw;
          vBuffer[props.vBufferN-2].nx=-nvy; vBuffer[props.vBufferN-2].ny=nvx;
          vBuffer[props.vBufferN-3].nx=-nvy; vBuffer[props.vBufferN-3].ny=nvx;
          vBufInsert( ax1,ay1, z,0.0f        ,texu, 0.0f , r,g,b,a1,-vvx,-vvy,0.0f)
          vvx = ax-dx1;  vvy = ay-dy1; 
          vlen = sqrtf(vvx*vvx+vvy*vvy); 
          vvx = vvx/vlen;  vvy = vvy/vlen;
          vBufInsert( dx1,dy1, z,0.0f        ,texu3,0.0f , r,g,b,a2,-vvx,-vvy,0.0f)
          vBufInsert( ax-nvyw,ay+nvxw,z,0.0f ,texu, 1.0f , r,g,b,a1,-nvy,nvx,0.0f)

          if (props.writeSegs){
            vn+=1; 
            verts[vn][0]=props.vBufferN-2;
            verts[vn][1]=props.vBufferN-1;
          }
        }else {
          float dx = dx2-ax2, dy = dy2-ay2;
          float texu3 = texu + sqrtf(dx * dx + dy * dy)/8;
          float vvx = ax-ax2, vvy = ay-ay2; 
          float vlen = sqrtf(vvx*vvx+vvy*vvy); 
          vvx = vvx/vlen; vvy = vvy/vlen;
          ax1=ax+nvyw; ay1=ay-nvxw;

          vBuffer[props.vBufferN-1].x=ax+nvyw; vBuffer[props.vBufferN-1].y = ay-nvxw;
          vBuffer[props.vBufferN-1].nx=nvy; vBuffer[props.vBufferN-1].ny=-nvx;
          vBufInsert( ax+nvyw,ay-nvxw,  z,0.0f ,texu,0.0f,  r,g,b,a1,nvy,-nvx,0.0f)
          vBufInsert( ax2,ay2,          z,0.0f ,texu,1.0f,  r,g,b,a1,-vvx,-vvy,0.0f)
          vvx = ax-dx2;  vvy = ay-dy2; 
          vlen = sqrtf(vvx*vvx+vvy*vvy); 
          vvx = vvx/vlen;  vvy = vvy/vlen;
          vBufInsert( dx2,dy2,          z,0.0f ,texu3,1.0f, r,g,b,a2,-vvx,-vvy,0.0f)
          if (props.writeSegs){
            vn+=1; 
            verts[vn][0]=props.vBufferN-1;
            verts[vn][1]=props.vBufferN;
          }
        }   
      }else{
        // printf("round corner\n");
        float _x1,_y1,_x2,_y2;
        _x1=ax1; _y1=ay1; _x2=ax2; _y2=ay2;

        mx=(_x1+_x2)/2; my=(_y1+_y2)/2;
        float divs = props.writeSegs ? fmax(16,props.size/2) : fmax(4,floor(props.size/3+3));
        float inc  = props.writeSegs ? (PI*2)/(divs) : PI/divs;
        float ang  = props.writeSegs ? 0 : -atan2(_y2-_y1,_x2-_x1)-(PI/2);

        float step = 0;
        float w3 = w2*(props.writeSegs? 1.05 : 1);
        for (size_t k=0; k<divs; k++) {
          float offx=sin(ang+step),  offy = cos(ang+step);
          vBufInsert( mx,         my,         z,0.0f  ,0.5f,0.5f, r,g,b,a2,0.0f,0.0f,0.0f);
          vBufInsert( mx+offx*w3, my+offy*w3, z,0.0f  ,0.0f,0.0f, r,g,b,a2,offx,offy,0.0f);

          step= step+inc;
          offx=sin(ang+step);  offy = cos(ang+step);
          vBufInsert( mx+offx*w3, my+offy*w3, z,0.0f  ,0.0f,0.0f, r,g,b,a2,offx,offy,0.0f);

          if (props.writeSegs){
            vn+=1; 
            verts[vn][0]=props.vBufferN-1;
            verts[vn][1]=props.vBufferN;
          }
        }
      }
    }

    if (props.ends==1) {
      uint doubleEnds = props.length==4 ? 2 : 1;
        bool startTip = (i==1);
        bool writeCirc = !(mx==first_mx && my==first_my) && props.writeSegs;
        if (startTip || (i==props.length-1 && ((props.preview && !props.writeSegs) OR writeCirc))) { 
          float w3 = (startTip ? w1 : w2)*(props.writeSegs? 1.01 : 1);
          if (w3>0.2) { 
            // printf("round\n");
            float _x1,_y1,_x2,_y2;
            if (startTip) { 
              _x1=ax1; _y1=ay1; _x2=ax2; _y2=ay2;
            }else{
              _x1=bx1; _y1=by1; _x2=bx2; _y2=by2;
            }

            mx=(_x1+_x2)/2; my=(_y1+_y2)/2;
            float divs = props.writeSegs ? fmax(16,props.size/2) : fmax(4,floor(props.size/3+3));
            float inc  = props.writeSegs ? (PI*2)/(divs) : PI/divs;
            float ang  = props.writeSegs ? 0 : -atan2(_y2-_y1,_x2-_x1)-(startTip ? PI/2 : -PI/2);

            float step = 0;
            for (size_t k=0; k<divs; k++) {
              vBufInsert( mx,  my,   z,0.0f  ,0.5f,0.5f, r,g,b,a2,0.0f,0.0f,0.0f);
              float offx=sin(ang+step),  offy = cos(ang+step);
              // printf("%f  %f, %f, %f, %f\n", step,step+inc, w3,offx,offy);
              vBufInsert( mx+offx*w3, my+offy*w3, z,0.0f  ,0.0f,0.0f, r,g,b,a2,offx,offy,0.0f);
              step= step+inc;
              offx=sin(ang+step);  offy = cos(ang+step);
              vBufInsert( mx+offx*w3, my+offy*w3, z,0.0f  ,0.0f,0.0f, r,g,b,a2,offx,offy,0.0f);
              if (props.writeSegs){
                vn+=1; 
                verts[vn][0]=props.vBufferN-1;
                verts[vn][1]=props.vBufferN;
              }
            }
          }
        }
    }

    // printf("line5 %f %f\n",ax1,ay1);
    // printf("pt %i / %i\n",i,props.length);
    vBufInsert( ax1,ay1, z, 0.0f   , texu, 0.0f,  r,g,b,a1, -normx,-normy, 0.0f);
    vBufInsert( ax2,ay2, z, 0.0f   , texu, 1.0f,  r,g,b,a1,  normx, normy, 0.0f);
    vBufInsert( bx2,by2, z, 0.0f   , texu2,1.0f,  r,g,b,a2,  normx, normy, 0.0f);
    vBufInsert( bx2,by2, z, 0.0f   , texu2,1.0f,  r,g,b,a2,  normx, normy, 0.0f);
    vBufInsert( bx1,by1, z, 0.0f   , texu2,0.0f,  r,g,b,a2, -normx,-normy, 0.0f);
    vBufInsert( ax1,ay1, z, 0.0f   , texu, 0.0f,  r,g,b,a1, -normx,-normy, 0.0f);

    if (props.writeSegs){
      // if (i==1){
      //   vn+=1; 
      //   verts[vn][0]=props.vBufferN-5;
      //   verts[vn][1]=props.vBufferN-4;
      // }
      vn+=1; 
      verts[vn][0]=props.vBufferN-1;
      verts[vn][1]=props.vBufferN;
      vn+=1; 
      verts[vn][0]=props.vBufferN-4;
      verts[vn][1]=props.vBufferN-3;
      // if (i==props.length-1){
      //   vn+=1; 
      //   verts[vn][0]=props.vBufferN-1;
      //   verts[vn][1]=props.vBufferN-2;
      // }
    }

    // printf("line6");

    texu = texu2; 
    cx1=ax1; cy1=ay1; cx2=ax2; cy2=ay2;
    dx1=bx1; dy1=by1; dx2=bx2; dy2=by2;
  }

  // gotta do this after everthing above cause i go back and move verts around
  if (props.writeSegs){
    uint s;
    strkvec a,b;
    float nx,ny;
    for (size_t i = 1; i <= vn; i++){
      a = vBuffer[verts[i][0]];
      b = vBuffer[verts[i][1]];
      
      segs[0][0] += 1; s = (uint)segs[0][0];
      segs[s][1]=a.x; segs[s][2]=a.y;
      segs[s][3]=b.x; segs[s][4]=b.y;
      segs[s][9]=segs[0][0];
      segs[s][18] = sqrt( (b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y) );
      segs[s][20] = 0;
      
      // float d = sqrt( (b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y) );

      // nx=(a.nx+b.nx)/2; ny=(a.ny+b.ny)/2;
      // mx=(a.x+b.x)/2+nx/10; my=(a.y+b.y)/2+ny/10;

      // segs[0][0] += 1; s = (uint)segs[0][0];
      // segs[s][1]=a.x; segs[s][2]=a.y;
      // segs[s][3]=mx; segs[s][4]=my;
      // segs[s][9]=segs[0][0];
      // segs[s][18] = sqrt( (mx - a.x) * (mx - a.x) + (my - a.y) * (my - a.y) );

      // segs[0][0] += 1; s = (uint)segs[0][0];
      // segs[s][1]=mx; segs[s][2]=my;
      // segs[s][3]=b.x; segs[s][4]=b.y;
      // segs[s][9]=segs[0][0];
      // segs[s][18] = sqrt( (b.x - mx) * (b.x - mx) + (b.y - my) * (b.y - my) );

    }
  }
  // printf("genned %i %i %i",vn,props.vBufferN,props.vBufferSize);
  return props.vBufferN;
}
int trigger(){
  return 1;
}

int sortSegs(float* seg1,float* seg2){
  if (seg1[14]==seg2[14]){
    return seg1[15]<seg2[15] ? -1 : 1;
  }else{
    return seg1[14]<seg2[14] ? -1 : 1;
  }
}

int segmentize(
  float segmentBuf[][24],
  float line[][10],
  float bbox[5],
  int fill,
  int last,
  int index)
{
  // printf("fill %i\n",fill);
  for (int i = 1; i<=last; i++) {
    int j = (i==(int)(line[0][0])) ? 1 : (i+1);

    segmentBuf[0][0]+=1;
    float* seg = segmentBuf[(int)(segmentBuf[0][0])];
    seg[0] = 0;
    // end points
    seg[1] = line[i][1];
    seg[2] = line[i][2];
    seg[3] = line[j][1];
    seg[4] = line[j][2];

    // control points (collapse if close to end points)
    float d = dist(seg[1], seg[2],seg[3], seg[4]);
    if (d<4 AND dist(seg[1], seg[2],line[i][5],line[i][6])>d/2){
      line[i][5] = (seg[1]+line[i][5])/2;
      line[i][6] = (seg[2]+line[i][6])/2;
    }
    if (d<4 AND dist(seg[3], seg[4],line[j][3],line[j][4])>d/2){
      line[j][3] = (seg[3]+line[j][3])/2;
      line[j][4] = (seg[4]+line[j][4])/2;
    }

    seg[5] = line[i][5];   
    seg[6] = line[i][6];   
    seg[7] = line[j][3]; 
    seg[8] = line[j][4]; 
    // info
    seg[9] = segmentBuf[0][0];
    seg[10] = index;
    seg[11] = i;
    // end t values
    seg[12] = 0;
    seg[13] = 1;

    // bounds
    seg[14] = fmin(fmin(fmin(seg[1],seg[3]), seg[5]),seg[7]);
    seg[15] = fmin(fmin(fmin(seg[2],seg[4]), seg[6]),seg[8]);
    seg[16] = fmax(fmax(fmax(seg[1],seg[3]), seg[5]),seg[7]);
    seg[17] = fmax(fmax(fmax(seg[2],seg[4]), seg[6]),seg[8]);

    seg[18] = d;
    seg[19] = 0;
    seg[20] = fill;

    // seg[21] -- set to 1 when split

  }

  for (int l = 1; l<=line[0][0]; l++) {
    bbox[1]=fmin(bbox[1],line[l][1]-0.1);
    bbox[2]=fmin(bbox[2],line[l][2]-0.1);
    bbox[3]=fmax(bbox[3],line[l][1]+0.1);
    bbox[4]=fmax(bbox[4],line[l][2]+0.1);
  }
  return 1;
}

int compSides(side*  a, side* b){
  if (a->lx==b->lx) { 
    if (a->ly==b->ly) { 
      if (a->rx==b->rx) { 
        if (a->ry==b->ry) { 
          return (a->e==b->e) ? 0 : ((a->e<b->e) ? -1 : 1);
        }else{
          return (a->ry<b->ry) ? -1 : 1; 
        }
      }else{
        return (a->rx<b->rx) ? -1 : 1;
      }
    }else{
      return (a->ly<b->ly) ? -1 : 1;
    }
  }else{ 
    return (a->lx<b->lx) ? -1 : 1;
  }
}


int ctriangulate( 
  float* points,
  int* segments, 
  float* holes,
  int* triangles_out, 
  float* verts_out,
  int* counts,
  triangle_params param
)
{

  struct triangulateio in = {0};
  struct triangulateio out = {0};
  clock_t timer;
  double time_taken;


  in.numberofpointattributes = 0;
  in.numberofpoints = param.numpoints;
  in.pointlist = points;
  in.numberofsegments = param.numsegs;
  in.segmentlist = segments;
  in.numberofholes = param.numholes;
  in.holelist = holes;
  in.numberofregions = 0;

  timer = clock();

  triangulate(param.render ? "pzQ" : ((counts[4]==1)?"pznQ":"pzcnQ"), &in, &out, (struct triangulateio *) NULL);
  
  // timer = clock() - timer;
  // time_taken = ((double)timer)/CLOCKS_PER_SEC; // in seconds
  // printf("triangulation took %f frames \n", time_taken*60);
  // timer= clock();
  counts[0]=out.numberoftriangles;
  counts[1]=out.numberofpoints;
  
  if (counts[3]==1) {
    return 0;
  }

  // printf("TRIANGLES: %i, POINTS: %i \n",counts[0],counts[1]);

  if (counts[0] ==0){
    return 0;
  }

  int t1,t2,t3;
  for (unsigned int i = 0; i < out.numberoftriangles; i ++){
    t1=out.trianglelist[i*3];
    t2=out.trianglelist[i*3+1];
    t3=out.trianglelist[i*3+2];
    float x1 = out.pointlist[t1*2]; float y1 = out.pointlist[t1*2+1];
    float x2 = out.pointlist[t2*2]; float y2 = out.pointlist[t2*2+1];
    float x3 = out.pointlist[t3*2]; float y3 = out.pointlist[t3*2+1];
    verts_out[i*6  ] = x1; verts_out[i*6+1] =y1;
    verts_out[i*6+2] = x2; verts_out[i*6+3] =y2;
    verts_out[i*6+4] = x3; verts_out[i*6+5] =y3;
  }

  if (!param.render){
    
    
    for (unsigned int i = 0; i < out.numberoftriangles; i ++){
      t1=out.trianglelist[i*3];
      t2=out.trianglelist[i*3+1];
      t3=out.trianglelist[i*3+2];
      for (unsigned int j = 0; j < 3; j++) {
        int neighbour = out.neighborlist[i * 3 + j];
        triangles_out[i*3+j] = neighbour>-1 ? neighbour+1 : -1;
      }
    }
  
  }
  // free(in.pointmarkerlist);
  // free(out.pointlist);
  // free(out.pointattributelist);
  // free(out.triangleattributelist);
  // free(out.segmentlist);
  // free(out.segmentmarkerlist);


  // timer = clock() - timer;
  // time_taken = ((double)timer)/CLOCKS_PER_SEC; // in seconds
  // printf("copy/free took %f frames \n", time_taken*60);

  return counts[0];
}
