Blame src/drawing.c

Ivan Mahonin 07b70f
Ivan Mahonin 07b70f
#include "private.h"
Ivan Mahonin ba9f06
#include "world.h"
Ivan Mahonin 07b70f
#include "drawing.h"
Ivan Mahonin 07b70f
bw 3c0f7f
Ivan Mahonin 07b70f
static double *path;
Ivan Mahonin 07b70f
static size_t pathSize;
Ivan Mahonin 07b70f
static size_t pathAllocated;
Ivan Mahonin 07b70f
Ivan Mahonin 3e7c5f
Ivan Mahonin 3e7c5f
static HeliDrawingState statesStack[1024];
Ivan Mahonin 3954ba
static int statesStackIndex = 0;
Ivan Mahonin 3954ba
Ivan Mahonin 3954ba
Ivan Mahonin 07b70f
Ivan Mahonin 0b65e7
typedef struct _HeliStrokePoint {
Ivan Mahonin 0b65e7
	double x, y, dx, dy;
Ivan Mahonin 0b65e7
} HeliStrokePoint;
Ivan Mahonin 0b65e7
Ivan Mahonin 0b65e7
typedef struct _HeliStroke {
Ivan Mahonin 0b65e7
	HeliStrokePoint *points;
Ivan Mahonin 0b65e7
	int count;
Ivan Mahonin 0b65e7
	int allocatedCount;
Ivan Mahonin 0b65e7
} HeliStroke;
Ivan Mahonin 0b65e7
Ivan Mahonin ba9f06
static void endPath(int close, int stroke, int fill, int fillSimple);
Ivan Mahonin ba9f06
Ivan Mahonin ba9f06
static void closePathSimple()
Ivan Mahonin ba9f06
	{ endPath(TRUE, TRUE, FALSE, TRUE); }
Ivan Mahonin ba9f06
Ivan Mahonin ba9f06
Ivan Mahonin deef1d
void heliDrawingApplyTexture(double *color, HeliTextureState *state) {
Ivan Mahonin deef1d
	glColor4dv(color);
Ivan Mahonin deef1d
	unsigned int texid = state->animation ? animationGetGLTexId(state->animation) : 0;
Ivan Mahonin deef1d
	if (texid) {
Ivan Mahonin 909bc2
		HeliPair *pair = heliUIntGet(&heliDrawingFramebuffersToFlush, texid);
Ivan Mahonin 909bc2
		if (pair) framebufferFlush((Framebuffer)pair->value);
Ivan Mahonin 909bc2
		
Ivan Mahonin deef1d
		unsigned int mode = state->fixed ? GL_EYE_LINEAR : GL_OBJECT_LINEAR;
Ivan Mahonin deef1d
		unsigned int param = state->fixed ? GL_EYE_PLANE : GL_OBJECT_PLANE;
Ivan Mahonin deef1d
		
Ivan Mahonin deef1d
		double kx = fabs(state->width) > HELI_PRECISION ? 1/state->width
Ivan Mahonin deef1d
		          : state->width < 0 ? -HELI_PRECISION : HELI_PRECISION;
Ivan Mahonin deef1d
		double ky = fabs(state->height) > HELI_PRECISION ? 1/state->height
Ivan Mahonin deef1d
		          : state->height < 0 ? -HELI_PRECISION : HELI_PRECISION;
Ivan Mahonin deef1d
		double x[4] = { kx, 0, 0, -state->x*kx };
Ivan Mahonin deef1d
		double y[4] = { 0, ky, 0, -state->y*ky };
Ivan Mahonin deef1d
		
Ivan Mahonin deef1d
		glEnable(GL_TEXTURE_2D);
Ivan Mahonin deef1d
		glBindTexture(GL_TEXTURE_2D, texid);
Ivan Mahonin deef1d
		glEnable(GL_TEXTURE_GEN_S);
Ivan Mahonin deef1d
		glEnable(GL_TEXTURE_GEN_T);
Ivan Mahonin deef1d
		glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, mode);
Ivan Mahonin deef1d
		glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, mode);
Ivan Mahonin deef1d
		glTexGendv(GL_S, param, x);
Ivan Mahonin deef1d
		glTexGendv(GL_T, param, y);
Ivan Mahonin deef1d
	}
Ivan Mahonin deef1d
}
Ivan Mahonin deef1d
Ivan Mahonin deef1d
void heliDrawingResetTexture() {
Ivan Mahonin deef1d
	glBindTexture(GL_TEXTURE_2D, 0);
Ivan Mahonin deef1d
	glDisable(GL_TEXTURE_GEN_S);
Ivan Mahonin deef1d
	glDisable(GL_TEXTURE_GEN_T);
Ivan Mahonin deef1d
	glDisable(GL_TEXTURE_2D);
Ivan Mahonin deef1d
	glColor4d(1, 1, 1, 1);
Ivan Mahonin deef1d
}
Ivan Mahonin deef1d
Ivan Mahonin deef1d
Ivan Mahonin d6f40c
void background(unsigned int colorCode) {
Ivan Mahonin d6f40c
	double color[4];
Ivan Mahonin d6f40c
	heliColorToDouble(colorCode, color);
Ivan Mahonin d6f40c
	glClearColor(color[0], color[1], color[2], color[3]);
Ivan Mahonin d6f40c
}
Ivan Mahonin d6f40c
void clear()
Ivan Mahonin d6f40c
	{ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); }
Ivan Mahonin d6f40c
Ivan Mahonin d4e89f
void fill(unsigned int colorCode)
Ivan Mahonin d4e89f
	{ heliColorToDouble(colorCode, heliDrawingGetState()->fillColor); }
Ivan Mahonin deef1d
void fillTexture(Animation animation, double x, double y, double width, double height, int fixed) {
Ivan Mahonin deef1d
	HeliTextureState *s = &heliDrawingGetState()->fillTexture; 
Ivan Mahonin deef1d
	s->animation = animation;
Ivan Mahonin deef1d
	s->x = x;
Ivan Mahonin deef1d
	s->y = y;
Ivan Mahonin deef1d
	s->width = width;
Ivan Mahonin deef1d
	s->height = height;
Ivan Mahonin deef1d
	s->fixed = fixed != 0;
Ivan Mahonin deef1d
}
Ivan Mahonin 3e7c5f
void noFillColor()
Ivan Mahonin 3e7c5f
	{ fill(0); }
Ivan Mahonin 3e7c5f
void noFillTexture()
Ivan Mahonin 3e7c5f
	{ fillTexture(NULL, 0, 0, 1, 1, FALSE); }
Ivan Mahonin 53e18e
void noFill()
Ivan Mahonin 3e7c5f
	{ noFillColor(); noFillTexture(); }
Ivan Mahonin deef1d
Ivan Mahonin d4e89f
void stroke(unsigned int colorCode)
Ivan Mahonin d4e89f
	{ heliColorToDouble(colorCode, heliDrawingGetState()->strokeColor); }
Ivan Mahonin deef1d
void strokeTexture(Animation animation, double x, double y, double width, double height, int fixed) {
Ivan Mahonin deef1d
	HeliTextureState *s = &heliDrawingGetState()->strokeTexture; 
Ivan Mahonin deef1d
	s->animation = animation;
Ivan Mahonin deef1d
	s->x = x;
Ivan Mahonin deef1d
	s->y = y;
Ivan Mahonin deef1d
	s->width = width;
Ivan Mahonin deef1d
	s->height = height;
Ivan Mahonin deef1d
	s->fixed = fixed != 0;
Ivan Mahonin deef1d
}
Ivan Mahonin 3e7c5f
void noStrokeColor()
Ivan Mahonin 3e7c5f
	{ stroke(0); }
Ivan Mahonin 3e7c5f
void noStrokeTexture()
Ivan Mahonin 3e7c5f
	{ strokeTexture(NULL, 0, 0, 1, 1, FALSE); }
Ivan Mahonin 53e18e
void noStroke()
Ivan Mahonin 3e7c5f
	{ noStrokeColor(); noStrokeTexture(); }
Ivan Mahonin 07b70f
Ivan Mahonin deef1d
void strokeWidth(double width)
Ivan Mahonin deef1d
	{ heliDrawingGetState()->strokeWidth = width; }
