#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include "web3d.h"

/* ---------------------------------------------------------------------- *
 * begin common part
 * ---------------------------------------------------------------------- */

double bound1(double d) {
    if (d<0) d=0.0;
    if (d>1) d=1.0;
    return d;
}

int bound255(int i) {
    if (i<0) i=0;
    if (i>255) i=255;
    return i;
}

void randomSeed(unsigned int seed) {
    srand(seed);
}

void randomizeByTime(void) {
    srand((unsigned int)time(NULL));
}

double randomInRange(double min, double max) {
    int r = rand();
    return (max-min)*r/RAND_MAX+min;
}

double radians(double deg) {
    return deg/180*M_PI;
}

static double __strokeWeight = 1;

void strokeWeight(double arg) {
    __strokeWeight = arg;
}

static char __stroke[8] = "none";

void stroke(unsigned int arg) {
    if (arg>0xffffff) arg=0xffffff;
    sprintf(__stroke, "#%06x", arg);
}

void noStroke(void) {
    sprintf(__stroke, "none");
}

static double __strokeOpacity = 1;

void strokeOpacity(double o) {
    __strokeOpacity = bound1(o);
}

static char __fill[8]   = "#000000";

void fill(unsigned int arg) {
    if (arg>0xffffff) arg=0xffffff;
    sprintf(__fill, "#%06x", arg);
}

void noFill(void) {
    sprintf(__fill, "none");
}

static double __fillOpacity = 1;

void fillOpacity(double o) {
    __fillOpacity = bound1(o);
}

static char* __fontFamily = "monospace";
/* char * __fontFamily = "Times New Roman"; */

static double __fontSize = 12;

void textFont(char* font, double size) {
    __fontFamily = font;
    __fontSize = size;
}

static int __gDepth = 0;

#define GSTACK_SIZE 1024

static int __gStack[GSTACK_SIZE] = { 0 };

static int __gSP = 0;

void __pushGStack(void) {
    if (__gSP>=GSTACK_SIZE) {
        fprintf(stderr, "Stack Overflow!\n");
        return;
    }
    __gStack[__gSP++] = __gDepth;
}

int __popGStack(void) {
    if (__gSP<=0) {
        fprintf(stderr, "Stack Underflow!\n");
        return 0;
    }
    return __gStack[--__gSP];
}

int rgb255(double r, double g, double b) {
    r=bound255((int)r); g=bound255((int)g); b=bound255((int)b);
    return ((int)r*0x10000)+((int)g*0x100)+(int)b;
}

int rgb100(double r, double g, double b) {
    return rgb255(r*2.55, g*2.55, b*2.55);
}

int rgb1(double r, double g, double b) {
    return rgb255(r*255, g*255, b*255);
}

int hsb1(double h, double s, double v) {
    double r=0, g=0, b=0;

    h *= 360;
    while(h>=360) h-=360;
    while(h<0)   h+=360;

    s = bound1(s);
    if (s==0) {
        r=g=b=v;
    } else {
        int H, h1;
        double f, p, q, t;

        v = bound1(v);

        H = (int)h;
        h1 = H/60;

        f = h/60 - h1;
        p = v * (1-s);
        q = v * (1-f*s);
        t = v * (1-(1-f)*s);

        switch (h1) {
        case 0: r=v; g=t; b=p; break;
        case 1: r=q; g=v; b=p; break;
        case 2: r=p; g=v; b=t; break;
        case 3: r=p; g=q; b=v; break;
        case 4: r=t; g=p; b=v; break;
        case 5: r=v; g=p; b=q; break;
        }
    }
    return rgb1(r, g, b);
}

int hsb100(double h, double s, double v) {
    return hsb1(h/100, s/100, v/100);
}

int hsb360(double h, double s, double v) {
    return hsb1(h/360, s/100, v/100);
}

int hsb255(double h, double s, double v) {
    return hsb1(h/255, s/255, v/255);
}

