Blame c++/contourgl/polyspan.cpp

Ivan Mahonin 93cbac
/*
Ivan Mahonin 93cbac
	polyspan.cpp
Ivan Mahonin 93cbac
	Polyspan
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
Ivan Mahonin 93cbac
	Copyright (c) 2007, 2008 Chris Moore
Ivan Mahonin 93cbac
	Copyright (c) 2012-2013 Carlos López
Ivan Mahonin 93cbac
	......... ... 2015 Ivan Mahonin
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	This package is free software; you can redistribute it and/or
Ivan Mahonin 93cbac
	modify it under the terms of the GNU General Public License as
Ivan Mahonin 93cbac
	published by the Free Software Foundation; either version 2 of
Ivan Mahonin 93cbac
	the License, or (at your option) any later version.
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	This package is distributed in the hope that it will be useful,
Ivan Mahonin 93cbac
	but WITHOUT ANY WARRANTY; without even the implied warranty of
Ivan Mahonin 93cbac
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Ivan Mahonin 93cbac
	General Public License for more details.
Ivan Mahonin 93cbac
*/
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
#include <cassert>
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
#include "polyspan.h"
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
Ivan Mahonin 6e407d
using namespace std;
Ivan Mahonin 6e407d
Ivan Mahonin 6e407d
Ivan Mahonin 93cbac
Polyspan::Polyspan():
Ivan Mahonin 93cbac
	open_index(0),
Ivan Mahonin 93cbac
	cur_x(0.0),
Ivan Mahonin 93cbac
	cur_y(0.0),
Ivan Mahonin 93cbac
	close_x(0.0),
Ivan Mahonin 93cbac
	close_y(0.0),
Ivan Mahonin 93cbac
	flags(NotSorted)