Ivan Mahonin 07b70f
Ivan Mahonin 546e3a
const char* rgba(double r, double g, double b, double a) {
Ivan Mahonin 07b70f
	static char buf[1024];
Ivan Mahonin 07b70f
	snprintf(buf, sizeof(buf) - 1, "%f %f %f %f", r, g, b, a);
Ivan Mahonin 07b70f
	return buf;
Ivan Mahonin 07b70f
}
Ivan Mahonin 07b70f
Ivan Mahonin 546e3a
const char* rgb(double r, double g, double b)
Ivan Mahonin 07b70f
	{ return rgba(r, g, b, 1); }
Ivan Mahonin 07b70f
Ivan Mahonin 8bc1f1
void translate(double x, double y)
Ivan Mahonin 8bc1f1
	{ glTranslated(x, y, 0); }
Ivan Mahonin 8bc1f1
void rotate(double angle)
Ivan Mahonin 8bc1f1
	{ glRotated(angle, 0, 0, 1); }
Ivan Mahonin 8bc1f1
void scale(double x, double y)
Ivan Mahonin 8bc1f1
	{ glScaled(x, y, 1); }
Ivan Mahonin 8bc1f1
void zoom(double z)
Ivan Mahonin 8bc1f1
	{ scale(z, z); }
Ivan Mahonin ba5c91
void noTransform()
Ivan Mahonin ba5c91
	{ glLoadIdentity(); }
Ivan Mahonin ba5c91
Ivan Mahonin 8bc1f1
Ivan Mahonin 8bc1f1
void cliprect(double x, double y, double width, double height) {
Ivan Mahonin 8bc1f1
	double eq[4][4] = {
Ivan Mahonin 8bc1f1
		{ 1, 0, 0, -x        },
Ivan Mahonin 8bc1f1
		{-1, 0, 0,  x+width  },
Ivan Mahonin 8bc1f1
		{ 0, 1, 0, -y        },
Ivan Mahonin 8bc1f1
		{ 0,-1, 0,  y+height } };
Ivan Mahonin 8bc1f1
	for(int i = 0; i < 4; ++i) {
Ivan Mahonin 8bc1f1
		glEnable(GL_CLIP_PLANE0 + i);
Ivan Mahonin 8bc1f1
		glClipPlane(GL_CLIP_PLANE0 + i, eq[i]);
Ivan Mahonin 8bc1f1
	}
Ivan Mahonin 8bc1f1
}
Ivan Mahonin 8bc1f1
Ivan Mahonin 8bc1f1
void noClip()
Ivan Mahonin 8bc1f1
	{ for(int i = 0; i < 4; ++i) glDisable(GL_CLIP_PLANE0 + i); }
Ivan Mahonin 8bc1f1
Ivan Mahonin 1d641c
Ivan Mahonin 1d641c
void projectionByViewport() {
Ivan Mahonin 89ec47
	unsigned int framebuffer_id = 0;
Ivan Mahonin 89ec47
	glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (int*)&framebuffer_id);
Ivan Mahonin 1d641c
	int vp[4] = {};
Ivan Mahonin 1d641c
	glGetIntegerv(GL_VIEWPORT, vp);
Ivan Mahonin 1d641c
	unsigned int mode;
Ivan Mahonin 1d641c
	glGetIntegerv(GL_MATRIX_MODE, (int*)&mode);
Ivan Mahonin 1d641c
	glMatrixMode(GL_PROJECTION);
Ivan Mahonin 1d641c
	glLoadIdentity();
Ivan Mahonin 89ec47
	if (framebuffer_id)
Ivan Mahonin 89ec47
		glOrtho(0, vp[2], 0, vp[3], -1.0, 1.0);
Ivan Mahonin 89ec47
	else
Ivan Mahonin 89ec47
		glOrtho(0, vp[2], vp[3], 0, -1.0, 1.0);
Ivan Mahonin 1d641c
	glMatrixMode(mode);
Ivan Mahonin 1d641c
}
Ivan Mahonin 1d641c
Ivan Mahonin 1d641c
Ivan Mahonin 1d641c
void viewportByFramebuffer(Framebuffer framebuffer) {
Ivan Mahonin 1d641c
	if (framebuffer)
Ivan Mahonin 1d641c
		glViewport(0, 0, framebufferGetWidth(framebuffer), framebufferGetHeight(framebuffer));
Ivan Mahonin 1d641c
	else
Ivan Mahonin 1d641c
		glViewport(0, 0, worldGetWidth(), worldGetHeight());
Ivan Mahonin 1d641c
}
Ivan Mahonin 1d641c
Ivan Mahonin 1d641c
Ivan Mahonin 1d641c
void viewportByWindow()
Ivan Mahonin 1d641c
	{ viewportByFramebuffer(NULL); }
Ivan Mahonin 1d641c
Ivan Mahonin 1d641c
Ivan Mahonin 1d641c
void targetEx(Framebuffer framebuffer, int updateViewport, int updateProjection) {
Ivan Mahonin 1d641c
	if (framebuffer) {
Ivan Mahonin 1d641c
		if (heliGLBindFramebufferPtr) {
Ivan Mahonin 1d641c
			unsigned int id = framebufferGetGLId(framebuffer);
Ivan Mahonin 1d641c
			heliGLBindFramebufferPtr(GL_READ_FRAMEBUFFER, id);
Ivan Mahonin 1d641c
			heliGLBindFramebufferPtr(GL_DRAW_FRAMEBUFFER, id);
Ivan Mahonin 909bc2
			unsigned int texid = framebufferGetGLTexId(framebuffer);
Ivan Mahonin 909bc2
			if (texid) heliUIntAdd(&heliDrawingFramebuffersToFlush, texid, framebuffer, NULL);
Ivan Mahonin 1d641c
		}
Ivan Mahonin 1d641c
		if (updateViewport) {
Ivan Mahonin 1d641c
			viewportByFramebuffer(framebuffer);
Ivan Mahonin 1d641c
			if (updateProjection) projectionByViewport();
Ivan Mahonin 1d641c
		}
Ivan Mahonin 1d641c
	} else {
Ivan Mahonin 1d641c
		if (heliGLBindFramebufferPtr) {
Ivan Mahonin 1d641c
			heliGLBindFramebufferPtr(GL_READ_FRAMEBUFFER, heliGLWindowFramebufferReadId);
Ivan Mahonin 1d641c
			heliGLBindFramebufferPtr(GL_DRAW_FRAMEBUFFER, heliGLWindowFramebufferDrawId);
Ivan Mahonin 1d641c
		}
Ivan Mahonin 1d641c
		if (updateViewport) {
Ivan Mahonin 1d641c
			viewportByWindow();
Ivan Mahonin 1d641c
			if (updateProjection) projectionByViewport();
Ivan Mahonin 1d641c
		}
Ivan Mahonin 1d641c
	}
Ivan Mahonin 1d641c
}
Ivan Mahonin 1d641c
void target(Framebuffer framebuffer)
Ivan Mahonin 1d641c
	{ targetEx(framebuffer, TRUE, TRUE); }
Ivan Mahonin 1d641c
void noTarget()
Ivan Mahonin 1d641c
	{ target(NULL); }