int hsl1(double h, double s, double l) {
    if (l==0) {
        /* max = min = 0 */
        return hsb1(h, 0, 0);
    } else if (l<=0.5) {
        double diff = s * l;
        double max  = l + diff;
        double min  = l - diff;
        return hsb1(h, 1-min/max, max);
    } else {
        double diff = s * (1-l);
        double max  = l + diff;
        double min  = l - diff;
        return hsb1(h, 1-min/max, max);
    }
}

int hsl100(double h, double s, double l) {
    return hsl1(h/100, s/100, l/100);
}

int hsl360(double h, double s, double l) {
    return hsl1(h/360, s/100, l/100);
}

int hsl255(double h, double s, double l) {
    return hsb1(h/255, s/255, l/255);
}

int bw255(double v) {
    return rgb255(v, v, v);
}

int bw100(double v) {
    return bw255(v*2.55);
}

int bw1(double v) {
    return bw255(v*255);
}

double cos360(double deg) {
    return cos(radians(deg));
}

double sin360(double deg) {
    return sin(radians(deg));
}

double* rgb2hsb360(int rgb, double* ret) {
    int b = rgb % 0x100;
    int g = rgb / 0x100 % 0x100;
    int r = rgb / 0x10000 % 0x100;
    int max, min, sub, angle;

    if (r>=g) { // ? r ? g ?
        if (g>=b) { // r g b
            max = r; min = b; sub = g - b; angle = 0;
        } else if (b>=r) { // b r g
            max = b; min = g; sub = r - g; angle = 240;
        } else { // r b g
            max = r; min = g; sub = g - b; angle = 0;
        }
    } else { // ? g ? r ?
        if (r>=b) { // g r b
            max = g; min = b; sub = b - r; angle = 120;
        } else if (b>=g) { // b g r
            max = b; min = r; sub = r - g; angle = 240;
        } else { // g b r
            max = g; min = r; sub = b - r; angle = 120;
        }
    }

    ret[0] = 60*((double)sub/(max-min)) + angle;
    if (ret[0]<0) ret[0]+=360;
    ret[1] = 100.0*(max - min) / max;
    ret[2] = max / 255.0 * 100;
    return ret;
}

int rotateH360(int color, double a) {
    double hsb[3];
    rgb2hsb360(color, hsb);
    hsb[0] += a;
    return hsb360(hsb[0], hsb[1], hsb[2]);
}

int rotateH(int color) {
    return rotateH360(color, PHYLLOTAXIS360);
}

int addS100(int color, double a) {
    double hsb[3];
    rgb2hsb360(color, hsb);
    hsb[1] += a;
    return hsb360(hsb[0], hsb[1], hsb[2]);
}

int scaleS(int color, double a) {
    double hsb[3];
    rgb2hsb360(color, hsb);
    hsb[1] *= a;
    return hsb360(hsb[0], hsb[1], hsb[2]);
}

int addB100(int color, double a) {
    double hsb[3];
    rgb2hsb360(color, hsb);
    hsb[2] += a;
    return hsb360(hsb[0], hsb[1], hsb[2]);
}

int scaleB(int color, double a) {
    double hsb[3];
    rgb2hsb360(color, hsb);
    hsb[2] *= a;
    return hsb360(hsb[0], hsb[1], hsb[2]);
}

/* ---------------------------------------------------------------------- *
 * end common part
 * ---------------------------------------------------------------------- */

void printVector(double v[3]) {
    printf("%.3f, %.3f, %.3f", v[0], v[1], v[2]);
}

static double __thickness = 0;

void thickness(double t) {
    __thickness = t;
}

static char* __separatorString;
void printSeparator() {
    printf(__separatorString);
    __separatorString = ", ";
}

void start() {
    printf("[\r\n");
    printf("  { \"tag\" : \"nop\" }\r\n");
    __fontFamily = "monospace";
    __separatorString = ", ";
    __gDepth = 0;
    __gSP = 0;
}

void resetMatrix() {
    while(__gDepth>0) {
        __gDepth--;
        printf("]\r\n");
        printf("}\r\n");
    }
    __gSP = 0;
}

void finish() {
    resetMatrix();
    printf("]\r\n");
}