Ivan Mahonin 93cbac
{ }
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
void Polyspan::clear() {
Ivan Mahonin 93cbac
	covers.clear();
Ivan Mahonin 93cbac
	cur_x = cur_y = close_x = close_y = 0;
Ivan Mahonin 93cbac
	open_index = 0;
Ivan Mahonin 93cbac
	current.set(0, 0, 0, 0);
Ivan Mahonin 93cbac
	flags = NotSorted;
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
// add the current cell, but only if there is information to add
Ivan Mahonin 93cbac
void Polyspan::addcurrent() {
Ivan Mahonin 93cbac
	if (current.cover || current.area) {
Ivan Mahonin 93cbac
		if (covers.size() == covers.capacity())
Ivan Mahonin 93cbac
			covers.reserve(covers.size() + 1024*1024);
Ivan Mahonin 93cbac
		covers.push_back(current);
Ivan Mahonin 93cbac
	}
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
// move to the next cell (cover values 0 initially), keeping the current if necessary
Ivan Mahonin 93cbac
void Polyspan::move_pen(int x, int y) {
Ivan Mahonin 93cbac
	if (y != current.y || x != current.x) {
Ivan Mahonin 93cbac
		addcurrent();
Ivan Mahonin 93cbac
		current.set(x, y, 0, 0);
Ivan Mahonin 93cbac
	}
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
// close the primitives with a line (or rendering will not work as expected)
Ivan Mahonin 93cbac
void Polyspan::close() {
Ivan Mahonin 93cbac
	if (flags & NotClosed) {
Ivan Mahonin 93cbac
		if (cur_x != close_x || cur_y != close_y) {
Ivan Mahonin 93cbac
			line_to(close_x, close_y);
Ivan Mahonin 93cbac
			addcurrent();
Ivan Mahonin 93cbac
			current.setcover(0,0);
Ivan Mahonin 93cbac
		}
Ivan Mahonin 93cbac
		flags &= ~NotClosed;
Ivan Mahonin 93cbac
	}
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
// Not recommended - destroys any separation of spans currently held
Ivan Mahonin 93cbac
void Polyspan::merge_all() {
Ivan Mahonin 93cbac
	sort(covers.begin(), covers.end());
Ivan Mahonin 93cbac
	open_index = 0;
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
// will sort the marks if they are not sorted
Ivan Mahonin 93cbac
void Polyspan::sort_marks() {
Ivan Mahonin 93cbac
	if (flags & NotSorted) {
Ivan Mahonin 7c6b57
		// only sort the open index
Ivan Mahonin 93cbac
		addcurrent();
Ivan Mahonin 93cbac
		current.setcover(0, 0);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		sort(covers.begin() + open_index, covers.end());
Ivan Mahonin 6e407d
Ivan Mahonin 93cbac
		flags &= ~NotSorted;
Ivan Mahonin 93cbac
	}
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
// encapsulate the current sublist of marks (used for drawing)
Ivan Mahonin 93cbac
void Polyspan::encapsulate_current() {
Ivan Mahonin 93cbac
	// sort the current list then reposition the open list section
Ivan Mahonin 93cbac
	sort_marks();
Ivan Mahonin 93cbac
	open_index = covers.size();
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
// move to start a new primitive list (enclose the last primitive if need be)
Ivan Mahonin 93cbac
void Polyspan::move_to(Real x, Real y) {
Ivan Mahonin 93cbac
	close();
Ivan Mahonin 93cbac
	if (isnan(x)) x=0;
Ivan Mahonin 93cbac
	if (isnan(y)) y=0;
Ivan Mahonin 93cbac
	move_pen((int)floor(x), (int)floor(y));
Ivan Mahonin 93cbac
	close_y = cur_y = y;
Ivan Mahonin 93cbac
	close_x = cur_x = x;
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
// primitive_to functions
Ivan Mahonin 93cbac
void Polyspan::line_to(Real x, Real y) {
Ivan Mahonin 93cbac
	Real n[4] = {0, 0, 0, 0};
Ivan Mahonin 93cbac
	bool afterx = false;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	const Real xin(x), yin(y);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	Real dx = x - cur_x;
Ivan Mahonin 93cbac
	Real dy = y - cur_y;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	// CLIP IT!!!!
Ivan Mahonin 93cbac
	// outside y - ignore entirely
Ivan Mahonin 93cbac
	if ( (cur_y >= window.maxy && y >= window.maxy)
Ivan Mahonin 93cbac
	  || (cur_y <  window.miny && y <  window.miny) )
Ivan Mahonin 93cbac
	{
Ivan Mahonin 93cbac
		cur_x = x;
Ivan Mahonin 93cbac
		cur_y = y;
Ivan Mahonin 93cbac
	} else { // not degenerate - more complicated
Ivan Mahonin 93cbac
		if (dy > 0) { //be sure it's not tooooo small
Ivan Mahonin 93cbac
			// cur_y ... window.miny ... window.maxy ... y
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			// initial degenerate - initial clip
Ivan Mahonin 93cbac
			if (cur_y < window.miny) {
Ivan Mahonin 93cbac
				// new clipped start point (must also move pen)
Ivan Mahonin 93cbac
				n[2] = cur_x + (window.miny - cur_y)*dx/dy;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
				cur_x = n[2];
Ivan Mahonin 93cbac
				cur_y = window.miny;
Ivan Mahonin 93cbac
				move_pen((int)floor(cur_x), window.miny);
Ivan Mahonin 93cbac
			}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			// generate data for the ending clipped info
Ivan Mahonin 93cbac
			if (y > window.maxy) {
Ivan Mahonin 93cbac
				// initial line to intersection (and degenerate)
Ivan Mahonin 93cbac
				n[2] = x + (window.maxy - y)*dx/dy;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
				//intersect coords
Ivan Mahonin 93cbac
				x = n[2];
Ivan Mahonin 93cbac
				y = window.maxy;
Ivan Mahonin 93cbac
			}
Ivan Mahonin 93cbac
		} else {
Ivan Mahonin 93cbac
			// initial degenerate - initial clip
Ivan Mahonin 93cbac
			if (cur_y > window.maxy) {
Ivan Mahonin 93cbac
				// new clipped start point (must also move pen)
Ivan Mahonin 93cbac
				n[2] = cur_x + (window.maxy - cur_y)*dx/dy;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
				cur_x = n[2];
Ivan Mahonin 93cbac
				cur_y = window.maxy;
Ivan Mahonin 93cbac
				move_pen((int)floor(cur_x), window.maxy);
Ivan Mahonin 93cbac
			}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			// generate data for the ending clipped info
Ivan Mahonin 93cbac
			if (y < window.miny) {
Ivan Mahonin 93cbac
				// initial line to intersection (and degenerate)
Ivan Mahonin 93cbac
				n[2] = x + (window.miny - y)*dx/dy;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
				// intersect coords
Ivan Mahonin 93cbac
				x = n[2];
Ivan Mahonin 93cbac
				y = window.miny;
Ivan Mahonin 93cbac
			}
Ivan Mahonin 93cbac
		}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// all degenerate - but require bounded clipped values
Ivan Mahonin 93cbac
		if ( (cur_x >= window.maxx && x >= window.maxx)
Ivan Mahonin 93cbac
		  || (cur_x <  window.minx && x <  window.minx) )
Ivan Mahonin 93cbac
		{
Ivan Mahonin 93cbac
			//clip both vertices - but only needed in the x direction
Ivan Mahonin 6e407d
			cur_x = max(cur_x,	(Real)window.minx);
Ivan Mahonin 6e407d
			cur_x = min(cur_x,	(Real)window.maxx);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			//clip the dest values - y is already clipped
Ivan Mahonin 6e407d
			x = max(x, (Real)window.minx);
Ivan Mahonin 6e407d
			x = min(x, (Real)window.maxx);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			//must start at new point...
Ivan Mahonin 93cbac
			move_pen((int)floor(cur_x), (int)floor(cur_y));
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			draw_line(cur_x,cur_y,x,y);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			cur_x = xin;
Ivan Mahonin 93cbac
			cur_y = yin;
Ivan Mahonin 93cbac
		} else {
Ivan Mahonin 93cbac
			// clip x
Ivan Mahonin 93cbac
			if (dx > 0) {
Ivan Mahonin 93cbac
				// initial degenerate - initial clip
Ivan Mahonin 93cbac
				if (cur_x < window.minx) {
Ivan Mahonin 93cbac
					// need to draw an initial segment from clippedx,cur_y to clippedx,intersecty
Ivan Mahonin 93cbac
					n[2] = cur_y + (window.minx - cur_x)*dy/dx;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
					move_pen(window.minx, (int)floor(cur_y));
Ivan Mahonin 93cbac
					draw_line(window.minx, cur_y, window.minx, n[2]);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
					cur_x = window.minx;
Ivan Mahonin 93cbac
					cur_y = n[2];
Ivan Mahonin 93cbac
				}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
				// generate data for the ending clipped info
Ivan Mahonin 93cbac
				if (x > window.maxx) {
Ivan Mahonin 93cbac
					// initial line to intersection (and degenerate)
Ivan Mahonin 93cbac
					n[2] = y + (window.maxx - x)*dy/dx;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
					n[0] = window.maxx;
Ivan Mahonin 93cbac
					n[1] = y;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
					// intersect coords
Ivan Mahonin 93cbac
					x = window.maxx;
Ivan Mahonin 93cbac
					y = n[2];
Ivan Mahonin 93cbac
					afterx = true;
Ivan Mahonin 93cbac
				}
Ivan Mahonin 93cbac
			} else {
Ivan Mahonin 93cbac
				// initial degenerate - initial clip
Ivan Mahonin 93cbac
				if (cur_x > window.maxx) {
Ivan Mahonin 93cbac
					// need to draw an initial segment from clippedx,cur_y to clippedx,intersecty
Ivan Mahonin 93cbac
					n[2] = cur_y + (window.maxx - cur_x)*dy/dx;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
					move_pen(window.maxx, (int)floor(cur_y));
Ivan Mahonin 93cbac
					draw_line(window.maxx, cur_y, window.maxx, n[2]);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
					cur_x = window.maxx;
Ivan Mahonin 93cbac
					cur_y = n[2];
Ivan Mahonin 93cbac
				}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
				// generate data for the ending clipped info
Ivan Mahonin 93cbac
				if (x < window.minx) {
Ivan Mahonin 93cbac
					//initial line to intersection (and degenerate)
Ivan Mahonin 93cbac
					n[2] = y + (window.minx - x)*dy/dx;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
					n[0] = window.minx;
Ivan Mahonin 93cbac
					n[1] = y;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
					//intersect coords
Ivan Mahonin 93cbac
					x = window.minx;
Ivan Mahonin 93cbac
					y = n[2];
Ivan Mahonin 93cbac
					afterx = true;
Ivan Mahonin 93cbac
				}
Ivan Mahonin 93cbac
			}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			move_pen((int)floor(cur_x), (int)floor(cur_y));
Ivan Mahonin 93cbac
			// draw the relevant line (clipped)
Ivan Mahonin 93cbac
			draw_line(cur_x, cur_y, x, y);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			if (afterx)
Ivan Mahonin 93cbac
				draw_line(x, y, n[0], n[1]);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			cur_x = xin;
Ivan Mahonin 93cbac
			cur_y = yin;
Ivan Mahonin 93cbac
		}
Ivan Mahonin 93cbac
	}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	flags |= NotClosed | NotSorted;
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin a7f97a
bool Polyspan::clip_conic(const Vector *p, const ContextRect &r) {
Ivan Mahonin 6e407d
	const Real minx = min(min(p[0][0], p[1][0]), p[2][0]);
Ivan Mahonin 6e407d
	const Real miny = min(min(p[0][1], p[1][1]), p[2][1]);
Ivan Mahonin 6e407d
	const Real maxx = max(max(p[0][0], p[1][0]), p[2][0]);
Ivan Mahonin 6e407d
	const Real maxy = max(max(p[0][1], p[1][1]), p[2][1]);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	return 	(minx > r.maxx) ||
Ivan Mahonin 93cbac
			(maxx < r.minx) ||
Ivan Mahonin 93cbac
			(miny > r.maxy) ||
Ivan Mahonin 93cbac
			(maxy < r.miny);
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin a7f97a
Real Polyspan::max_edges_conic(const Vector *p) {
Ivan Mahonin 93cbac
	const Real x1 = p[1][0] - p[0][0];
Ivan Mahonin 93cbac
	const Real y1 = p[1][1] - p[0][1];
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	const Real x2 = p[2][0] - p[1][0];
Ivan Mahonin 93cbac
	const Real y2 = p[2][1] - p[1][1];
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	const Real d1 = x1*x1 + y1*y1;
Ivan Mahonin 93cbac
	const Real d2 = x2*x2 + y2*y2;
Ivan Mahonin 93cbac
Ivan Mahonin 6e407d
	return max(d1,d2);
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
void Polyspan::subd_conic_stack(Vector *arc) {
Ivan Mahonin 93cbac
	/*
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	b0
Ivan Mahonin 93cbac
	*		0+1 a
Ivan Mahonin 93cbac
	b1 b	*		1+2*1+2 a
Ivan Mahonin 93cbac
	*		1+2	b	*
Ivan Mahonin 93cbac
	b2 		*
Ivan Mahonin 93cbac
	*
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	0.1.2 ->	0.1 2 3.4
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	*/
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	Real a, b;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	arc[4][0] = arc[2][0];
Ivan Mahonin 93cbac
	b = arc[1][0];
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	a = arc[1][0] = (arc[0][0] + b)/2;
Ivan Mahonin 93cbac
	b = arc[3][0] = (arc[4][0] + b)/2;
Ivan Mahonin 93cbac
	arc[2][0] = (a + b)/2;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	arc[4][1] = arc[2][1];
Ivan Mahonin 93cbac
	b = arc[1][1];
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	a = arc[1][1] = (arc[0][1] + b)/2;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	b = arc[3][1] = (arc[4][1] + b)/2;
Ivan Mahonin 93cbac
	arc[2][1] = (a + b)/2;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	/* //USING SIMD
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	arc[4] = arc[2];
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	arc[3] = (arc[2] + arc[1])/2;
Ivan Mahonin 93cbac
	arc[1] = (arc[0] + arc[1])/2;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	arc[2] = (arc[1] + arc[3])/2;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	*/
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
void Polyspan::conic_to(Real x1, Real y1, Real x, Real y) {
Ivan Mahonin 93cbac
	Vector *current = arc;
Ivan Mahonin 93cbac
	int		level = 0;
Ivan Mahonin 93cbac
	int 	num = 0;
Ivan Mahonin 93cbac
	bool	onsecond = false;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	arc[0] = Vector(x, y);
Ivan Mahonin 93cbac
	arc[1] = Vector(x1, y1);
Ivan Mahonin 93cbac
	arc[2] = Vector(cur_x, cur_y);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	// just draw the line if it's outside
Ivan Mahonin 93cbac
	if (clip_conic(arc, window))
Ivan Mahonin 93cbac
	{
Ivan Mahonin 93cbac
		line_to(x,y);
Ivan Mahonin 93cbac
		return;
Ivan Mahonin 93cbac
	}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	// Ok so it's not super degenerate, subdivide and draw (run through minimum subdivision levels first)
Ivan Mahonin 93cbac
	while(current >= arc) {
Ivan Mahonin 93cbac
		assert(num < MAX_SUBDIVISION_SIZE);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// if the curve is clipping then draw degenerate
Ivan Mahonin 93cbac
		if (clip_conic(current, window)) {
Ivan Mahonin 93cbac
			line_to(current[0][0],current[0][1]); //backwards so front is destination
Ivan Mahonin 93cbac
			current -= 2;
Ivan Mahonin 93cbac
			if (onsecond) level--;
Ivan Mahonin 93cbac
			onsecond = true;
Ivan Mahonin 93cbac
			num--;
Ivan Mahonin 93cbac
			continue;
Ivan Mahonin 93cbac
		} else
Ivan Mahonin 93cbac
		// if we are not at the level minimum
Ivan Mahonin 93cbac
		if (level < MIN_SUBDIVISION_DRAW_LEVELS) {
Ivan Mahonin 93cbac
			subd_conic_stack(current);
Ivan Mahonin 93cbac
			current += 2; 		// cursor on second curve
Ivan Mahonin 93cbac
			level ++;
Ivan Mahonin 93cbac
			num ++;
Ivan Mahonin 93cbac
			onsecond = false;
Ivan Mahonin 93cbac
			continue;
Ivan Mahonin 93cbac
		} else
Ivan Mahonin 93cbac
		// split it again, if it's too big
Ivan Mahonin 93cbac
		if (max_edges_conic(current) > 0.25) { // distance of .5 (cover no more than half the pixel)
Ivan Mahonin 93cbac
			subd_conic_stack(current);
Ivan Mahonin 93cbac
			current += 2; 		// cursor on second curve
Ivan Mahonin 93cbac
			level ++;
Ivan Mahonin 93cbac
			num ++;
Ivan Mahonin 93cbac
			onsecond = false;
Ivan Mahonin 93cbac
		} else { // NOT TOO BIG? RENDER!!!
Ivan Mahonin 93cbac
			// cur_x, cur_y = current[2], so we need to go 1,0
Ivan Mahonin 93cbac
			line_to(current[1][0], current[1][1]);
Ivan Mahonin 93cbac
			line_to(current[0][0], current[0][1]);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			current -= 2;
Ivan Mahonin 93cbac
			if (onsecond) level--;
Ivan Mahonin 93cbac
			num--;
Ivan Mahonin 93cbac
			onsecond = true;
Ivan Mahonin 93cbac
		}
Ivan Mahonin 93cbac
	}
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin a7f97a
bool Polyspan::clip_cubic(const Vector *p, const ContextRect &r) {
Ivan Mahonin 93cbac
	return 	((p[0][0] > r.maxx) && (p[1][0] > r.maxx) && (p[2][0] > r.maxx) && (p[3][0] > r.maxx)) ||
Ivan Mahonin 93cbac
			((p[0][0] < r.minx) && (p[1][0] < r.minx) && (p[2][0] < r.minx) && (p[3][0] < r.minx)) ||
Ivan Mahonin 93cbac
			((p[0][1] > r.maxy) && (p[1][1] > r.maxy) && (p[2][1] > r.maxy) && (p[3][1] > r.maxy)) ||
Ivan Mahonin 93cbac
			((p[0][1] < r.miny) && (p[1][1] < r.miny) && (p[2][1] < r.miny) && (p[3][1] < r.miny));
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin a7f97a
Real Polyspan::max_edges_cubic(const Vector *p) {
Ivan Mahonin 93cbac
	const Real x1 = p[1][0] - p[0][0];
Ivan Mahonin 93cbac
	const Real y1 = p[1][1] - p[0][1];
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	const Real x2 = p[2][0] - p[1][0];
Ivan Mahonin 93cbac
	const Real y2 = p[2][1] - p[1][1];
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	const Real x3 = p[3][0] - p[2][0];
Ivan Mahonin 93cbac
	const Real y3 = p[3][1] - p[2][1];
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	const Real d1 = x1*x1 + y1*y1;
Ivan Mahonin 93cbac
	const Real d2 = x2*x2 + y2*y2;
Ivan Mahonin 93cbac
	const Real d3 = x3*x3 + y3*y3;
Ivan Mahonin 93cbac
Ivan Mahonin 6e407d
	return max(max(d1, d2), d3);
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
void Polyspan::subd_cubic_stack(Vector *arc) {
Ivan Mahonin 93cbac
	Real a, b, c;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	/*
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	b0
Ivan Mahonin 93cbac
	*		0+1 a
Ivan Mahonin 93cbac
	b1 b	*		1+2*1+2 a
Ivan Mahonin 93cbac
	*		1+2	b	*			0+3*1+3*2+3
Ivan Mahonin 93cbac
	b2 c	*		1+2*2+2	b	*
Ivan Mahonin 93cbac
	*		2+3	c	*
Ivan Mahonin 93cbac
	b3 		*
Ivan Mahonin 93cbac
	*
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	0.1 2.3 ->	0.1 2 3 4 5.6
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	*/
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	arc[6][0] = arc[3][0];
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	b = arc[1][0];
Ivan Mahonin 93cbac
	c = arc[2][0];
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	a = arc[1][0] = (arc[0][0] + b)/2;
Ivan Mahonin 93cbac
	b = (b + c)/2;
Ivan Mahonin 93cbac
	c = arc[5][0] = (arc[6][0] + c)/2;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	a = arc[2][0] = (a + b)/2;
Ivan Mahonin 93cbac
	b = arc[4][0] = (b + c)/2;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	arc[3][0] = (a + b)/2;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	arc[6][1] = arc[3][1];
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	b = arc[1][1];
Ivan Mahonin 93cbac
	c = arc[2][1];
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	a = arc[1][1] = (arc[0][1] + b)/2;
Ivan Mahonin 93cbac
	b = (b + c)/2;
Ivan Mahonin 93cbac
	c = arc[5][1] = (arc[6][1] + c)/2;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	a = arc[2][1] = (a + b)/2;
Ivan Mahonin 93cbac
	b = arc[4][1] = (b + c)/2;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	arc[3][1] = (a + b)/2;
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
void Polyspan::cubic_to(Real x1, Real y1, Real x2, Real y2, Real x, Real y) {
Ivan Mahonin 93cbac
	Vector *current = arc;
Ivan Mahonin 93cbac
	int		num = 0;
Ivan Mahonin 93cbac
	int		level = 0;
Ivan Mahonin 93cbac
	bool	onsecond = false;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	arc[0] = Vector(x, y);
Ivan Mahonin 93cbac
	arc[1] = Vector(x2, y2);
Ivan Mahonin 93cbac
	arc[2] = Vector(x1, y1);
Ivan Mahonin 93cbac
	arc[3] = Vector(cur_x, cur_y);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	// just draw the line if it's outside
Ivan Mahonin 93cbac
	if (clip_cubic(arc, window)) {
Ivan Mahonin 93cbac
		line_to(x,y);
Ivan Mahonin 93cbac
		return;
Ivan Mahonin 93cbac
	}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	// Ok so it's not super degenerate, subdivide and draw (run through minimum subdivision levels first)
Ivan Mahonin 93cbac
	while(current >= arc) { // once current goes below arc, there are no more curves left
Ivan Mahonin 93cbac
		assert(num < MAX_SUBDIVISION_SIZE);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// if we are not at the level minimum
Ivan Mahonin 93cbac
		if (level < MIN_SUBDIVISION_DRAW_LEVELS) {
Ivan Mahonin 93cbac
			subd_cubic_stack(current);
Ivan Mahonin 93cbac
			current += 3; 		// cursor on second curve
Ivan Mahonin 93cbac
			level ++;
Ivan Mahonin 93cbac
			num ++;
Ivan Mahonin 93cbac
			onsecond = false;
Ivan Mahonin 93cbac
			continue;
Ivan Mahonin 93cbac
		} else
Ivan Mahonin 93cbac
		// if the curve is clipping then draw degenerate
Ivan Mahonin 93cbac
		if (clip_cubic(current, window)) {
Ivan Mahonin 93cbac
			line_to(current[0][0], current[0][1]); // backwards so front is destination
Ivan Mahonin 93cbac
			current -= 3;
Ivan Mahonin 93cbac
			if (onsecond) level--;
Ivan Mahonin 93cbac
			onsecond = true;
Ivan Mahonin 93cbac
			num --;
Ivan Mahonin 93cbac
			continue;
Ivan Mahonin 93cbac
		} else
Ivan Mahonin 93cbac
		// split it again, if it's too big
Ivan Mahonin 93cbac
		if (max_edges_cubic(current) > 0.25) { //could use max_edges<3>
Ivan Mahonin 93cbac
			subd_cubic_stack(current);
Ivan Mahonin 93cbac
			current += 3; 		// cursor on second curve
Ivan Mahonin 93cbac
			level ++;
Ivan Mahonin 93cbac
			num ++;
Ivan Mahonin 93cbac
			onsecond = false;
Ivan Mahonin 93cbac
		} else { // NOT TOO BIG? RENDER!!!
Ivan Mahonin 93cbac
			// cur_x, cur_y = current[3], so we need to go 2,1,0
Ivan Mahonin 93cbac
			line_to(current[2][0], current[2][1]);
Ivan Mahonin 93cbac
			line_to(current[1][0], current[1][1]);
Ivan Mahonin 93cbac
			line_to(current[0][0], current[0][1]);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			current -= 3;
Ivan Mahonin 93cbac
			if (onsecond) level--;
Ivan Mahonin 93cbac
			num --;
Ivan Mahonin 93cbac
			onsecond = true;
Ivan Mahonin 93cbac
		}
Ivan Mahonin 93cbac
	}
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
void Polyspan::draw_scanline(int y, Real x1, Real y1, Real x2, Real y2) {
Ivan Mahonin 93cbac
	int	ix1 = (int)floor(x1);
Ivan Mahonin 93cbac
	int	ix2 = (int)floor(x2);
Ivan Mahonin 93cbac
	Real fx1 = x1 - ix1;
Ivan Mahonin 93cbac
	Real fx2 = x2 - ix2;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	Real dx,dy,dydx,mult;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	dx = x2 - x1;
Ivan Mahonin 93cbac
	dy = y2 - y1;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	// case horizontal line
Ivan Mahonin 93cbac
	if (y1 == y2) {
Ivan Mahonin 93cbac
		move_pen(ix2, y); // pen needs to be at the last coord
Ivan Mahonin 93cbac
		return;
Ivan Mahonin 93cbac
	}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	// case all in same pixel
Ivan Mahonin 93cbac
	if (ix1 == ix2) { // impossible for degenerate case (covered by the previous cases)
Ivan Mahonin 93cbac
		current.addcover(dy, (fx1 + fx2)*dy/2); // horizontal trapezoid area
Ivan Mahonin 93cbac
		return;
Ivan Mahonin 93cbac
	}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	if (dx > 0) {
Ivan Mahonin 93cbac
		// ---->	fx1...1  0...1  ...  0...1  0...fx2
Ivan Mahonin 93cbac
		dydx = dy/dx;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// set initial values
Ivan Mahonin 93cbac
		// Iterate through the covered pixels
Ivan Mahonin 93cbac
		mult = (1 - fx1)*dydx;	// next y intersection diff value (at 1)
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// first pixel
Ivan Mahonin 93cbac
		current.addcover(mult, (1 + fx1)*mult/2);	// fx1, fy1, 1, fy@1 - starting trapezoidal area
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// move to the next pixel
Ivan Mahonin 93cbac
		y1 += mult;
Ivan Mahonin 93cbac
		ix1++;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		move_pen(ix1, y);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// set up for whole ones
Ivan Mahonin 93cbac
		while(ix1 != ix2) {
Ivan Mahonin 93cbac
			// trapezoid(0, y1, 1, y1 + dydx);
Ivan Mahonin 93cbac
			current.addcover(dydx,dydx/2); // accumulated area 1/2 the cover
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			// move to next pixel (+1)
Ivan Mahonin 93cbac
			ix1++;
Ivan Mahonin 93cbac
			y1 += dydx;
Ivan Mahonin 93cbac
			move_pen(ix1, y);
Ivan Mahonin 93cbac
		}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// last pixel
Ivan Mahonin 93cbac
		// final y-pos - last intersect pos
Ivan Mahonin 93cbac
		mult = fx2 * dydx;
Ivan Mahonin 93cbac
		current.addcover(mult, (0 + fx2)*mult/2);
Ivan Mahonin 93cbac
	} else {
Ivan Mahonin 93cbac
		// fx2...1  0...1  ...  0...1  0...fx1   <----
Ivan Mahonin 93cbac
		// mult = (0 - fx1) * dy / dx;
Ivan Mahonin 93cbac
		// neg sign sucked into dydx
Ivan Mahonin 93cbac
		dydx = -dy/dx;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// set initial values
Ivan Mahonin 93cbac
		// Iterate through the covered pixels
Ivan Mahonin 93cbac
		mult = fx1*dydx; // next y intersection diff value
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// first pixel
Ivan Mahonin 93cbac
		current.addcover(mult, fx1*mult/2); // fx1, fy1, 0, fy@0 - starting trapezoidal area
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// move to next pixel
Ivan Mahonin 93cbac
		y1 += mult;
Ivan Mahonin 93cbac
		ix1--;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		move_pen(ix1, y);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// set up for whole ones
Ivan Mahonin 93cbac
		while(ix1 != ix2) {
Ivan Mahonin 93cbac
			// trapezoid(0, y1, 1, y1+dydx);
Ivan Mahonin 93cbac
			current.addcover(dydx, dydx/2); // accumulated area 1/2 the cover
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			// move to next pixel (-1)
Ivan Mahonin 93cbac
			y1 += dydx;
Ivan Mahonin 93cbac
			ix1--;
Ivan Mahonin 93cbac
			move_pen(ix1, y);
Ivan Mahonin 93cbac
		}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// last pixel
Ivan Mahonin 93cbac
		mult = y2 - y1; // final y-pos - last intersect pos
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		current.addcover(mult, (fx2+1)*mult/2);
Ivan Mahonin 93cbac
	}
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
void Polyspan::draw_line(Real x1, Real y1, Real x2, Real y2) {
Ivan Mahonin 93cbac
	int iy1 = (int)floor(y1);
Ivan Mahonin 93cbac
	int iy2 = (int)floor(y2);
Ivan Mahonin 93cbac
	Real fy1 = y1 - iy1;
Ivan Mahonin 93cbac
	Real fy2 = y2 - iy2;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	assert(!isnan(fy1));
Ivan Mahonin 93cbac
	assert(!isnan(fy2));
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	Real dx,dy,dxdy,mult,x_from,x_to;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	const Real SLOPE_EPSILON = 1e-10;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	// case all one scanline
Ivan Mahonin 93cbac
	if (iy1 == iy2) {
Ivan Mahonin 93cbac
		draw_scanline(iy1, x1, y1, x2, y2);
Ivan Mahonin 93cbac
		return;
Ivan Mahonin 93cbac
	}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	// difference values
Ivan Mahonin 93cbac
	dy = y2 - y1;
Ivan Mahonin 93cbac
	dx = x2 - x1;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	// case vertical line
Ivan Mahonin 93cbac
	if (dx < SLOPE_EPSILON && dx > -SLOPE_EPSILON) {
Ivan Mahonin 93cbac
		// calc area and cover on vertical line
Ivan Mahonin 93cbac
		if (dy > 0) {
Ivan Mahonin 93cbac
			// ---->	fx1...1  0...1  ...  0...1  0...fx2
Ivan Mahonin 93cbac
			Real sub;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			int ix1 = (int)floor(x1);
Ivan Mahonin 93cbac
			Real fx1 = x1 - ix1;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			// current pixel
Ivan Mahonin 93cbac
			sub = 1 - fy1;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			current.addcover(sub, fx1*sub);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			// next pixel
Ivan Mahonin 93cbac
			iy1++;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			// move pen to next pixel
Ivan Mahonin 93cbac
			move_pen(ix1, iy1);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			while(iy1 != iy2) {
Ivan Mahonin 93cbac
				// accumulate cover
Ivan Mahonin 93cbac
				current.addcover(1,fx1);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
				// next pixel
Ivan Mahonin 93cbac
				iy1++;
Ivan Mahonin 93cbac
				move_pen(ix1, iy1);
Ivan Mahonin 93cbac
			}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			// last pixel
Ivan Mahonin 93cbac
			current.addcover(fy2, fy2*fx1);
Ivan Mahonin 93cbac
		} else {
Ivan Mahonin 93cbac
			Real sub;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			int	ix1 = (int)floor(x1);
Ivan Mahonin 93cbac
			Real fx1 = x1 - ix1;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			// current pixel
Ivan Mahonin 93cbac
			sub = 0 - fy1;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			current.addcover(sub, fx1*sub);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			// next pixel
Ivan Mahonin 93cbac
			iy1--;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			move_pen(ix1, iy1);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			while(iy1 != iy2) {
Ivan Mahonin 93cbac
				// accumulate in current pixel
Ivan Mahonin 93cbac
				current.addcover(-1,-fx1);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
				// move to next
Ivan Mahonin 93cbac
				iy1--;
Ivan Mahonin 93cbac
				move_pen(ix1,iy1);
Ivan Mahonin 93cbac
			}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			current.addcover(fy2-1,(fy2-1)*fx1);
Ivan Mahonin 93cbac
		}
Ivan Mahonin 93cbac
		return;
Ivan Mahonin 93cbac
	}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	// case normal line - guaranteed dx != 0 && dy != 0
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	// calculate the initial intersection with "next" scanline
Ivan Mahonin 93cbac
	if (dy > 0) {
Ivan Mahonin 93cbac
		dxdy = dx/dy;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		mult = (1 - fy1)*dxdy;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// x intersect scanline
Ivan Mahonin 93cbac
		x_from = x1 + mult;
Ivan Mahonin 93cbac
		draw_scanline(iy1, x1, fy1, x_from, 1);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// move to next line
Ivan Mahonin 93cbac
		iy1++;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		move_pen((int)floor(x_from), iy1);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		while(iy1 != iy2) {
Ivan Mahonin 93cbac
			// keep up on the x axis, and render the current scanline
Ivan Mahonin 93cbac
			x_to = x_from + dxdy;
Ivan Mahonin 93cbac
			draw_scanline(iy1, x_from, 0, x_to, 1);
Ivan Mahonin 93cbac
			x_from = x_to;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			// move to next pixel
Ivan Mahonin 93cbac
			iy1++;
Ivan Mahonin 93cbac
			move_pen((int)floor(x_from), iy1);
Ivan Mahonin 93cbac
		}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		//draw the last one, fractional
Ivan Mahonin 93cbac
		draw_scanline(iy2, x_from, 0, x2, fy2);
Ivan Mahonin 93cbac
	} else {
Ivan Mahonin 93cbac
		dxdy = -dx/dy;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		mult = fy1*dxdy;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// x intersect scanline
Ivan Mahonin 93cbac
		x_from = x1 + mult;
Ivan Mahonin 93cbac
		draw_scanline(iy1,x1,fy1,x_from,0);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// each line after
Ivan Mahonin 93cbac
		iy1--;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		move_pen((int)floor(x_from), iy1);
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		while(iy1 != iy2) {
Ivan Mahonin 93cbac
			x_to = x_from + dxdy;
Ivan Mahonin 93cbac
			draw_scanline(iy1, x_from, 1, x_to, 0);
Ivan Mahonin 93cbac
			x_from = x_to;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
			iy1--;
Ivan Mahonin 93cbac
			move_pen((int)floor(x_from), iy1);
Ivan Mahonin 93cbac
		}
Ivan Mahonin 93cbac
		// draw the last one, fractional
Ivan Mahonin 93cbac
		draw_scanline(iy2, x_from, 1, x2, fy2);
Ivan Mahonin 93cbac
	}
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
Real Polyspan::extract_alpha(Real area, bool evenodd) const {
Ivan Mahonin 93cbac
	if (area < 0)
Ivan Mahonin 93cbac
		area = -area;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	if (evenodd) {
Ivan Mahonin 93cbac
		// even-odd winding style
Ivan Mahonin 93cbac
		while (area > 1)
Ivan Mahonin 93cbac
			area -= 2;
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
		// want pyramid like thing
Ivan Mahonin 93cbac
		if (area < 0)
Ivan Mahonin 93cbac
			area = -area;
Ivan Mahonin 93cbac
	} else {
Ivan Mahonin 93cbac
		// non-zero winding style
Ivan Mahonin 93cbac
		if (area > 1)
Ivan Mahonin 93cbac
			return 1;
Ivan Mahonin 93cbac
	}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
	return area;
Ivan Mahonin 93cbac
}
Ivan Mahonin 93cbac
Ivan Mahonin 93cbac
/* === E N T R Y P O I N T ================================================= */