Ivan Mahonin 1d641c
Ivan Mahonin 1d641c
Ivan Mahonin 07b70f
void rect(double x, double y, double width, double height) {
Ivan Mahonin 07b70f
	resetPath();
Ivan Mahonin deef1d
	heliFillRectSimple(x, y, x+width, y+height, heliGLGetAAResolution());
Ivan Mahonin 07b70f
	moveTo(x, y);
Ivan Mahonin 07b70f
	lineTo(x + width, y);
Ivan Mahonin 07b70f
	lineTo(x + width, y + height);
Ivan Mahonin 07b70f
	lineTo(x, y + height);
Ivan Mahonin deef1d
	endPath(TRUE, TRUE, FALSE, FALSE);
Ivan Mahonin 07b70f
}
Ivan Mahonin 07b70f
Ivan Mahonin f8dca4
void rectTextured(Animation animation, double x, double y, double width, double height) {
Ivan Mahonin f8dca4
	saveState();
Ivan Mahonin f8dca4
	fillTexture(animation, x, y, width, height, FALSE);
Ivan Mahonin f8dca4
	rect(x, y, width, height);
Ivan Mahonin f8dca4
	restoreState();
Ivan Mahonin f8dca4
}
Ivan Mahonin f8dca4
Ivan Mahonin 07b70f
void line(double x1, double y1, double x2, double y2) {
Ivan Mahonin 07b70f
	resetPath();
Ivan Mahonin 07b70f
	moveTo(x1, y1);
Ivan Mahonin 07b70f
	lineTo(x2, y2);
Ivan Mahonin 07b70f
	strokePath();
Ivan Mahonin 07b70f
}
Ivan Mahonin 07b70f
Ivan Mahonin 07b70f
void ellipse(double x, double y, double width, double height) {
Ivan Mahonin 07b70f
	resetPath();
Ivan Mahonin deef1d
	if (fabs(fabs(width) - fabs(height)) <= HELI_PRECISION) {
Ivan Mahonin deef1d
		heliFillCircleSimple(x + 0.5*width, y + 0.5*height, 0.5*width, heliGLGetAAResolution());
Ivan Mahonin deef1d
		arcPath(x, y, width, height, 0, 360);
Ivan Mahonin deef1d
		endPath(TRUE, TRUE, FALSE, FALSE);
Ivan Mahonin deef1d
	} else {
Ivan Mahonin deef1d
		arcPath(x, y, width, height, 0, 360);
Ivan Mahonin deef1d
		closePathSimple();
Ivan Mahonin deef1d
	}
Ivan Mahonin 07b70f
}
Ivan Mahonin 07b70f
Ivan Mahonin ba9f06
void point(double x, double y) {
Ivan Mahonin deef1d
	saveStateEx(STATE_FILL | STATE_STROKE);
Ivan Mahonin 3954ba
	HeliDrawingState *s = heliDrawingGetState();
Ivan Mahonin deef1d
	memcpy(&s->fillColor, &s->strokeColor, sizeof(s->fillColor));
Ivan Mahonin deef1d
	memcpy(&s->fillTexture, &s->strokeTexture, sizeof(s->fillTexture));
Ivan Mahonin deef1d
	noStroke();
Ivan Mahonin deef1d
	if (s->strokeWidth > HELI_PRECISION)
Ivan Mahonin deef1d
		ellipse(x - s->strokeWidth*0.5, y - s->strokeWidth*0.5, s->strokeWidth, s->strokeWidth);
Ivan Mahonin deef1d
	restoreState();
Ivan Mahonin ba9f06
}
Ivan Mahonin 07b70f
Ivan Mahonin 07b70f
void arcPath(double x, double y, double w, double h, double start, double stop) {
Ivan Mahonin ba9f06
	w *= 0.5; x += w;
Ivan Mahonin ba9f06
	h *= 0.5; y += h;
Ivan Mahonin 0b65e7
	if (fabs(w) < HELI_PRECISION || fabs(h) < HELI_PRECISION) return;
Ivan Mahonin 0b65e7
	double step = fabs(w) > fabs(h) ? 1/fabs(w) : 1/fabs(h);
Ivan Mahonin 0b65e7
	if (step > PI/180) step = PI/180;
Ivan Mahonin 07b70f
	start *= PI/180;
Ivan Mahonin 07b70f
	stop *= PI/180;
Ivan Mahonin ba9f06
	double a = start;
Ivan Mahonin ba9f06
	if (start < stop) {
Ivan Mahonin ba9f06
		while(1) {
Ivan Mahonin ba9f06
			if (a > stop) a = stop;
Ivan Mahonin ba9f06
			lineTo(x + cos(a)*w, y + sin(a)*h);
Ivan Mahonin ba9f06
			if (a == stop) break;
Ivan Mahonin ba9f06
			a += step;
Ivan Mahonin ba9f06
		}
Ivan Mahonin ba9f06
	} else {
Ivan Mahonin ba9f06
		while(1) {
Ivan Mahonin ba9f06
			if (a < stop) a = stop;
Ivan Mahonin ba9f06
			lineTo(x + cos(a)*w, y + sin(a)*h);
Ivan Mahonin ba9f06
			if (a == stop) break;
Ivan Mahonin ba9f06
			a -= step;
Ivan Mahonin ba9f06
		}
Ivan Mahonin 07b70f
	}
Ivan Mahonin 07b70f
}
Ivan Mahonin 07b70f
Ivan Mahonin 07b70f
void arc(double x, double y, double w, double h, double start, double stop) {
Ivan Mahonin 07b70f
	resetPath();
Ivan Mahonin 07b70f
	arcPath(x, y, w, h, start, stop);
Ivan Mahonin 07b70f
	strokePath();
Ivan Mahonin 07b70f
}
Ivan Mahonin 07b70f
Ivan Mahonin 07b70f
void regularPolygon(double x, double y, int sides, double size) {
Ivan Mahonin 07b70f
	resetPath();
Ivan Mahonin 07b70f
	size *= 0.5;
Ivan Mahonin 07b70f
	moveTo(x + size, y);
Ivan Mahonin 07b70f
	for(int i = 1; i < sides; ++i) {
Ivan Mahonin ba9f06
		double a = i*2*PI/sides;
Ivan Mahonin 07b70f
		lineTo(x + size*cos(a), y + size*sin(a));
Ivan Mahonin 07b70f
	}
Ivan Mahonin ba9f06
	closePathSimple();
Ivan Mahonin 07b70f
}
Ivan Mahonin 07b70f
Ivan Mahonin 07b70f
Ivan Mahonin 07b70f
void resetPath()
Ivan Mahonin 07b70f
	{ pathSize = 0; }