void printVertices(double vertices[][3], int length) {
    char* sep = "";
    int i;
    printf("[");
    for (i=0; i<length; i++) {
        printf("%s ", sep);
        printVector(vertices[i]);
        sep = ",";
    }
    printf("]");
}

void polyline(double vertices[][3], int length) {
    if (length>=2) {
        printSeparator();
        printf("{ \"tag\": \"polyline\"\r\n");
        printf(", \"vertices\": ");
        printVertices(vertices, length);
        printf("\r\n");
        printf(", \"stroke\": \"%s\"\r\n", __stroke);
        printf(", \"stroke-width\": \"%f\"\r\n", __strokeWeight);
        printf("}\r\n");
    }
}

void triangleMesh(double vertices[][3], int length) {
    if (length>=2) {
        printSeparator();
        printf("{ \"tag\": \"triangleMesh\"\r\n");
        printf(", \"vertices\": ");
        printVertices(vertices, length);
        printf("\r\n");
        printf(", \"fill\": \"%s\"\r\n", __fill);
        printf("}\r\n");
    }
}

void printFaces(int indices[][3], int length) {
    int i;
    char* sep = "";
    printf("[");
    for (i=0; i<length; i++) {
        printf("%s ", sep);
        printf("%d, %d, %d", indices[i][0], indices[i][1], indices[i][2]);
        sep = ",";
    }
    printf("]");
}

void triangleMeshWithIndices(double vertices[][3], int vlength, int indices[][3], int ilength) {
    if (vlength>=2) {
        printSeparator();
        printf("{ \"tag\": \"triangleMesh\"\r\n");
        printf(", \"vertices\": ");
        printVertices(vertices, vlength);
        printf("\r\n");
        printf(", \"faces\": ");
        printFaces(indices, ilength);
        printf("\r\n");
        printf(", \"fill\": \"%s\"\r\n", __fill);
        printf("}\r\n");
    }
}


// 3D primitives
void box(double width, double height, double depth) {
    printSeparator();
    printf("{ \"tag\": \"box\"\r\n");
    printf(", \"box\": [ %.3f, %.3f, %.3f ]\r\n", width, height, depth);
    printf(", \"stroke\": \"%s\"\r\n", __stroke);
    printf(", \"fill\": \"%s\"\r\n", __fill);
    printf(", \"stroke-opacity\": \"%f\"\r\n", __strokeOpacity);
    printf(", \"stroke-width\": \"%f\"\r\n", __strokeWeight);
    printf(", \"fill-opacity\": \"%f\"\r\n", __fillOpacity);
    printf("}\r\n");
}

void sphere(double radius) {
    printSeparator();
    printf("{ \"tag\": \"sphere\"\r\n");
    printf(", \"sphere\": %.3f", radius);
    printf(", \"stroke\": \"%s\"\r\n", __stroke);
    printf(", \"fill\": \"%s\"\r\n", __fill);
    printf(", \"stroke-opacity\": \"%f\"\r\n", __strokeOpacity);
    printf(", \"stroke-width\": \"%f\"\r\n", __strokeWeight);
    printf(", \"fill-opacity\": \"%f\"\r\n", __fillOpacity);
    printf("}\r\n");
}

void cone(double radius, double height) {
    printSeparator();
    printf("{ \"tag\": \"cone\"\r\n");
    printf(", \"radius\": %.3f\r\n", radius);
    printf(", \"height\": %.3f\r\n", height);
    printf(", \"stroke\": \"%s\"\r\n", __stroke);
    printf(", \"fill\": \"%s\"\r\n", __fill);
    printf(", \"stroke-opacity\": \"%f\"\r\n", __strokeOpacity);
    printf(", \"stroke-width\": \"%f\"\r\n", __strokeWeight);
    printf(", \"fill-opacity\": \"%f\"\r\n", __fillOpacity);
    printf("}\r\n");
}