Ivan Mahonin 07b70f
Ivan Mahonin deef1d
Ivan Mahonin deef1d
void heliFillCircleSimple(double x, double y, double r, double aaBorder) {
Ivan Mahonin deef1d
	r = fabs(r);
Ivan Mahonin deef1d
	if (r < HELI_PRECISION) return;
Ivan Mahonin deef1d
	
Ivan Mahonin deef1d
	HeliDrawingState *s = heliDrawingGetState();
Ivan Mahonin deef1d
	if (s->fillColor[3] <= HELI_PRECISION) return;
Ivan Mahonin deef1d
	
Ivan Mahonin deef1d
	heliDrawingApplyTexture(s->fillColor, &s->fillTexture);
Ivan Mahonin deef1d
	
Ivan Mahonin deef1d
	double step = 1/r;
Ivan Mahonin deef1d
	if (step > PI/180) step = PI/180;
Ivan Mahonin deef1d
	if (aaBorder < HELI_PRECISION) {
Ivan Mahonin deef1d
		glEnable(GL_MULTISAMPLE);
Ivan Mahonin deef1d
		glBegin(GL_TRIANGLE_FAN);
Ivan Mahonin deef1d
		for(double a = 0; a < 2*PI; a += step) glVertex2d(x + cos(a)*r, y + sin(a)*r);
Ivan Mahonin deef1d
		glEnd();
Ivan Mahonin deef1d
		glDisable(GL_MULTISAMPLE);
Ivan Mahonin deef1d
	} else {
Ivan Mahonin deef1d
		double color[4] = { s->fillColor[0], s->fillColor[1], s->fillColor[2], s->fillColor[3] };
Ivan Mahonin deef1d
		double sideColor[4] = { color[0], color[1], color[2], 0 };
Ivan Mahonin deef1d
		double r1 = r + 0.5*aaBorder;
Ivan Mahonin deef1d
		double r0 = r - 0.5*aaBorder;
Ivan Mahonin deef1d
		if (r0 > HELI_PRECISION) {
Ivan Mahonin deef1d
			glBegin(GL_TRIANGLE_FAN);
Ivan Mahonin deef1d
			for(double a = 0; a < 2*PI; a += step) glVertex2d(x + cos(a)*r0, y + sin(a)*r0);
Ivan Mahonin deef1d
			glEnd();
Ivan Mahonin deef1d
		} else {
Ivan Mahonin deef1d
			color[3] *= r/aaBorder;
Ivan Mahonin deef1d
			r0 = 0;
Ivan Mahonin deef1d
		}
Ivan Mahonin deef1d
		glBegin(GL_TRIANGLE_STRIP);
Ivan Mahonin deef1d
		for(double a = step; a < 2*PI; a += step) {
Ivan Mahonin deef1d
			double s = sin(a), c = cos(a);
Ivan Mahonin deef1d
			glColor4dv(color);
Ivan Mahonin deef1d
			glVertex2d(x + c*r0, y + s*r0);
Ivan Mahonin deef1d
			glColor4dv(sideColor);
Ivan Mahonin deef1d
			glVertex2d(x + c*r1, y + s*r1);
Ivan Mahonin deef1d
		}
Ivan Mahonin deef1d
		glEnd();
Ivan Mahonin deef1d
	}
Ivan Mahonin deef1d
}
Ivan Mahonin deef1d
Ivan Mahonin deef1d
Ivan Mahonin deef1d
void heliFillRectSimple(double x0, double y0, double x1, double y1, double aaBorder) {
Ivan Mahonin deef1d
	if (fabs(x1 - x0) < HELI_PRECISION || fabs(y1 - y0) < HELI_PRECISION) return;
Ivan Mahonin deef1d
	
Ivan Mahonin deef1d
	HeliDrawingState *s = heliDrawingGetState();
Ivan Mahonin deef1d
	if (s->fillColor[3] <= HELI_PRECISION) return;
Ivan Mahonin deef1d
	
Ivan Mahonin deef1d
	heliDrawingApplyTexture(s->fillColor, &s->fillTexture);
Ivan Mahonin deef1d
	if (aaBorder <= HELI_PRECISION) {
Ivan Mahonin deef1d
		glEnable(GL_MULTISAMPLE);
Ivan Mahonin deef1d
		glBegin(GL_QUADS);
Ivan Mahonin deef1d
		glVertex2d(x0, y0);
Ivan Mahonin deef1d
		glVertex2d(x1, y0);
Ivan Mahonin deef1d
		glVertex2d(x1, y1);
Ivan Mahonin deef1d
		glVertex2d(x0, y1);
Ivan Mahonin deef1d
		glEnd();
Ivan Mahonin deef1d
		glDisable(GL_MULTISAMPLE);
Ivan Mahonin deef1d
	} else {
Ivan Mahonin deef1d
		double color[4] = { s->fillColor[0], s->fillColor[1], s->fillColor[2], s->fillColor[3] };
Ivan Mahonin deef1d
		double sideColor[4] = { color[0], color[1], color[2], 0 };
Ivan Mahonin deef1d
		
Ivan Mahonin deef1d
		if (x1 < x0) { double x = x0; x0 = x1; x1 = x; }
Ivan Mahonin deef1d
		if (y1 < y0) { double y = y0; y0 = y1; y1 = y; }
Ivan Mahonin deef1d
		double x  = (x1 + x0)*0.5;
Ivan Mahonin deef1d
		double y  = (y1 + y0)*0.5;
Ivan Mahonin deef1d
		double hw = (x1 - x0)*0.5;
Ivan Mahonin deef1d
		double hh = (y1 - y0)*0.5;
Ivan Mahonin deef1d
		
Ivan Mahonin f8dca4
		double k0 = 0;
Ivan Mahonin f8dca4
		double k1 = 0.5;
Ivan Mahonin f8dca4
		double w0 = hw - k0*aaBorder;
Ivan Mahonin f8dca4
		double w1 = hw + k1*aaBorder;
Ivan Mahonin deef1d
		if (w0 < HELI_PRECISION) w0 = 0;
Ivan Mahonin f8dca4
		double h0 = hh - k0*aaBorder;
Ivan Mahonin f8dca4
		double h1 = hh + k1*aaBorder;
Ivan Mahonin deef1d
		if (h0 < HELI_PRECISION) h0 = 0;
Ivan Mahonin deef1d
		
Ivan Mahonin f8dca4
		const double k = (w0 ? 1 : w1/((k0+k1)*aaBorder))
Ivan Mahonin f8dca4
		               * (h0 ? 1 : h1/((k0+k1)*aaBorder));
Ivan Mahonin deef1d
		color[3] *= k;
Ivan Mahonin deef1d
		
Ivan Mahonin deef1d
		const int vcnt = 14;
Ivan Mahonin deef1d
		
Ivan Mahonin deef1d
		const double vertices[][2] = {
Ivan Mahonin f8dca4
			{ x-w1, y-h1 }, { x-w0, y-h0 },
Ivan Mahonin f8dca4
			{ x+w1, y-h1 }, { x+w0, y-h0 },
Ivan Mahonin f8dca4
			{ x+w1, y+h1 }, { x+w0, y+h0 },
Ivan Mahonin f8dca4
			{ x-w1, y+h1 }, { x-w0, y+h0 },
Ivan Mahonin f8dca4
			{ x-w1, y-h1 }, { x-w0, y-h0 },
Ivan Mahonin f8dca4
			{ x-w0, y-h0 },
Ivan Mahonin f8dca4
			{ x+w0, y-h0 },
Ivan Mahonin f8dca4
			{ x-w0, y+h0 },
Ivan Mahonin f8dca4
			{ x+w0, y+h0 } };
Ivan Mahonin deef1d
		
Ivan Mahonin deef1d
		const double *colors[] = {
Ivan Mahonin deef1d
			sideColor, color,
Ivan Mahonin deef1d
			sideColor, color,
Ivan Mahonin deef1d
			sideColor, color,
Ivan Mahonin deef1d
			sideColor, color,
Ivan Mahonin deef1d
			sideColor, color,
Ivan Mahonin deef1d
			color,
Ivan Mahonin deef1d
			color,
Ivan Mahonin deef1d
			color,
Ivan Mahonin deef1d
			color };
Ivan Mahonin deef1d
		
Ivan Mahonin deef1d
		glBegin(GL_TRIANGLE_STRIP);
Ivan Mahonin deef1d
		for(int i = 0; i < vcnt; ++i) {
Ivan Mahonin deef1d
			glColor4dv(colors[i]);
Ivan Mahonin deef1d
			glVertex2dv(vertices[i]);
Ivan Mahonin deef1d
		}
Ivan Mahonin deef1d
		glEnd();
Ivan Mahonin deef1d
	}
Ivan Mahonin deef1d
	heliDrawingResetTexture();
Ivan Mahonin deef1d
}
Ivan Mahonin deef1d
Ivan Mahonin deef1d
Ivan Mahonin ba9f06
static void drawFillSimple() {
Ivan Mahonin 3954ba
	HeliDrawingState *s = heliDrawingGetState();
Ivan Mahonin deef1d
	if (s->fillColor[3] <= HELI_PRECISION) return;
Ivan Mahonin ba9f06
	if (pathSize < 6) return;
Ivan Mahonin ba9f06
	
Ivan Mahonin deef1d
	heliDrawingApplyTexture(s->fillColor, &s->fillTexture);
Ivan Mahonin ba9f06
	glEnable(GL_MULTISAMPLE);
Ivan Mahonin ba9f06
	glBegin(GL_TRIANGLE_FAN);
Ivan Mahonin ba9f06
	for(int i = 0; i < pathSize; i += 2)
Ivan Mahonin ba9f06
		glVertex2dv(&path[i]);
Ivan Mahonin ba9f06
	glEnd();
Ivan Mahonin ba9f06
	glDisable(GL_MULTISAMPLE);
Ivan Mahonin deef1d
	heliDrawingResetTexture();
Ivan Mahonin ba9f06
}
Ivan Mahonin ba9f06
Ivan Mahonin ba9f06
static void drawFill() {
Ivan Mahonin 3954ba
	HeliDrawingState *s = heliDrawingGetState();
Ivan Mahonin deef1d
	if (s->fillColor[3] <= HELI_PRECISION) return;
Ivan Mahonin ba9f06
	if (pathSize < 6) return;
Ivan Mahonin ba9f06
	
Ivan Mahonin ba9f06
	double l = path[0], r = l;
Ivan Mahonin ba9f06
	double t = path[1], b = t;
Ivan Mahonin ba9f06
	for(int i = 2; i < pathSize; i += 2) {
Ivan Mahonin ba9f06
		if (l > path[i]) l = path[i];
Ivan Mahonin ba9f06
		if (r < path[i]) r = path[i];
Ivan Mahonin ba9f06
		if (t > path[i + 1]) t = path[i + 1];
Ivan Mahonin ba9f06
		if (b < path[i + 1]) b = path[i + 1];
Ivan Mahonin 07b70f
	}
Ivan Mahonin ba9f06
	if ( r - l <= HELI_PRECISION
Ivan Mahonin ba9f06
	  || b - t <= HELI_PRECISION) return;
Ivan Mahonin ba9f06
	l -= 1; r += 1;
Ivan Mahonin ba9f06
	t -= 1; b += 1;
Ivan Mahonin ba9f06
	
Ivan Mahonin ba9f06
	glEnable(GL_MULTISAMPLE);
Ivan Mahonin ba9f06
	glEnable(GL_STENCIL_TEST);
Ivan Mahonin ba9f06
	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
Ivan Mahonin ba9f06
Ivan Mahonin ba9f06
	glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
Ivan Mahonin ba9f06
	glBegin(GL_QUADS);
Ivan Mahonin ba9f06
	glVertex2d(l, t);
Ivan Mahonin ba9f06
	glVertex2d(r, t);
Ivan Mahonin ba9f06
	glVertex2d(r, b);
Ivan Mahonin ba9f06
	glVertex2d(l, b);
Ivan Mahonin ba9f06
	glEnd();
Ivan Mahonin ba9f06
	
Ivan Mahonin 1d641c
	if (heliGLStencilOpSeparatePtr) {
Ivan Mahonin 1d641c
		heliGLStencilOpSeparatePtr(GL_FRONT, GL_INCR_WRAP, GL_INCR_WRAP, GL_INCR_WRAP);
Ivan Mahonin 1d641c
		heliGLStencilOpSeparatePtr(GL_BACK, GL_DECR_WRAP, GL_DECR_WRAP, GL_DECR_WRAP);
Ivan Mahonin 87fe10
		glBegin(GL_TRIANGLE_FAN);
Ivan Mahonin 87fe10
		for(int i = 0; i < pathSize; i += 2)
Ivan Mahonin 87fe10
			glVertex2dv(&path[i]);
Ivan Mahonin 87fe10
		glEnd();
Ivan Mahonin 87fe10
	} else {
Ivan Mahonin 87fe10
		glEnable(GL_CULL_FACE);
Ivan Mahonin 87fe10
		
Ivan Mahonin 87fe10
		glStencilOp(GL_DECR_WRAP, GL_DECR_WRAP, GL_DECR_WRAP);
Ivan Mahonin 87fe10
		glCullFace(GL_FRONT);
Ivan Mahonin 87fe10
		glBegin(GL_TRIANGLE_FAN);
Ivan Mahonin 87fe10
		for(int i = 0; i < pathSize; i += 2)
Ivan Mahonin 87fe10
			glVertex2dv(&path[i]);
Ivan Mahonin 87fe10
		glEnd();
Ivan Mahonin 87fe10
		
Ivan Mahonin 87fe10
		glStencilOp(GL_INCR_WRAP, GL_INCR_WRAP, GL_INCR_WRAP);
Ivan Mahonin 87fe10
		glCullFace(GL_BACK);
Ivan Mahonin 87fe10
		glBegin(GL_TRIANGLE_FAN);
Ivan Mahonin 87fe10
		for(int i = 0; i < pathSize; i += 2)
Ivan Mahonin 87fe10
			glVertex2dv(&path[i]);
Ivan Mahonin 87fe10
		glEnd();
Ivan Mahonin 87fe10
		
Ivan Mahonin 87fe10
		glDisable(GL_CULL_FACE);
Ivan Mahonin 87fe10
	}
Ivan Mahonin ba9f06
	
Ivan Mahonin ba9f06
	glStencilFunc(GL_NOTEQUAL, 0, -1u);
Ivan Mahonin ba9f06
	glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
Ivan Mahonin ba9f06
	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
Ivan Mahonin deef1d
Ivan Mahonin deef1d
	heliDrawingApplyTexture(s->fillColor, &s->fillTexture);
Ivan Mahonin ba9f06
	glBegin(GL_QUADS);
Ivan Mahonin ba9f06
	glVertex2d(l, t);
Ivan Mahonin ba9f06
	glVertex2d(r, t);
Ivan Mahonin ba9f06
	glVertex2d(r, b);
Ivan Mahonin ba9f06
	glVertex2d(l, b);
Ivan Mahonin ba9f06
	glEnd();
Ivan Mahonin deef1d
	heliDrawingResetTexture();
Ivan Mahonin ba9f06
	
Ivan Mahonin ba9f06
	glStencilFunc(GL_ALWAYS, 0, -1u);
Ivan Mahonin ba9f06
	glDisable(GL_STENCIL_TEST);
Ivan Mahonin ba9f06
	glDisable(GL_MULTISAMPLE);
Ivan Mahonin ba9f06
}
Ivan Mahonin ba9f06
Ivan Mahonin 0b65e7
static void strokeAddPoint(HeliStroke *stroke, double x, double y, double dx, double dy) {
Ivan Mahonin 0b65e7
	if (stroke->allocatedCount < stroke->count + 1) {
Ivan Mahonin 0b65e7
		stroke->allocatedCount += stroke->allocatedCount/4 + 32;
Ivan Mahonin 0b65e7
		stroke->points = realloc(stroke->points, stroke->allocatedCount*4*sizeof(*stroke->points));
Ivan Mahonin 0b65e7
		memset(stroke->points + stroke->count, 0, (stroke->allocatedCount - stroke->count)*sizeof(*stroke->points));
Ivan Mahonin 0b65e7
	}
Ivan Mahonin 0b65e7
	HeliStrokePoint *p = &stroke->points[ stroke->count++ ];
Ivan Mahonin 0b65e7
	p->x = x;
Ivan Mahonin 0b65e7
	p->y = y;
Ivan Mahonin 0b65e7
	p->dx = dx;
Ivan Mahonin 0b65e7
	p->dy = dy;
Ivan Mahonin 0b65e7
}
Ivan Mahonin 0b65e7
Ivan Mahonin 0b65e7
static void strokeAddCornerSub(HeliStroke *stroke, double dx, double dy, double precisionSqr, int level) {
Ivan Mahonin 0b65e7
	assert(stroke->count > 0);
Ivan Mahonin 0b65e7
	HeliStrokePoint *prev = stroke->points + stroke->count - 1;
Ivan Mahonin 0b65e7
	if (level > 0) {
Ivan Mahonin 0b65e7
		double distx = dx - prev->dx;
Ivan Mahonin 0b65e7
		double disty = dy - prev->dy;
Ivan Mahonin 0b65e7
		if (distx*distx + disty*disty > precisionSqr) {
Ivan Mahonin 0b65e7
			double mx = prev->dx + dx;
Ivan Mahonin 0b65e7
			double my = prev->dy + dy;
Ivan Mahonin 0b65e7
			double lsqr = mx*mx + my*my;
Ivan Mahonin 0b65e7
			if (lsqr > HELI_PRECISION_SQR) {
Ivan Mahonin 0b65e7
				double kl = 1/sqrt(lsqr);
Ivan Mahonin 0b65e7
				mx *= kl;
Ivan Mahonin 0b65e7
				my *= kl;
Ivan Mahonin 0b65e7
				strokeAddCornerSub(stroke, mx, my, precisionSqr, level-1);
Ivan Mahonin 0b65e7
				strokeAddCornerSub(stroke, dx, dy, precisionSqr, level-1);
Ivan Mahonin 0b65e7
				return;
Ivan Mahonin 0b65e7
			}
Ivan Mahonin 0b65e7
		}
Ivan Mahonin 0b65e7
	}
Ivan Mahonin 0b65e7
	strokeAddPoint(stroke, prev->x, prev->y, dx, dy);
Ivan Mahonin 0b65e7
}
Ivan Mahonin 0b65e7
Ivan Mahonin 0b65e7
static void strokeAddCorner(
Ivan Mahonin 0b65e7
	HeliStroke *stroke,
Ivan Mahonin 0b65e7
	double xp, double yp,
Ivan Mahonin 0b65e7
	double x0, double y0,
Ivan Mahonin 0b65e7
	double x1, double y1,
Ivan Mahonin 0b65e7
	double precisionSqr )
Ivan Mahonin 0b65e7
{
Ivan Mahonin 0b65e7
	double dx0 = x0 - xp;
Ivan Mahonin 0b65e7
	double dy0 = y0 - yp;
Ivan Mahonin 0b65e7
	double dx1 = x1 - x0;
Ivan Mahonin 0b65e7
	double dy1 = y1 - y0;
Ivan Mahonin 0b65e7
	
Ivan Mahonin 0b65e7
	double l0 = sqrt(dx0*dx0 + dy0*dy0);
Ivan Mahonin 0b65e7
	double l1 = sqrt(dx1*dx1 + dy1*dy1);
Ivan Mahonin 0b65e7
	assert(l0 > HELI_PRECISION);
Ivan Mahonin 0b65e7
	assert(l1 > HELI_PRECISION);
Ivan Mahonin 0b65e7
	
Ivan Mahonin 0b65e7
	double kl0 = 1/l0, kl1 = 1/l1;
Ivan Mahonin 0b65e7
	double nx0 = dx0*kl0, ny0 = dy0*kl0;
Ivan Mahonin 0b65e7
	double nx1 = dx1*kl1, ny1 = dy1*kl1;
Ivan Mahonin 89ec47
	assert(fabs(sqrt(nx0*nx0 + ny0*ny0) - 1) <= HELI_PRECISION);
Ivan Mahonin 89ec47
	assert(fabs(sqrt(nx1*nx1 + ny1*ny1) - 1) <= HELI_PRECISION);
Ivan Mahonin 0b65e7
	
Ivan Mahonin 0b65e7
	double dot = nx0*ny1 - ny0*nx1;
Ivan Mahonin 0b65e7
	if (dot > HELI_PRECISION) {
Ivan Mahonin 0b65e7
		// round
Ivan Mahonin 0b65e7
		strokeAddPoint(stroke, x0, y0, ny0, -nx0);
Ivan Mahonin 0b65e7
		strokeAddCornerSub(stroke, ny1, -nx1, precisionSqr, 8);
Ivan Mahonin 0b65e7
	} else
Ivan Mahonin 89ec47
	if (dot < -HELI_PRECISION) {
Ivan Mahonin 0b65e7
		// corner
Ivan Mahonin 0b65e7
		double dx = nx1 - nx0;
Ivan Mahonin 0b65e7
		double dy = ny1 - ny0;
Ivan Mahonin 0b65e7
		double kl = 1/sqrt(dx*dx + dy*dy);
Ivan Mahonin 0b65e7
		double nx = dx*kl, ny = dy*kl;
Ivan Mahonin 0b65e7
		double c = nx*nx1 + ny*ny1;
Ivan Mahonin 0b65e7
		double cl = 1/sqrt(1 - c*c);
Ivan Mahonin 89ec47
		if (cl >  3) cl =  3;
Ivan Mahonin 0b65e7
		strokeAddPoint(stroke, x0, y0, nx*cl, ny*cl);
Ivan Mahonin 0b65e7
	} else {
Ivan Mahonin 0b65e7
		// flat
Ivan Mahonin 0b65e7
		strokeAddPoint(stroke, x0, y0, ny0, -nx0);
Ivan Mahonin 0b65e7
	}
Ivan Mahonin 0b65e7
}
Ivan Mahonin 0b65e7
Ivan Mahonin 0b65e7
static void strokeDraw(HeliStroke *stroke, double w, double *color) {
Ivan Mahonin 8bc1f1
	const double aaBorder = heliGLGetAAResolution();
Ivan Mahonin 8bc1f1
	if (aaBorder > HELI_PRECISION) {
Ivan Mahonin 0b65e7
		double c0[4] = { color[0], color[1], color[2], 0 };
Ivan Mahonin 0b65e7
		double c1[4] = { color[0], color[1], color[2], color[3] };
Ivan Mahonin 0b65e7
		double w0 = w + 0.5*aaBorder;
Ivan Mahonin 0b65e7
		double w1 = w - 0.5*aaBorder;
Ivan Mahonin 0b65e7
		if (w1 < HELI_PRECISION) {
Ivan Mahonin 0b65e7
			c1[3] *= w0/aaBorder;
Ivan Mahonin 0b65e7
			w1 = 0;
Ivan Mahonin 0b65e7
		}
Ivan Mahonin 0b65e7
		
Ivan Mahonin 0b65e7
		glBegin(GL_TRIANGLE_STRIP);
Ivan Mahonin 0b65e7
		for(int i = 0; i < stroke->count; ++i) {
Ivan Mahonin 0b65e7
			HeliStrokePoint *p = stroke->points + i;
Ivan Mahonin 0b65e7
			glColor4dv(c0);
Ivan Mahonin 0b65e7
			glVertex2d(p->x + p->dx*w0, p->y + p->dy*w0);
Ivan Mahonin 0b65e7
			glColor4dv(c1);
Ivan Mahonin 0b65e7
			glVertex2d(p->x + p->dx*w1, p->y + p->dy*w1);
Ivan Mahonin 0b65e7
		}
Ivan Mahonin 0b65e7
		glEnd();
Ivan Mahonin 0b65e7
		
Ivan Mahonin 0b65e7
		if (w1 > 0) {
Ivan Mahonin 0b65e7
			glColor4dv(c1);
Ivan Mahonin 0b65e7
			glBegin(GL_TRIANGLE_STRIP);
Ivan Mahonin 0b65e7
			for(int i = 0; i < stroke->count; ++i) {
Ivan Mahonin 0b65e7
				HeliStrokePoint *p = stroke->points + i;
Ivan Mahonin 0b65e7
				glVertex2d(p->x, p->y);
Ivan Mahonin 0b65e7
				glVertex2d(p->x + p->dx*w1, p->y + p->dy*w1);
Ivan Mahonin 0b65e7
			}
Ivan Mahonin 0b65e7
			glEnd();
Ivan Mahonin 0b65e7
		}
Ivan Mahonin 0b65e7
		
Ivan Mahonin 0b65e7
		glColor4d(1, 1, 1, 1);
Ivan Mahonin 0b65e7
	} else {
Ivan Mahonin 0b65e7
		glColor4dv(color);
Ivan Mahonin 0b65e7
		glEnable(GL_MULTISAMPLE);
Ivan Mahonin 0b65e7
		glBegin(GL_TRIANGLE_STRIP);
Ivan Mahonin 0b65e7
		for(int i = 0; i < stroke->count; ++i) {
Ivan Mahonin 0b65e7
			HeliStrokePoint *p = stroke->points + i;
Ivan Mahonin 0b65e7
			glVertex2d(p->x, p->y);
Ivan Mahonin 0b65e7
			glVertex2d(p->x + p->dx*w, p->y + p->dy*w);
Ivan Mahonin 0b65e7
		}
Ivan Mahonin 0b65e7
		glEnd();
Ivan Mahonin 0b65e7
		glDisable(GL_MULTISAMPLE);
Ivan Mahonin 0b65e7
		glColor4d(1, 1, 1, 1);
Ivan Mahonin 0b65e7
	}
Ivan Mahonin 0b65e7
}
Ivan Mahonin 0b65e7
Ivan Mahonin 0b65e7
Ivan Mahonin 0b65e7
static void drawStroke(int close) {
Ivan Mahonin 3954ba
	HeliDrawingState *s = heliDrawingGetState();
Ivan Mahonin deef1d
	if (s->strokeColor[3] <= HELI_PRECISION) return;
Ivan Mahonin deef1d
	if (s->strokeWidth < HELI_PRECISION) return;
Ivan Mahonin ba9f06
	if (pathSize < 4) return;
Ivan Mahonin 0b65e7
	
Ivan Mahonin 0b65e7
	HeliStroke stroke = {};
Ivan Mahonin 0b65e7
	stroke.allocatedCount = pathSize;
Ivan Mahonin 0b65e7
	stroke.points = calloc(stroke.allocatedCount, sizeof(*stroke.points));
Ivan Mahonin 0b65e7
	
Ivan Mahonin deef1d
	double w = 0.5*s->strokeWidth;
Ivan Mahonin 0b65e7
	double precisionSqr = 1/(w*0.75);
Ivan Mahonin 0b65e7
	precisionSqr *= precisionSqr;
Ivan Mahonin 0b65e7
	
Ivan Mahonin deef1d
	heliDrawingApplyTexture(s->strokeColor, &s->strokeTexture);
Ivan Mahonin deef1d
	
Ivan Mahonin 0b65e7
	if (close) {
Ivan Mahonin 0b65e7
		for(int i = 0; i < pathSize; i += 2) {
Ivan Mahonin 0b65e7
			int p = i > 0 ? i - 2 : pathSize - 2;
Ivan Mahonin 0b65e7
			int n = i < pathSize - 2 ? i + 2 : 0;
Ivan Mahonin 0b65e7
			strokeAddCorner(
Ivan Mahonin 0b65e7
				&stroke,
Ivan Mahonin 0b65e7
				path[p], path[p+1],
Ivan Mahonin 0b65e7
				path[i], path[i+1],
Ivan Mahonin 0b65e7
				path[n], path[n+1],
Ivan Mahonin 0b65e7
				precisionSqr );
Ivan Mahonin 0b65e7
		}
Ivan Mahonin 0b65e7
		strokeAddPoint(
Ivan Mahonin 0b65e7
			&stroke,
Ivan Mahonin 0b65e7
			stroke.points[0].x,
Ivan Mahonin 0b65e7
			stroke.points[0].y,
Ivan Mahonin 0b65e7
			stroke.points[0].dx,
Ivan Mahonin 0b65e7
			stroke.points[0].dy );
Ivan Mahonin deef1d
		strokeDraw(&stroke, w, s->strokeColor);
Ivan Mahonin 0b65e7
		
Ivan Mahonin 0b65e7
		stroke.count = 0;
Ivan Mahonin 0b65e7
		
Ivan Mahonin 0b65e7
		for(int i = pathSize - 2; i >= 0; i -= 2) {
Ivan Mahonin 0b65e7
			int p = i < pathSize - 2 ? i + 2 : 0;
Ivan Mahonin 0b65e7
			int n = i > 0 ? i - 2 : pathSize - 2;
Ivan Mahonin 0b65e7
			strokeAddCorner(
Ivan Mahonin 0b65e7
				&stroke,
Ivan Mahonin 0b65e7
				path[p], path[p+1],
Ivan Mahonin 0b65e7
				path[i], path[i+1],
Ivan Mahonin 0b65e7
				path[n], path[n+1],
Ivan Mahonin 0b65e7
				precisionSqr );
Ivan Mahonin 0b65e7
		}
Ivan Mahonin 0b65e7
		strokeAddPoint(
Ivan Mahonin 0b65e7
			&stroke,
Ivan Mahonin 0b65e7
			stroke.points[0].x,
Ivan Mahonin 0b65e7
			stroke.points[0].y,
Ivan Mahonin 0b65e7
			stroke.points[0].dx,
Ivan Mahonin 0b65e7
			stroke.points[0].dy );
Ivan Mahonin deef1d
		strokeDraw(&stroke, w, s->strokeColor);
Ivan Mahonin 0b65e7
	} else {
Ivan Mahonin 0b65e7
		double px = 0, py = 0, nx = 0, ny = 0;
Ivan Mahonin 0b65e7
		for(int i = 0; i < pathSize; i += 2) {
Ivan Mahonin 0b65e7
			double x = path[i], y = path[i+1];
Ivan Mahonin 0b65e7
			if (i > 0)            { px = path[i-2]; py = path[i-1]; }
Ivan Mahonin 0b65e7
			if (i < pathSize - 2) { nx = path[i+2]; ny = path[i+3]; }
Ivan Mahonin 0b65e7
			if (i == 0)            { px = x + y - ny; py = y + nx - x; }
Ivan Mahonin 0b65e7
			if (i == pathSize - 2) { nx = x + py - y; ny = y + x - px; }
Ivan Mahonin 0b65e7
			strokeAddCorner(&stroke, px, py, x, y, nx, ny, precisionSqr);
Ivan Mahonin 0b65e7
		}
Ivan Mahonin 0b65e7
		--stroke.count;
Ivan Mahonin 0b65e7
		for(int i = pathSize - 2; i >= 0; i -= 2) {
Ivan Mahonin 0b65e7
			double x = path[i], y = path[i+1];
Ivan Mahonin 0b65e7
			if (i > 0)            { nx = path[i-2]; ny = path[i-1]; }
Ivan Mahonin 0b65e7
			if (i < pathSize - 2) { px = path[i+2]; py = path[i+3]; }
Ivan Mahonin 0b65e7
			if (i == 0)            { nx = x + py - y; ny = y + x - px; }
Ivan Mahonin 0b65e7
			if (i == pathSize - 2) { px = x + y - ny; py = y + nx - x; }
Ivan Mahonin 0b65e7
			strokeAddCorner(&stroke, px, py, x, y, nx, ny, precisionSqr);
Ivan Mahonin 0b65e7
		}
Ivan Mahonin 0b65e7
		memcpy(stroke.points + stroke.count - 1, stroke.points, sizeof(*stroke.points));
Ivan Mahonin deef1d
		strokeDraw(&stroke, w, s->strokeColor);
Ivan Mahonin 0b65e7
	}
Ivan Mahonin 0b65e7
	
Ivan Mahonin deef1d
	heliDrawingResetTexture();
Ivan Mahonin 0b65e7
	free(stroke.points);
Ivan Mahonin 07b70f
}
Ivan Mahonin 07b70f
Ivan Mahonin 07b70f
static void pushPathPoint(double x, double y) {
Ivan Mahonin 07b70f
	if (pathAllocated < pathSize + 2) {
Ivan Mahonin 07b70f
		pathAllocated += pathAllocated/4 + 32;
Ivan Mahonin a2530e
		path = realloc(path, pathAllocated*sizeof(*path));
Ivan Mahonin 07b70f
		memset(&path[pathSize], 0, (pathAllocated - pathSize)*sizeof(*path));
Ivan Mahonin 07b70f
	}
Ivan Mahonin 07b70f
	path[pathSize++] = x;
Ivan Mahonin 07b70f
	path[pathSize++] = y;
Ivan Mahonin 07b70f
}
Ivan Mahonin 07b70f
Ivan Mahonin ba9f06
static void endPath(int close, int stroke, int fill, int fillSimple) {
Ivan Mahonin 89ec47
	int origPathSize = pathSize;
Ivan Mahonin 89ec47
	
Ivan Mahonin 0b65e7
	// remove duplicates
Ivan Mahonin 0b65e7
	int removed = 0;
Ivan Mahonin 0b65e7
	for(int i = 2; i < pathSize; i += 2) {
Ivan Mahonin 0b65e7
		if ( fabs(path[i-removed-2] - path[i]) <= 2*HELI_PRECISION
Ivan Mahonin 0b65e7
		  && fabs(path[i-removed-1] - path[i+1]) <= 2*HELI_PRECISION )
Ivan Mahonin 0b65e7
		{
Ivan Mahonin 0b65e7
			removed += 2;
Ivan Mahonin 0b65e7
		} else
Ivan Mahonin 0b65e7
		if (removed > 0) {
Ivan Mahonin 0b65e7
			path[i-removed] = path[i];
Ivan Mahonin 0b65e7
			path[i-removed+1] = path[i+1];
Ivan Mahonin 0b65e7
		}
Ivan Mahonin 0b65e7
	}
Ivan Mahonin 0b65e7
	
Ivan Mahonin 0b65e7
	// remove tails duplicates
Ivan Mahonin 0b65e7
	pathSize -= removed;
Ivan Mahonin 0b65e7
	if ( pathSize >= 4
Ivan Mahonin 0b65e7
	  && close
Ivan Mahonin 0b65e7
	  && fabs(path[pathSize-2] - path[0]) <= 2*HELI_PRECISION
Ivan Mahonin 0b65e7
	  && fabs(path[pathSize-1] - path[1]) <= 2*HELI_PRECISION )
Ivan Mahonin 0b65e7
		pathSize -= 2;
Ivan Mahonin 0b65e7
	
Ivan Mahonin 0b65e7
	// draw
Ivan Mahonin 89ec47
	int isPoint = FALSE;
Ivan Mahonin 89ec47
	double px, py;
Ivan Mahonin ba9f06
	if (pathSize >= 4) {
Ivan Mahonin ba9f06
		if (fillSimple) drawFillSimple();
Ivan Mahonin ba9f06
			else if (fill) drawFill();
Ivan Mahonin 0b65e7
		if (stroke) drawStroke(close);
Ivan Mahonin 89ec47
	} else
Ivan Mahonin 89ec47
	if (origPathSize >= 4 && stroke) {
Ivan Mahonin 89ec47
		px = path[0];
Ivan Mahonin 89ec47
		py = path[1];
Ivan Mahonin 89ec47
		isPoint = TRUE;
Ivan Mahonin ba9f06
	}
Ivan Mahonin 0b65e7
	
Ivan Mahonin ba9f06
	resetPath();
Ivan Mahonin 89ec47
	
Ivan Mahonin 89ec47
	if (isPoint) point(px, py);
Ivan Mahonin ba9f06
}
Ivan Mahonin ba9f06
Ivan Mahonin 07b70f
void closePath()
Ivan Mahonin ba9f06
	{ endPath(TRUE, TRUE, TRUE, FALSE); }