void nonuniformCylinder(double radius, double height, double topRadius) {
    printSeparator();
    printf("{ \"tag\": \"cylinder\"\r\n");
    printf(", \"radius\": %.3f\r\n", radius);
    printf(", \"height\": %.3f\r\n", height);
    printf(", \"topRadius\": %.3f\r\n", topRadius);
    printf(", \"stroke\": \"%s\"\r\n", __stroke);
    printf(", \"fill\": \"%s\"\r\n", __fill);
    printf(", \"stroke-opacity\": \"%f\"\r\n", __strokeOpacity);
    printf(", \"stroke-width\": \"%f\"\r\n", __strokeWeight);
    printf(", \"fill-opacity\": \"%f\"\r\n", __fillOpacity);
    printf("}\r\n");
}

void cylinder(double radius, double height) {
    nonuniformCylinder(radius, height, radius);
}

void line(double x1, double y1, double z1, double x2, double y2, double z2) {
    printSeparator();
    printf("{ \"tag\": \"polyline\"\r\n");
    printf(", \"vertices\": ");
    printf("[ %.3f, %.3f, %.3f, %.3f, %.3f, %.3f]", x1, y1, z1, x2, y2, z2);
    printf("\r\n");
    printf(", \"stroke\": \"%s\"\r\n", __stroke);
    printf(", \"stroke-opacity\": \"%f\"\r\n", __strokeOpacity);
    printf(", \"stroke-width\": \"%f\"\r\n", __strokeWeight);
    printf("}\r\n");
}

void triangle(double x1, double y1, double z1, double x2, double y2, double z2, double x3, double y3, double z3) {
    printSeparator();
    printf("{ \"tag\": \"triangleMesh\"\r\n");
    printf(", \"vertices\": ");
    printf("[ %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f]", x1, y1, z1, x2, y2, z2, x3, y3, z3);
    printf("\r\n");
    printf(", \"thickness\": \"%f\"\r\n", __thickness);
    printf(", \"stroke\": \"%s\"\r\n", __stroke);
    printf(", \"fill\": \"%s\"\r\n", __fill);
    printf(", \"stroke-opacity\": \"%f\"\r\n", __strokeOpacity);
    printf(", \"fill-opacity\": \"%f\"\r\n", __fillOpacity);
    printf(", \"stroke-width\": \"%f\"\r\n", __strokeWeight);
    printf("}\r\n");
}

void point(double x, double y, double z) {
    printSeparator();
    printf("{ \"tag\": \"pixels\"\r\n");
    printf(", \"vertices\": ");
    printf("[ %.3f, %.3f, %.3f]", x, y, z);
    printf("\r\n");
    printf(", \"stroke\": \"%s\"\r\n", __stroke);
    printf(", \"stroke-opacity\": \"%f\"\r\n", __strokeOpacity);
    printf(", \"stroke-width\": \"%f\"\r\n", __strokeWeight);
    printf("}\r\n");
}

// Transforms
void applyMatrix(double n00, double n01, double n02, double n03
                 , double n10, double n11, double n12, double n13
                 , double n20, double n21, double n22, double n23
                 , double n30, double n31, double n32, double n33) {
    printSeparator();
    printf("{ \"tag\": \"matrix\"\r\n");
    printf(", \"matrix\": [ %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f]\r\n"
           ,  n00,  n01,  n02,  n03
           ,  n10,  n11,  n12,  n13
           ,  n20,  n21,  n22,  n23
           ,  n30,  n31,  n32,  n33);
    printf(", \"children\": [\r\n");
    printf(" { \"tag\" : \"nop\" }\r\n");
    __gDepth++;
}

void pushMatrix() {
    __pushGStack();
}

void popMatrix() {
    int tmp = __popGStack();
    while(__gDepth>tmp) {
        __gDepth--;
        printf("]\r\n");
        printf("}\r\n");
    }
}

void scale(double x, double y, double z) {
    printSeparator();
    printf("{ \"tag\": \"scale\"\r\n");
    printf(", \"scale\": [ %.3f, %.3f, %.3f ]\r\n", x, y, z);
    printf(", \"children\": [\r\n");
    printf(" { \"tag\" : \"nop\" }\r\n");
    __gDepth++;
}