Ivan Mahonin 07b70f
void strokePath()
Ivan Mahonin ba9f06
	{ endPath(FALSE, TRUE, FALSE, FALSE); }
Ivan Mahonin 07b70f
void lineTo(double x, double y)
Ivan Mahonin 07b70f
	{ pushPathPoint(x, y); }
Ivan Mahonin 07b70f
void moveTo(double x, double y)
Ivan Mahonin 07b70f
	{ resetPath(); lineTo(x, y); }
Ivan Mahonin 07b70f
Ivan Mahonin 07b70f
Ivan Mahonin 3954ba
HeliDrawingState *heliDrawingGetState()
Ivan Mahonin 3954ba
	{ return statesStack + statesStackIndex; }
Ivan Mahonin 3954ba
Ivan Mahonin 3954ba
HeliDrawingState *heliDrawingGetStateStack()
Ivan Mahonin 3954ba
	{ return statesStack; }
Ivan Mahonin 3954ba
Ivan Mahonin 3e7c5f
void resetState()
Ivan Mahonin 3e7c5f
	{ resetStateEx(STATE_ALL); }
Ivan Mahonin 3e7c5f
Ivan Mahonin 3e7c5f
void resetStateEx(unsigned int flags) {
Ivan Mahonin 909bc2
	if (flags & STATE_FILL_COLOR        ) fill(colorByRGBA(1, 1, 1, 1));
Ivan Mahonin 1d641c
	if (flags & STATE_FILL_TEXTURE      ) noFillTexture();
Ivan Mahonin 1d641c
	if (flags & STATE_STROKE_COLOR      ) stroke(colorByRGBA(0, 0, 0, 1));
Ivan Mahonin 1d641c
	if (flags & STATE_STROKE_TEXTURE    ) noStrokeTexture();
Ivan Mahonin 1d641c
	if (flags & STATE_STROKE_WIDTH      ) strokeWidth(1);
Ivan Mahonin 1d641c
	if (flags & STATE_TEXT_ALIGN        ) textAlign(HALIGN_LEFT, VALIGN_TOP);
Ivan Mahonin 1d641c
	if (flags & STATE_TEXT_SIZE         ) textSize(24);
Ivan Mahonin 1d641c
	if (flags & STATE_TARGET_FRAMEBUFFER) targetEx(NULL, FALSE, FALSE);
Ivan Mahonin 1d641c
	if (flags & STATE_TARGET_VIEWPORT   ) viewportByWindow();
Ivan Mahonin 1d641c
	if (flags & STATE_TARGET_PROJECTION ) projectionByViewport();
Ivan Mahonin d6f40c
	if (flags & STATE_BACKGROUND        ) background(colorByRGBA(1, 1, 1, 1));
Ivan Mahonin d6f40c
	if (flags & STATE_TRANSFORM         ) noTransform();
Ivan Mahonin d6f40c
	if (flags & STATE_CLIP              ) noClip();
Ivan Mahonin 3e7c5f
}
Ivan Mahonin 3e7c5f
Ivan Mahonin 3e7c5f
void saveState()
Ivan Mahonin deef1d
	{ saveStateEx(STATE_ALL); }