void translate(double x, double y, double z) {
    printSeparator();
    printf("{ \"tag\": \"translate\"\r\n");
    printf(", \"translate\": [ %.3f, %.3f, %.3f ]\r\n", x, y, z);
    printf(", \"children\": [\r\n");
    printf(" { \"tag\" : \"nop\" }\r\n");
    __gDepth++;
}

void rotate(double angle) {
    printSeparator();
    printf("{ \"tag\": \"rotate\"\r\n");
    printf(", \"rotate\": %.3f\r\n", angle);
    printf(", \"children\": [\r\n");
    printf(" { \"tag\" : \"nop\" }\r\n");
    __gDepth++;
}

void rotateX(double angle) {
    printSeparator();
    printf("{ \"tag\": \"rotateX\"\r\n");
    printf(", \"rotateX\": %.3f\r\n", angle);
    printf(", \"children\": [\r\n");
    printf(" { \"tag\" : \"nop\" }\r\n");
    __gDepth++;
}

void rotateY(double angle) {
    printSeparator();
    printf("{ \"tag\": \"rotateY\"\r\n");
    printf(", \"rotateY\": %.3f\r\n", angle);
    printf(", \"children\": [\r\n");
    printf(" { \"tag\" : \"nop\" }\r\n");
    __gDepth++;
}

void rotateZ(double angle) {
    printSeparator();
    printf("{ \"tag\": \"rotateZ\"\r\n");
    printf(", \"rotateZ\": %.3f\r\n", angle);
    printf(", \"children\": [\r\n");
    printf(" { \"tag\" : \"nop\" }\r\n");
    __gDepth++;
}

// Images
void imageBB(char* url, double x, double y, double z, double w, double h) {  // with Bounding Box
    printSeparator();
    printf("{ \"tag\": \"image\"\r\n");
    printf(", \"position\": [ %.3f, %.3f, %.3f ]\r\n", x, y, z);
    printf(", \"size\": [ %.3f, %.3f ]\r\n", w, h);
    printf(", \"url\": \"%s\"\r\n", url);
    printf("}\r\n");
}

void image(char* url, double x, double y, double z) {
    printSeparator();
    printf("{ \"tag\": \"image\"\r\n");
    printf(", \"position\": [ %.3f, %.3f, %.3f ]\r\n", x, y, z);
    printf(", \"url\": \"%s\"\r\n", url);
    printf("}\r\n");
}

void imageO(char* url) {
    printSeparator();
    printf("{ \"tag\": \"image\"\r\n");
    printf(", \"url\": \"%s\"\r\n", url);
    printf("}\r\n");
}

// Images
void objectBB(char* url, double x, double y, double z, double w, double h, double d) {
    printSeparator();
    printf("{ \"tag\": \"object\"\r\n");
    printf(", \"position\": [ %.3f, %.3f, %.3f ]\r\n", x, y, z);
    printf(", \"bbox\": [ %.3f, %.3f, %.3f ]\r\n", w, h, d);
    printf(", \"url\": \"%s\"\r\n", url);
    printf("}\r\n");
}

void object(char* url, double x, double y, double z) {
    printSeparator();
    printf("{ \"tag\": \"object\"\r\n");
    printf(", \"position\": [ %.3f, %.3f, %.3f ]\r\n", x, y, z);
    printf(", \"url\": \"%s\"\r\n", url);
    printf("}\r\n");
}

void objectO(char* url) {
    printSeparator();
    printf("{ \"tag\": \"object\"\r\n");
    printf(", \"url\": \"%s\"\r\n", url);
    printf("}\r\n");
}

// Text
void text(char* data, double x, double y, double z, ...) {
    va_list list;
    va_start(list, z);
    printSeparator();
    printf("{ \"tag\": \"text\"\r\n");
    printf(", \"text\": \"");
    vprintf(data, list);
    printf("\"\r\n");
    printf(", \"position\": [ %.3f, %.3f, %.3f ]\r\n", x, y, z);
    printf(", \"font-family\": \"%s\"\r\n", __fontFamily);
    printf(", \"font-size\": \"%f\"\r\n", __fontSize);
    printf(", \"thickness\": \"%f\"\r\n", __thickness);
    printf(", \"stroke\": \"%s\"\r\n", __stroke);
    printf(", \"fill\": \"%s\"\r\n", __fill);
    printf(", \"stroke-opacity\": \"%f\"\r\n", __strokeOpacity);
    printf(", \"fill-opacity\": \"%f\"\r\n", __fillOpacity);
    printf(", \"stroke-width\": \"%f\"\r\n", __strokeWeight);
    printf("}\r\n");
}