Ivan Mahonin deef1d
Ivan Mahonin deef1d
void saveStateEx(unsigned int flags) {
Ivan Mahonin 3954ba
	if (statesStackIndex >= sizeof(statesStack)/sizeof(*statesStack) - 1) {
Ivan Mahonin 3954ba
		fprintf(stderr, "helianthus: drawing stack overflow\n");
Ivan Mahonin 3954ba
		return;
Ivan Mahonin 3954ba
	}
Ivan Mahonin 3954ba
	++statesStackIndex;
Ivan Mahonin 3954ba
	memcpy(statesStack + statesStackIndex, statesStack + statesStackIndex - 1, sizeof(*statesStack));
Ivan Mahonin deef1d
	statesStack[statesStackIndex].flags = flags;
Ivan Mahonin deef1d
	heliGLGetCommonState(&statesStack[statesStackIndex].glState, flags);
Ivan Mahonin 3954ba
}
Ivan Mahonin 3954ba
Ivan Mahonin deef1d
void restoreState() {
Ivan Mahonin 3954ba
	assert(statesStackIndex > 0);
Ivan Mahonin 3954ba
	if (statesStackIndex <= 0) {
Ivan Mahonin 3954ba
		fprintf(stderr, "helianthus: drawing stack underflow\n");
Ivan Mahonin 3954ba
		return;
Ivan Mahonin 3954ba
	}
Ivan Mahonin deef1d
	
Ivan Mahonin deef1d
	HeliDrawingState *prev = &statesStack[statesStackIndex];
Ivan Mahonin deef1d
	heliGLSetCommonState(&prev->glState);
Ivan Mahonin 3954ba
	--statesStackIndex;
Ivan Mahonin deef1d
	HeliDrawingState *curr = &statesStack[statesStackIndex];
Ivan Mahonin deef1d
	
Ivan Mahonin deef1d
	unsigned int flags = ~prev->flags;
Ivan Mahonin deef1d
	if (flags & STATE_FILL_COLOR)
Ivan Mahonin deef1d
		memcpy(curr->fillColor, prev->fillColor, sizeof(curr->fillColor));
Ivan Mahonin deef1d
	if (flags & STATE_FILL_TEXTURE)
Ivan Mahonin deef1d
		memcpy(&curr->fillTexture, &prev->fillTexture, sizeof(curr->fillTexture));
Ivan Mahonin deef1d
	if (flags & STATE_STROKE_COLOR)
Ivan Mahonin deef1d
		memcpy(curr->strokeColor, prev->strokeColor, sizeof(curr->strokeColor));
Ivan Mahonin deef1d
	if (flags & STATE_STROKE_TEXTURE)
Ivan Mahonin deef1d
		memcpy(&curr->strokeTexture, &prev->strokeTexture, sizeof(curr->strokeTexture));
Ivan Mahonin deef1d
	if (flags & STATE_STROKE_WIDTH)
Ivan Mahonin deef1d
		curr->strokeWidth = prev->strokeWidth;
Ivan Mahonin deef1d
	if (flags & STATE_TEXT_ALIGN)
Ivan Mahonin deef1d
		{ curr->horAlign = prev->horAlign; curr->vertAlign = prev->vertAlign; }
Ivan Mahonin deef1d
	if (flags & STATE_TEXT_SIZE)
Ivan Mahonin deef1d
		curr->fontSize = prev->fontSize;
Ivan Mahonin deef1d
	if (flags & STATE_TEXT_FONT)
Ivan Mahonin deef1d
		curr->font = prev->font;
Ivan Mahonin 3954ba
}
Ivan Mahonin 3954ba
Ivan Mahonin f63775
void heliDrawingClearFrame() {
Ivan Mahonin 1d641c
	unsigned int prev;
Ivan Mahonin 1d641c
	glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (int*)&prev);