void textO(char* data, ...) {
    va_list list;
    va_start(list, data);
    printSeparator();
    printf("{ \"tag\": \"text\"\r\n");
    printf(", \"text\": \"");
    vprintf(data, list);
    printf("\"\r\n");
    printf(", \"position\": [ 0, 0, 0 ]\r\n");
    printf(", \"font-family\": \"%s\"\r\n", __fontFamily);
    printf(", \"font-size\": \"%f\"\r\n", __fontSize);
    printf(", \"thickness\": \"%f\"\r\n", __thickness);
    printf(", \"stroke\": \"%s\"\r\n", __stroke);
    printf(", \"fill\": \"%s\"\r\n", __fill);
    printf(", \"stroke-opacity\": \"%f\"\r\n", __strokeOpacity);
    printf(", \"fill-opacity\": \"%f\"\r\n", __fillOpacity);
    printf(", \"stroke-width\": \"%f\"\r\n", __strokeWeight);
    printf("}\r\n");
}

// Curves
void curve(double x1, double y1, double z1, double x2, double y2, double z2, double x3, double y3, double z3, double x4, double y4, double z4) {
    // TODO
    beginShape();
    vertex(x1, y1, z1);
    vertex(x2, y2, z2);
    vertex(x3, y3, z3);
    vertex(x4, y4, z4);
    endShape(0);
}

void bezier(double x1, double y1, double z1, double cx1, double cy1, double cz1, double cx2, double cy2, double cz2, double x2, double y2, double z2) {
    beginShape();
    vertex(x1, y1, z1);
    bezierVertex(cx1, cy1, cz1, cx2, cy2, cz2, x2, y2, z2);
    endShape(0);
}
/*
 */
static int __first_vertex = 1;
// Vertex
void beginShape() {
    __first_vertex = 1;
    printSeparator();
    printf("{ \"tag\": \"path\"\r\n");
    printf(", \"stroke\": \"%s\"\r\n", __stroke);
    printf(", \"fill\": \"%s\"\r\n", __fill);
    printf(", \"stroke-opacity\": \"%f\"\r\n", __strokeOpacity);
    printf(", \"fill-opacity\": \"%f\"\r\n", __fillOpacity);
    printf(", \"stroke-width\": \"%f\"\r\n", __strokeWeight);
    printf(", \"d\": [ ");
}

void endShape(int close) {
    if (close) {
        printf(",[\"Z\"]");
    }
    printf(" ]\r\n");
    printf("}\r\n");
}

void vertex(double x, double y, double z) {

    if (__first_vertex) {
        printf("[\"M\",");
        __first_vertex = 0;
    } else {
        printf(",[\"L\",");
    }
    printf("[%.3f,%.3f,%.3f]", x, y, z);
    printf("]");
}

void qbezierVertex(double cx, double cy, double cz, double x, double y, double z) {
    printf(",[\"Q\",");
    printf("[%.3f,%.3f,%.3f,%.3f,%.3f,%.3f]", cx, cy, cz, x, y, z);
    printf("]");
}

void bezierVertex(double cx1, double cy1, double cz1, double cx2, double cy2, double cz2, double x, double y, double z) {
    printf(",[\"C\",");
    printf("[%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f]", cx1, cy1, cz1, cx1, cy1, cz1, x, y, z);
    printf("]");
}

void curveVertex(double x, double y, double z) {
    // TODO Catmull-Rom splines
}

static int __first_vertex2D = 1;
// Vertex
void begin2DShape() {
    __first_vertex2D = 1;
    printSeparator();
    printf("{ \"tag\": \"path2D\"\r\n");
    printf(", \"thickness\": \"%f\"\r\n", __thickness);
    printf(", \"stroke\": \"%s\"\r\n", __stroke);
    printf(", \"fill\": \"%s\"\r\n", __fill);
    printf(", \"stroke-opacity\": \"%f\"\r\n", __strokeOpacity);
    printf(", \"fill-opacity\": \"%f\"\r\n", __fillOpacity);
    printf(", \"stroke-width\": \"%f\"\r\n", __strokeWeight);
    printf(", \"d\": [ ");
}

void vertex2D(double x, double y) {
    if (__first_vertex2D) {
        printf("[\"M\",");
        __first_vertex2D = 0;
    } else {
        printf(",[\"L\",");
    }
    printf("[%.3f,%.3f]", x, y);
    printf("]");
}

void qbezierVertex2D(double cx, double cy, double x, double y) {
    printf(",[\"Q\",");
    printf("[%.3f,%.3f,%.3f,%.3f]", cx, cy, x, y);
    printf("]");
}

void bezierVertex2D(double cx1, double cy1, double cx2, double cy2, double x, double y) {
    printf(",[\"C\",");
    printf("[%.3f,%.3f,%.3f,%.3f,%.3f,%.3f]", cx1, cy1, cx2, cy2, x, y);
    printf("]");
}

void curveVertex2D(double x, double y) {
    // TODO Catmull-Rom splines
}

void end2DShape() {
    printf(" ]\r\n");
    printf("}\r\n");
}


void rect(double x, double y, double w, double h) {
    pushMatrix();
    translate(x, y, 0);
    printSeparator();
    printf("{ \"tag\": \"rect\"\r\n");
    printf(", \"rect\": [ %.3f, %.3f ]\r\n", w, h);
    printf(", \"thickness\": \"%f\"\r\n", __thickness);
    printf(", \"stroke\": \"%s\"\r\n", __stroke);
    printf(", \"fill\": \"%s\"\r\n", __fill);
    printf(", \"stroke-opacity\": \"%f\"\r\n", __strokeOpacity);
    printf(", \"fill-opacity\": \"%f\"\r\n", __fillOpacity);
    printf(", \"stroke-width\": \"%f\"\r\n", __strokeWeight);
    printf("}\r\n");
    popMatrix();
}

void roundRect(double x, double y, double w, double h, double rw, double rh) {
    pushMatrix();
    translate(x, y, 0);
    printSeparator();
    printf("{ \"tag\": \"rect\"\r\n");
    printf(", \"rect\": [ %.3f, %.3f ]\r\n", w, h);
    printf(", \"radii\": [ %.3f, %.3f ]\r\n", rw, rh);
    printf(", \"thickness\": \"%f\"\r\n", __thickness);
    printf(", \"stroke\": \"%s\"\r\n", __stroke);
    printf(", \"fill\": \"%s\"\r\n", __fill);
    printf(", \"stroke-opacity\": \"%f\"\r\n", __strokeOpacity);
    printf(", \"fill-opacity\": \"%f\"\r\n", __fillOpacity);
    printf(", \"stroke-width\": \"%f\"\r\n", __strokeWeight);
    printf("}\r\n");
    popMatrix();
}

void ellipse(double x, double y, double w, double h) {
    pushMatrix();
    translate(x, y, 0);
    printSeparator();
    printf("{ \"tag\": \"ellipse\"\r\n");
    printf(", \"ellipse\": [ %.3f, %.3f ]\r\n", w, h);
    printf(", \"thickness\": \"%f\"\r\n", __thickness);
    printf(", \"stroke\": \"%s\"\r\n", __stroke);
    printf(", \"fill\": \"%s\"\r\n", __fill);
    printf(", \"stroke-opacity\": \"%f\"\r\n", __strokeOpacity);
    printf(", \"fill-opacity\": \"%f\"\r\n", __fillOpacity);
    printf(", \"stroke-width\": \"%f\"\r\n", __strokeWeight);
    printf("}\r\n");
    popMatrix();
}