Ivan Mahonin d6f40c
	clear();
Ivan Mahonin 1d641c
	if (heliGLBindFramebufferPtr) heliGLBindFramebufferPtr(GL_DRAW_FRAMEBUFFER, prev);
Ivan Mahonin 07b70f
}
Ivan Mahonin 07b70f
Ivan Mahonin 650f35
void heliDrawingPrepareFrame() {
Ivan Mahonin 650f35
	resetPath();
Ivan Mahonin 3954ba
	
Ivan Mahonin f63775
	heliDrawingClearFrame();
Ivan Mahonin a20939
	glDisable(GL_DEPTH_TEST);
Ivan Mahonin ba9f06
	glDisable(GL_STENCIL_TEST);
Ivan Mahonin a20939
	glDisable(GL_CULL_FACE);
Ivan Mahonin a20939
	glDisable(GL_MULTISAMPLE);
Ivan Mahonin ba9f06
	
Ivan Mahonin ba9f06
	glEnable(GL_BLEND);
Ivan Mahonin 1d641c
	if (heliGLBlendFuncSeparatePtr)
Ivan Mahonin 89ec47
		heliGLBlendFuncSeparatePtr(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE);
Ivan Mahonin 1d641c
	else
Ivan Mahonin 1d641c
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
Ivan Mahonin ba9f06
	glEnable(GL_LINE_SMOOTH);
Ivan Mahonin ba9f06
	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
Ivan Mahonin 650f35
}
Ivan Mahonin 650f35
Ivan Mahonin 07b70f
void heliDrawingFinish() {
Ivan Mahonin 07b70f
	resetPath();
Ivan Mahonin 07b70f
	free(path);
Ivan Mahonin 07b70f
	path = NULL;
Ivan Mahonin 07b70f
	pathAllocated = 0;
Ivan Mahonin 909bc2
	heliArrayDestroy(&heliDrawingFramebuffersToFlush);
Ivan Mahonin 07b70f
}