//Output.cpp, Copyright (c) 2000-2008 R.Lackner
//
//    This file is part of RLPlot.
//
//    RLPlot is free software; you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation; either version 2 of the License, or
//    (at your option) any later version.
//
//    RLPlot is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with RLPlot; if not, write to the Free Software
//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>				//file open flags
#include <sys/stat.h>			//I/O flags
#ifdef _WINDOWS
	#include <io.h>					//for read/write
#else
	#define O_BINARY 0x0
	#include <unistd.h>
#endif
#include "rlplot.h"

tag_Units Units[] = {{0, "mm", 1.0f}, {1, "cm", 10.0f}, {2, "inch", 25.4f},
	};

extern Default defs;
extern GraphObj *CurrGO, *TrackGO;		//Selected Graphic Objects
extern Label *CurrLabel;
extern Graph *CurrGraph;
extern dragHandle *CurrHandle;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Output base class
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
anyOutput::anyOutput()
{
	units = defs.cUnits;		minLW = 1;
	dBgCol = dFillCol = defs.Color(COL_BG);
	xAxis.owner = yAxis.owner = zAxis.owner = (void *)this;
	xAxis.flags = yAxis.flags = zAxis.flags = 0L;
	xAxis.min = yAxis.min = zAxis.min = 0.0;
	xAxis.max = yAxis.max = zAxis.max = 1.0;
	xAxis.nBreaks = yAxis.nBreaks = zAxis.nBreaks = 0;
	xAxis.breaks = yAxis.breaks = zAxis.breaks = 0L;
	ddx = ddy = ddz = 1.0;
	RLP.finc = 1.0f;
	RLP.fp = 0.0f;
	dPattern = 0xffffffffL;			//impossible (invisible)line pattern to start with
	dBgCol = defs.Color(COL_BG);	OC_type = OC_UNKNOWN;
	MrkMode = MRK_NONE;				MrkRect = 0L;
	VPorg.fx = VPorg.fy = 0.0;		VPscale = 1.0;
	MenuHeight = 0;					cCursor = MC_ARROW;
	rotM[0][0] = rotM[1][1] = rotM[2][2] = 1.0;
	rotM[0][1] = rotM[0][2] = rotM[1][0] = rotM[1][2] = rotM[2][0] = rotM[2][1] = 0.0;
	hasHistMenu = false;			HistMenuSize = 0;
	light_source.fx = light_source.fy = 0.0;
}

void
anyOutput::SetRect(fRECT rec, int u, AxisDEF *x_ax, AxisDEF *y_ax)
{
	double spx, spy;

	if (u >= 0 && u < NUM_UNITS) defs.cUnits = units = u;
	else units = defs.cUnits;
	spx = rec.Xmax - rec.Xmin;	spy = rec.Ymin -rec.Ymax;
	MrkMode = MRK_NONE;
	Box1.Xmin = co2fix(rec.Xmin);	Box1.Ymin = co2fiy(rec.Ymax);
	Box1.Xmax = co2fix(rec.Xmax);	Box1.Ymax = co2fiy(rec.Ymin);
	if(!x_ax || !y_ax) return;
	if(x_ax->flags & AXIS_DEFRECT) {
		Box1.Xmin = co2fix(x_ax->loc[0].fx);
		Box1.Xmax = co2fix(x_ax->loc[1].fx);
		spx = x_ax->loc[1].fx - x_ax->loc[0].fx;
		}
	if(y_ax->flags & AXIS_DEFRECT) {
		Box1.Ymin = co2fiy(y_ax->loc[0].fy);
		Box1.Ymax = co2fiy(y_ax->loc[1].fy);
		spy = y_ax->loc[1].fy - y_ax->loc[0].fy;
		}
	memcpy(&xAxis, x_ax, sizeof(AxisDEF));
	memcpy(&yAxis, y_ax, sizeof(AxisDEF));
	ddy = GetAxisFac(&yAxis, un2fiy(spy), 1);
	ddx = GetAxisFac(&xAxis, un2fix(spx), 0);
	xAxis.owner = yAxis.owner = this;
}

void
anyOutput::UseAxis(AxisDEF *ax, int type)
{
	AxisDEF *cax;

	MrkMode = MRK_NONE;
	if(!ax) return;
	switch (type) {
		case 1:			//x-axis
			memcpy(cax = &xAxis, ax, sizeof(AxisDEF));
			Box1.Xmin = co2fix(ax->loc[0].fx);
			Box1.Xmax = co2fix(ax->loc[1].fx);
			ddx = GetAxisFac(&xAxis, Box1.Xmax - Box1.Xmin, 0);
			break;
		case 2:			//y-axis
			memcpy(cax = &yAxis, ax, sizeof(AxisDEF));
			if(ax->flags & AXIS_3D) {
				Box1.Ymax = co2fiy(ax->loc[0].fy);
				Box1.Ymin = co2fiy(ax->loc[1].fy);
				}
			else {
				Box1.Ymin = co2fiy(ax->loc[0].fy);
				Box1.Ymax = co2fiy(ax->loc[1].fy);
				}
			ddy = GetAxisFac(&yAxis, Box1.Ymax - Box1.Ymin, 1);
			break;
		case 3:			//z-axis
			memcpy(cax = &zAxis, ax, sizeof(AxisDEF));
			Box1z.fx = un2fiz(ax->loc[0].fz);
			Box1z.fy = un2fiz(ax->loc[1].fz);
			ddz = GetAxisFac(&zAxis, Box1z.fy - Box1z.fx, 2);
			break;
		default:		//unnknown direction
			return;
		}
	cax->owner = this;
}

void
anyOutput::SetSpace(fPOINT3D *cub1, fPOINT3D *cub2, int u, double *rot, 
	fPOINT3D *cent, AxisDEF *x_ax, AxisDEF *y_ax, AxisDEF *z_ax)
{
	double rotQ[6];		//rotation definition:
						//  unit vector x
						//              y
						//              z
						//  sin(phi)
						//  cos(phi)
						//  1.0 -cos(phi)
	double dp;

	if (u >= 0 && u < NUM_UNITS) defs.cUnits = units = u;
	else units = defs.cUnits;
	MrkMode = MRK_NONE;
	HideTextCursor();
	memcpy(&xAxis, x_ax, sizeof(AxisDEF));
	memcpy(&yAxis, y_ax, sizeof(AxisDEF));
	memcpy(&zAxis, z_ax, sizeof(AxisDEF));
	xAxis.owner = yAxis.owner = zAxis.owner = this;
	//assume resolution equal in all directions: use un2fix() for
	//   all coordinates
	Box1.Xmin = co2fix(cub1->fx);		Box1.Ymin = co2fiy(cub2->fy);
	Box1.Xmax = co2fix(cub2->fx);		Box1.Ymax = co2fiy(cub1->fy);
	Box1z.fx = un2fiz(cub1->fz);		Box1z.fy = un2fiz(cub2->fz);
	if(x_ax->flags & AXIS_DEFRECT) {
		Box1.Xmin = co2fix(x_ax->loc[0].fx);	Box1.Xmax = co2fix(x_ax->loc[1].fx);
		}
	if(y_ax->flags & AXIS_DEFRECT) {
		Box1.Ymax = co2fiy(y_ax->loc[0].fy);	Box1.Ymin = co2fiy(y_ax->loc[1].fy);
		}
	if(z_ax->flags & AXIS_DEFRECT) {
		Box1z.fx = un2fiz(z_ax->loc[0].fz);		Box1z.fy = un2fiz(z_ax->loc[1].fz);
		}
	ddx = GetAxisFac(&xAxis, Box1.Xmax-Box1.Xmin, 0);
	ddy = GetAxisFac(&yAxis, Box1.Ymax-Box1.Ymin, 1);
	ddz = GetAxisFac(&zAxis, Box1z.fy - Box1z.fx, 2);
	rotC.fx = un2fix(cent->fx)+ VPorg.fx;
	rotC.fy = un2fiy(cent->fy)+ VPorg.fy;	
	rotC.fz = un2fiz(cent->fz);
	memcpy(rotQ, rot, sizeof(rotQ));
	//normalize vector part of rotQ
	dp = sqrt(rotQ[0]*rotQ[0] + rotQ[1]*rotQ[1] + rotQ[2]*rotQ[2]);
	rotQ[0] /= dp;		rotQ[1] /= dp;		rotQ[2] /= dp;
	dp = sqrt(rotQ[0]*rotQ[0] + rotQ[1]*rotQ[1] + rotQ[2]*rotQ[2]);
	//set up rotation matrix from quaternion
	//see: Graphic Gems, A.S. Glassner ed.; Academic Press Inc.
	//M.E. Pique: Rotation Tools
	// ISBN 0-12-286165-5, p. 466
	rotM[0][0] = rotQ[5]*rotQ[0]*rotQ[0] + rotQ[4];
	rotM[0][1] = rotQ[5]*rotQ[0]*rotQ[1] + rotQ[3]*rotQ[2];
	rotM[0][2] = rotQ[5]*rotQ[0]*rotQ[2] - rotQ[3]*rotQ[1];
	rotM[1][0] = rotQ[5]*rotQ[0]*rotQ[1] - rotQ[3]*rotQ[2];
	rotM[1][1] = rotQ[5]*rotQ[1]*rotQ[1] + rotQ[4];
	rotM[1][2] = rotQ[5]*rotQ[1]*rotQ[2] + rotQ[3]*rotQ[0];
	rotM[2][0] = rotQ[5]*rotQ[0]*rotQ[2] + rotQ[3]*rotQ[1];
	rotM[2][1] = rotQ[5]*rotQ[1]*rotQ[2] - rotQ[3]*rotQ[0];
	rotM[2][2] = rotQ[5]*rotQ[2]*rotQ[2] + rotQ[4];
}

void
anyOutput::LightSource(double x, double y)
{
	int i, j, m;
	double angx, angy;
	double a[3][3], b[3][3];

	if(light_source.fx == 0.0 || light_source.fy == 0.0 ||
		x != light_source.fx || y != light_source.fy) {
		light_source.fx = x;		light_source.fy = y;
		angx = x * 0.017453292;		angy = y * 0.017453292;
		for (i = 0; i < 3; i++)	for(j = 0; j < 3; j++) {
			a[i][j] = b[i][j] = 0.0;
			}
		//first axis
		a[0][0] = 1.0;			a[1][1] = cos(angx);		a[1][2] = -sin(angx);
		a[2][1] = -a[1][2];		a[2][2] = a[1][1];
		//second axis
		b[0][0] = cos(angy);	b[0][1] = -sin(angy);		b[1][0] = -b[0][1];
		b[1][1] = b[0][0];		b[2][2] = 1.0;
		//combine the two rotations
		for (i = 0; i < 3; i++) for(j = 0; j < 3; j++){
			light_vec[i][j] = 0.0;
			for(m = 0; m < 3; m++) light_vec[i][j] += (a[i][m] * b[m][j]);
			}
		}
}

DWORD
anyOutput::VecColor(double *plane_vec, DWORD color1, DWORD color2)
{
	double v[3], vec[3], vlength;
	int i, j;

	//rotate vector towards the light source
	if(!plane_vec) return color1;
	v[0] = plane_vec[0];		v[1] = plane_vec[2];	v[2] = plane_vec[1];
	for (i = 0; i < 3; i++) for(j = 0, vec[i] = 0.0; j < 3; j++)
		vec[i] += (light_vec[i][j] * v[j]);
	//normalize vec: both vector should have unit length but make sure
	vlength = sqrt(vec[0]*vec[0]+vec[1]*vec[1]+vec[2]*vec[2]);
	if(vlength < 0.9) return color1;
	vec[0] /= vlength;	vec[1] /= vlength;	vec[2] /= vlength;
	//calc color
	return IpolCol(color1, color2, fabs(vec[1]));
}

bool
anyOutput::GetSize(RECT *rc)
{
	memcpy(rc, &DeskRect, sizeof(RECT));
	return true;
}

double
anyOutput::fx2fix(double x)
{
	double temp;

	x = TransformValue(&xAxis, x, true);
	temp = (x - xAxis.min)*ddx;
	if(0 == (xAxis.flags & AXIS_INVERT)) return temp + Box1.Xmin;
	return Box1.Xmax-temp;
}

double
anyOutput::fy2fiy(double y)
{
	double temp;

	y = TransformValue(&yAxis, y, true);
	temp = (y - yAxis.min)*ddy;
	if(AXIS_INVERT == (yAxis.flags & AXIS_INVERT)) return temp + Box1.Ymin;
	return Box1.Ymax-temp;
}

double
anyOutput::fz2fiz(double z)
{
	double temp;

	z = TransformValue(&zAxis, z, true);
	temp = (z - zAxis.min)*ddz;
	if(0 == (zAxis.flags & AXIS_INVERT)) return temp + Box1z.fx;
	return Box1z.fy-temp;
}

bool 
anyOutput::fp2fip(lfPOINT *fdp, lfPOINT *fip)
{
	double x, y, si, csi, temp;

	if((xAxis.flags & AXIS_ANGULAR) && (yAxis.flags & AXIS_RADIAL)) {
		x = 6.283185307 * TransformValue(&xAxis, fdp->fx + xAxis.Start, true)/(xAxis.max-xAxis.min);
		si = sin(x);					csi = cos(x);
		y = TransformValue(&yAxis, fdp->fy, true);
		temp = (y - yAxis.min)*ddy;
		if(yAxis.flags & AXIS_INVERT) temp = Box1.Ymax - Box1.Ymin - temp;
		fip->fx = ((Box1.Xmin + Box1.Xmax)/2.0) + csi * temp;
		if(xAxis.flags & AXIS_INVERT) fip->fy = Box1.Ymax + si * temp;
		else fip->fy = Box1.Ymax - si * temp;
		fip->fy += disp_y;
		return true;
		}
	else {
		fip->fx = fx2fix(fdp->fx);		fip->fy = fy2fiy(fdp->fy);
		return true;
		}
	return false;
}

bool 
anyOutput::fvec2ivec(fPOINT3D *v, fPOINT3D *iv)
{
	double x, y, z;
	
	if(!v || !iv) return false;
	x = fx2fix(v->fx)-rotC.fx;
	y = fy2fiy(v->fy)-rotC.fy;
	z = fz2fiz(v->fz)-rotC.fz;
	iv->fx = x * rotM[0][0] + y * rotM[0][1] + z * rotM[0][2] + rotC.fx;
	iv->fy = x * rotM[1][0] + y * rotM[1][1] + z * rotM[1][2] + rotC.fy;
	iv->fz = x * rotM[2][0] + y * rotM[2][1] + z * rotM[2][2] + rotC.fz;
	iv->fx += disp_x;			iv->fy += disp_y;
	return true;
}

bool
anyOutput::cvec2ivec(fPOINT3D *v, fPOINT3D *iv)
{
	double x, y, z;
	
	if(!v || !iv) return false;
	x = co2fix(v->fx)-rotC.fx;
	y = co2fiy(v->fy)-rotC.fy;
	z = un2fiz(v->fz)-rotC.fz;
	iv->fx = x * rotM[0][0] + y * rotM[0][1] + z * rotM[0][2] + rotC.fx;
	iv->fy = x * rotM[1][0] + y * rotM[1][1] + z * rotM[1][2] + rotC.fy;
	iv->fz = x * rotM[2][0] + y * rotM[2][1] + z * rotM[2][2] + rotC.fz;
	iv->fx += disp_x;			iv->fy += disp_y;
	return true;
}

bool
anyOutput::uvec2ivec(fPOINT3D *v, fPOINT3D *iv)
{
	double x, y, z;
	
	if(!v || !iv) return false;
	x = un2fix(v->fx);
	y = un2fiy(v->fy);
	z = un2fiz(v->fz);
	iv->fx = x * rotM[0][0] + y * rotM[0][1] + z * rotM[0][2];
	iv->fy = x * rotM[1][0] + y * rotM[1][1] + z * rotM[1][2];
	iv->fz = x * rotM[2][0] + y * rotM[2][1] + z * rotM[2][2];
	return true;
}

double
anyOutput::un2fix(double x)
{
	return (x * VPscale * hres*Units[units].convert/25.4);
}

double
anyOutput::un2fiy(double y)
{
	return (y * VPscale * vres*Units[units].convert/25.4);
}

double
anyOutput::un2fiz(double z)
{
	return (z * VPscale * hres*Units[units].convert/25.4);
}

double
anyOutput::fix2un(double fix)
{
	return (fix/Units[units].convert*25.4/hres)/VPscale;
}

double
anyOutput::fiy2un(double fiy)
{
	return (fiy/Units[units].convert*25.4/vres)/VPscale;
}

bool
anyOutput::GetLine(LineDEF *lDef)
{
	if(lDef) {
		lDef->width = LineWidth;
		lDef->color = dLineCol;
		lDef->pattern = dPattern;
		return true;
		}
	return false;
}

bool
anyOutput::SetTextSpec(TextDEF *set)
{
	memcpy(&TxtSet, set, sizeof(TextDEF));
	TxtSet.text = 0L;
	return true;
}

bool
anyOutput::ShowMark(void *src, int Mode)
{
	GraphObj *go;

	if(MrkMode != MRK_NONE) HideMark();
	MrkMode = Mode;			MrkRect = src;		HideCopyMark();
	switch (Mode) {
		case MRK_INVERT:
			return UpdateRect((RECT*)src, true);
		case MRK_GODRAW:	case MRK_SSB_DRAW:
			go = (GraphObj *) src;				go->DoMark(this, true);
			CurrGO = go;
			if(CurrLabel && CurrLabel != CurrGO) {
				HideTextCursor();				CurrLabel = 0L;
				}
			return true;
		}
	return false;
}

bool
anyOutput::HideMark()
{
	switch(MrkMode) {
		case MRK_NONE:
			return true;
		case MRK_INVERT:
			MrkMode = MRK_NONE;
			return UpdateRect((RECT*)MrkRect, false);
		case MRK_GODRAW:
			MrkMode = MRK_NONE;					//inhibit reentrance
			if(CurrLabel && CurrLabel->Command(CMD_HIDEMARK, 0L, this))
				CurrGraph->Command(CMD_REDRAW, 0L, this);
			else if(MrkRect)((GraphObj*)MrkRect)->DoMark(this, false);
			else if(CurrGraph) CurrGraph->Command(CMD_REDRAW, 0L, this);
			return true;
		case MRK_SSB_DRAW:
			MrkMode = MRK_NONE;
			if (MrkRect) ((GraphObj*)MrkRect)->DoMark(this, false);
			return true;
		}
	return false;
}

int
anyOutput::CalcCursorPos(char *txt, POINT p, POINT *fit)
{
	int i, d, w, h, CurrPos;

	d = TxtSet.iSize >>2;
	if(!txt || !fit) return 0;
	if (!(i = (int)strlen(txt)))return 0;
	//right justified text
	if(TXA_HRIGHT == (TxtSet.Align & TXA_HRIGHT)){
		if((p.x - fit->x) < d) return i;
		for (CurrPos = i-1; CurrPos >= 0; CurrPos--) {
			if(!oGetTextExtent(txt+CurrPos, i-CurrPos, &w, &h)) return 0;
			if((w = p.x - w - d) <= fit->x) return CurrPos;
			}
		return 0;
		}
	//left justified text
	else {
		if((fit->x - p.x) < d) return 0;
		for (CurrPos = i; CurrPos >= 0; CurrPos--) {
			if(!oGetTextExtent(txt, CurrPos, &w, &h)) return 0;
			if((w = p.x + w - d) <= fit->x) return CurrPos;
			}
		}
	return 0;
}

bool
anyOutput::TextCursor(char *txt, POINT p, POINT *fit, int *pos, int dx)
{
	int i, w, h, CurrPos;
	RECT disp;

	if(fit) CurrPos = CalcCursorPos(txt, p, fit);
	//recalculate caret position
	if(txt && pos && !fit){
		if(TxtSet.Align & TXA_HRIGHT) {		//right justfied text
			if((i = (int)strlen(txt)-(*pos))){
				if(!oGetTextExtent(txt+(*pos), i, &w, &h)) return false;
				w = p.x - w;
				}
			else w = p.x-1;
			}
		else {								//left justified text
			if(!(*pos)) w = 0;
			else if(!oGetTextExtent(txt, *pos, &w, &h))return false;
			w += p.x;
			}
		}
	else if(!fit)return false;
	//right justified text: search caret and cursor position
	else if(txt && (TxtSet.Align & TXA_HRIGHT)){
		i = (int)strlen(txt);
		if(i == CurrPos) w = 1;
		else if(!oGetTextExtent(txt+CurrPos, i-CurrPos, &w, &h)) return false;
		w = p.x - w;
		}
	//left justified text: search caret and cursor position
	else if(txt && fit) {
		if (!CurrPos) w = 0;
		else if(!oGetTextExtent(txt, CurrPos, & w, &h)) return false;
		w += p.x;
		}
	if(fit && pos) *pos = CurrPos;
	disp.left = disp.right = w+dx;
	disp.top = p.y;
	if(TxtSet.Align & TXA_VCENTER) {
		disp.top -= (TxtSet.iSize>>1);
		}
#ifdef _WINDOWS
	disp.bottom = disp.top + TxtSet.iSize-1;
#else
	disp.top -= 1;
	disp.bottom = disp.top + iround(TxtSet.iSize*1.25)-2;
#endif
	ShowTextCursor(this, &disp, 0x0L);
	return true;
}

//we need our own implementation of Bresenham's line drawing algorithm to draw
//   a line with variable pattern sizes.
//Ref: P.S. Heckbert (1990) "Digital Line Drawing", in: Graphic Gems
//   (A.S. Glassner, ed.); Academic Press, Inc.,
//   ISBN 0-12-286165-5
bool
anyOutput::PatLine(POINT p1, POINT p2)
{
	int d, ax, ay, sx, sy, dx, dy;
	double fInc2;
	bool bPen;
	POINT tr[2];

	dx = p2.x - p1.x;
	fInc2 = RLP.finc * 0.414213562;			//increment by sqrt(2) if 45� slope
	if ( p2.x < p1.x) { 	ax = (-dx)<<1;		sx = -1;		}
	else {					ax = dx <<1;		sx = 1;		}
	dy = p2.y - p1.y;
	if (p2.y < p1.y) {	ay = (-dy)<<1;		sy = -1;		}
	else {					ay = dy<<1;			sy = 1;		}
	tr[0].x = tr[1].x = p1.x;							tr[0].y = tr[1].y = p1.y;
	if(dPattern &(1 << ((int)RLP.fp))) bPen = false;
	else bPen = true;
	if (ax > ay) {													// x dominant
		d = ay - (ax >>1);
		for ( ; ; ) {
			RLP.fp += RLP.finc;
			if(RLP.fp >= 32.0f) RLP.fp -= 32.0f;
			if(bPen) {
				if(tr[1].x == p2.x) return oSolidLine(tr);
				if (d >= 0) {tr[1].y += sy;	d -= ax; RLP.fp += fInc2;}
				tr[1].x += sx;
				if(dPattern &(1 << ((int)RLP.fp))) {
					bPen = false;
					oSolidLine(tr);
					tr[0].x = tr[1].x; tr[0].y = tr[1].y;
					}
				}
			else {
				if(tr[0].x == p2.x) return true;
				if (d >= 0) {tr[0].y += sy;	d -= ax; RLP.fp += fInc2;}
				tr[0].x += sx;
				if(!(dPattern &(1 << ((int)RLP.fp)))) {
					bPen = true;
					tr[1].x = tr[0].x; tr[1].y = tr[0].y;
					}
				}
			d += ay;
			}
		}
	else {															// y dominant
		d = ax - (ay >>1);
		for ( ; ; ) {
			RLP.fp += RLP.finc;
			if(RLP.fp >= 32.0f) RLP.fp -= 32.0f;
			if (bPen){
				if (tr[1].y == p2.y) return oSolidLine(tr);
				if (d >= 0) {tr[1].x += sx; d -= ay; RLP.fp += fInc2;}
				tr[1].y += sy;
				if(dPattern &(1 << ((int)RLP.fp))) {
					bPen = false;
					oSolidLine(tr);
					tr[0].x = tr[1].x; tr[0].y = tr[1].y;
					}
				}
			else {
				if (tr[0].y == p2.y) return true;
				if (d >= 0) {tr[0].x += sx; d -= ay; RLP.fp += fInc2;}
				tr[0].y += sy;
				if(!(dPattern &(1 << ((int)RLP.fp)))) {
					bPen = true;
					tr[1].x = tr[0].x; tr[1].y = tr[0].y;
					}
				}
			d += ax;
			}
		}
}

static int Helv_Char_Width [] = {
 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41,
 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41,
 15, 17, 20, 31, 31, 49, 37, 11, 18, 18, 21, 32, 15, 18, 15, 15,
 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 15, 15, 32, 32, 32, 31,
 56, 37, 37, 40, 40, 37, 34, 43, 40, 15, 28, 37, 31, 45, 40, 43,
 37, 43, 40, 37, 33, 40, 37, 54, 35, 35, 34, 15, 15, 15, 24, 31,
 18, 31, 31, 28, 31, 31, 15, 31, 31, 11, 13, 28, 11, 47, 31, 31,
 31, 31, 18, 28, 15, 31, 29, 39, 27, 27, 27, 18, 14, 18, 32, 41,
 31, 41, 12, 31, 18, 55, 31, 31, 18, 56, 37, 18, 55, 41, 34, 41,
 41, 12, 12, 18, 18, 19, 31, 55, 16, 55, 28, 18, 52, 41, 27, 37,
 15, 17, 31, 31, 31, 31, 14, 31, 18, 41, 20, 31, 32, 18, 41, 30,
 22, 30, 18, 18, 18, 32, 30, 15, 18, 18, 20, 31, 46, 46, 46, 34,
 37, 37, 37, 37, 37, 37, 55, 40, 37, 37, 37, 37, 15, 15, 15, 15,
 40, 40, 43, 43, 43, 43, 43, 32, 43, 40, 40, 40, 40, 37, 37, 34,
 31, 31, 31, 31, 31, 31, 49, 28, 31, 31, 31, 31, 15, 15, 15, 15,
 31, 31, 31, 31, 31, 31, 31, 30, 34, 31, 31, 31, 31, 28, 31, 28};

/*
 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 18, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 18, 18, 24, 42, 39, 66, 51, 18, 24, 24, 30, 42, 18, 45, 18, 21,
 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 18, 18, 45, 45, 45, 36,
 75, 51, 51, 54, 54, 48, 42, 57, 54, 24, 39, 54, 42, 63, 54, 54,
 48, 54, 51, 48, 48, 54, 51, 66, 51, 48, 45, 21, 21, 21, 36, 42,
 18, 39, 42, 36, 42, 39, 24, 42, 39, 18, 18, 36, 18, 60, 42, 39,
 42, 42, 27, 36, 24, 42, 39, 54, 36, 39, 36, 24, 18, 24, 42, 18,
 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
 18, 18, 39, 42, 39, 42, 18, 39, 24, 57, 27, 42, 45, 24, 57, 24,
 27, 42, 21, 21, 21, 42, 36, 18, 21, 21, 27, 42, 57, 57, 57, 36,
 51, 51, 51, 51, 51, 51, 69, 54, 48, 48, 48, 48, 24, 24, 24, 24,
 54, 54, 54, 54, 54, 54, 54, 42, 54, 54, 54, 54, 54, 48, 48, 45,
 39, 39, 39, 39, 39, 39, 63, 36, 39, 39, 39, 39, 18, 18, 18, 18,
 39, 42, 39, 39, 39, 39, 39, 42, 39, 42, 42, 42, 42, 39, 42, 39};
*/
static int Cour_Char_Width [] = {
 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 35, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35};



bool
anyOutput::oGetTextExtent(char *text, int cb, int *width, int *height)
{
	int *CharWidth;
	int i, w;

	switch (TxtSet.Font) {
	case FONT_COURIER:		CharWidth = Cour_Char_Width;	break;
	default:				CharWidth = Helv_Char_Width;	break;
		}
	if(!cb && text) cb = (int)strlen(text);
	for(i = w = 0; i < cb; i++) w += ((unsigned)text[i] < 256 ? CharWidth[(unsigned)text[i]] : 35);
	*width = iround(((double)w * (double)TxtSet.iSize)/52.0);
	*height = TxtSet.iSize;
	return true;
}

bool
anyOutput::oGetTextExtentW(w_char *text, int cb, int *width, int *height)
{
	if(cb < 1) for(cb = 0; text[cb]; cb++);
	*width = (TxtSet.iSize * cb)>>1;
	*height = TxtSet.iSize;
	return true;
}

bool 
anyOutput::oSphere(int cx, int cy, int r, POINT *pts, int cp, char *nam)
{
	FillDEF fd;
	HatchOut *ho;
	int i, j, mlw;

	if(pts && cp) oPolygon(pts, cp, nam);
	else oCircle(cx - r, cy - r, cx + r + 1, cy + r + 1, nam);
	fd.color = dFillCol;		fd.color2 = dFillCol2;
	fd.hatch = 0L;			fd.scale = 1.0;
	fd.type = FILL_NONE;
	if(ho = new HatchOut(this)) {
		ho->SetFill(&fd);
		mlw = minLW;		minLW = ho->minLW = 2;
		ho->light_source.fx = light_source.fx;
		ho->light_source.fy = light_source.fy;
		for(i = 0; i < 3; i++) for (j = 0; j < 3; j++) {
			ho->light_vec[i][j] = light_vec[i][j];
			}
		ho->oSphere(cx, cy, r-iLine, pts, cp, 0L);
		delete(ho);		minLW = mlw;
		return true;
		}
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Process hatch patterns in an output class
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
enum {HO_NONE, HO_RECT, HO_CIRCLE, HO_ELLIPSE, HO_BIGELLYPSE,
	HO_POLYGON};
struct _HatchDef{
	union {
		struct {
			RECT rec;
			}rec;
		struct {
			POINT centre;
			long sr;
			}cir;
		struct {
			POINT centre;
			int ix, iy;
			unsigned long sab;
			}ell;
		struct {
			POINT fo[2];
			unsigned int rsab;
			int a, b;
			}bell;
		struct {
			POINT *pts;
			int cp;
			}plg;
		};
}HatchDef;

HatchOut::HatchOut(anyOutput *Parent):anyOutput()
{
	ParInit = false;
	ho = HO_NONE;
	ht = FILL_COMBS;
	out = Parent;
	xbase = ybase = 1.5;
	units = 0;					//use mm for defaults
	if(Parent) out->GetSize(&DeskRect);
	else DeskRect.left = DeskRect.right = DeskRect.top = DeskRect.bottom = 0;
	MyLineDef.width = 0.0f;
	MyLineDef.patlength = 10.0f;
	MyLineDef.color = 0x00ff0000L;
	MyLineDef.pattern = 0x00000000L;
}


HatchOut::~HatchOut()
{
}

bool
HatchOut::SetFill(FillDEF *fill)
{
	if(!fill) return false;
	ht = (fill->type & 0xff);
	if(fill->hatch) memcpy(&MyLineDef, fill->hatch, sizeof(LineDEF));
	//we assume that all operations are at a 1:1 pixel relation to the parent,
	//  but we use un2fix and un2fiy from the parent: correct for zoom ....
	switch(out->units) {
	case 0:						//parent uses mm
		xbase = out->un2fix(1.5);		ybase = out->un2fiy(1.5);
		break;
	case 1:						//parent uses cm
		xbase = out->un2fix(0.15);		ybase = out->un2fiy(0.15);
		break;
	case 2:						//parent uses inches
		xbase = out->un2fix(0.059);		ybase = out->un2fiy(0.059);
		break;
		}
	if(fill->scale >0.05f && fill->scale < 20.0) {
		xbase *= fill->scale;		ybase *= fill->scale;
		}
	dFillCol = fill->color;
	dFillCol2 = fill->color2;
	return true;
}

bool
HatchOut::StartPage()
{
	if(out) out->GetSize(&DeskRect);
	return true;
}

bool
HatchOut::oCircle(int x1, int y1, int x2, int y2, char *nam)
{
	long tmp;

	if(x1 < x2) {		UseRect.left = x1;		UseRect.right = x2;		}
	else {				UseRect.left = x2;		UseRect.right = x1;		}
	if(y1 < y2) {		UseRect.top = y1;			UseRect.bottom = y2;		}
	else {				UseRect.top = y2;			UseRect.bottom = y1;		}
	if((UseRect.right -UseRect.left)==(UseRect.bottom-UseRect.top)) {
		HatchDef.cir.centre.x = (UseRect.right + UseRect.left)/2;
		HatchDef.cir.centre.y = (UseRect.bottom + UseRect.top)/2;
		tmp = (UseRect.right - UseRect.left)/2;
		HatchDef.cir.sr = (long)tmp * (long)tmp-1;
		if(HatchDef.cir.sr >9) HatchDef.cir.sr -= tmp;	//stay inside circle
		ho = HO_CIRCLE;
		PrepareParent(false);
		return DoHatch();
		}
	//for small ellipses use the centered equation
	if((UseRect.right -UseRect.left) <512 && (UseRect.bottom-UseRect.top)<512) {
		ho = HO_ELLIPSE;
		HatchDef.ell.centre.x = (UseRect.right + UseRect.left)/2;
		HatchDef.ell.centre.y = (UseRect.bottom + UseRect.top)/2;
		HatchDef.ell.ix = tmp = (UseRect.right - UseRect.left)/2;
		HatchDef.ell.sab = tmp * tmp;
		HatchDef.ell.iy = tmp = (UseRect.bottom - UseRect.top)/2;
		HatchDef.ell.sab *= (tmp * tmp);
		PrepareParent(false);
		return DoHatch();
		}
	//for bigger ellipses we use the focuses to describe the ellipse
	//  this reduces numerical problems
	ho = HO_BIGELLYPSE;
	HatchDef.bell.fo[0].x= HatchDef.bell.fo[1].x= (UseRect.right+UseRect.left)/2;
	HatchDef.bell.fo[0].y= HatchDef.bell.fo[1].y= (UseRect.bottom+UseRect.top)/2;
	if((UseRect.right -UseRect.left) >(UseRect.bottom-UseRect.top)){
		HatchDef.bell.rsab =	UseRect.right - UseRect.left;
		HatchDef.bell.a = (UseRect.right - UseRect.left)/2;
		HatchDef.bell.b = (UseRect.bottom - UseRect.top)/2;
		tmp = isqr(HatchDef.bell.a*HatchDef.bell.a -
			HatchDef.bell.b*HatchDef.bell.b);
		HatchDef.bell.fo[0].x -= tmp;
		HatchDef.bell.fo[1].x += tmp;
		}
	else {
		HatchDef.bell.rsab =	UseRect.bottom - UseRect.top;
		HatchDef.bell.b = (UseRect.right - UseRect.left)/2;
		HatchDef.bell.a = (UseRect.bottom - UseRect.top)/2;
		tmp = isqr(HatchDef.bell.a*HatchDef.bell.a -
			HatchDef.bell.b*HatchDef.bell.b);
		HatchDef.bell.fo[0].y -= tmp;
		HatchDef.bell.fo[1].y += tmp;
		}
	PrepareParent(false);
	return DoHatch();
}

bool
HatchOut::oSphere(int cx, int cy, int r, POINT *pts, int cp, char *nam)
{
	double v[3], vec[3];
	int i, j;

	ht = FILL_LIGHT3D;

	v[0] = 0.0;		v[1] = 1.0;		v[2] = 0.0;
	for (i = 0; i < 3; i++) for(j = 0, vec[i] = 0.0; j < 3; j++)
		vec[i] += (light_vec[i][j] * v[j]);
	circ_grad.cx = cx + iround(vec[0]*((double)r));
	circ_grad.cy = cy - iround(vec[2]*((double)r));
	circ_grad.r = r;
	if(pts && cp) return oPolygon(pts, cp, nam);
	return oCircle(cx - r, cy - r, cx + r + 1, cy + r + 1, nam);
}

bool
HatchOut::oRectangle(int x1, int y1, int x2, int y2, char *nam)
{
	if(x1 < x2) {
		HatchDef.rec.rec.left = UseRect.left = x1;
		HatchDef.rec.rec.right = UseRect.right = x2-1;
		}
	else {
		HatchDef.rec.rec.left = UseRect.left = x2;
		HatchDef.rec.rec.right = UseRect.right = x1-1;
		}
	if(y1 < y2) {
		HatchDef.rec.rec.top = UseRect.top = y1;
		HatchDef.rec.rec.bottom = UseRect.bottom = y2-1;
		}
	else {
		HatchDef.rec.rec.top = UseRect.top = y2;
		HatchDef.rec.rec.bottom = UseRect.bottom = y1-1;
		}
	ho = HO_RECT;
	PrepareParent(false);
	return DoHatch();
}

bool
HatchOut::oPolygon(POINT *pts, int cp, char *nam)
{
	int i;
	POINT *p;

	p = (POINT*)malloc((cp+2)*(sizeof(POINT)));
	HatchDef.plg.pts = p;
	if(!p || cp < 3)return false;
	HatchDef.plg.pts[0].x = UseRect.left = UseRect.right = pts[0].x;
	HatchDef.plg.pts[0].y = UseRect.top = UseRect.bottom = pts[0].y;
	for(i = 1; i < cp; i++){
		UseRect.left = UseRect.left < pts[i].x ? UseRect.left : pts[i].x;
		UseRect.right = UseRect.right > pts[i].x ? UseRect.right : pts[i].x;
		UseRect.top = UseRect.top < pts[i].y ? UseRect.top : pts[i].y;
		UseRect.bottom = UseRect.bottom > pts[i].y ? UseRect.bottom : pts[i].y;
		p[i].x = pts[i].x;		p[i].y = pts[i].y;
		}
	i--;
	if(p[i].x != pts[0].x || p[i].y != pts[0].y) {
		i++;
		p[i].x = pts[0].x;		p[i].y = pts[0].y;
		}
	HatchDef.plg.cp = i+1;
	ho= HO_POLYGON;
	PrepareParent(false);
	return DoHatch();
}

bool
HatchOut::PrepareParent(bool Restore)
{
	if(Restore){
		if(out && ParInit)out->SetLine(&ParLineDef);
      }
	else if(out) {
		out->GetLine(&ParLineDef);
 		ParInit = out->SetLine(&MyLineDef);
		}
	return true;
}

bool
HatchOut::DoHatch()
{
	MkPolyLine(NULL, NULL);
	switch(ht){
	case FILL_NONE:											break;
	case FILL_HLINES:		Lines000();						break;
	case FILL_VLINES:		Lines090();						break;
	case FILL_HVCROSS:		Lines000();		Lines090();		break;
	case FILL_DLINEU:		Lines045();						break;
	case FILL_DLINED:		Lines315();						break;
	case FILL_DCROSS:		Lines045();		Lines315();		break;
	case FILL_STIPPLE1:		case FILL_STIPPLE2:		
	case FILL_STIPPLE3:		case FILL_STIPPLE4:
	case FILL_STIPPLE5:		Stipple(ht);					break;
	case FILL_ZIGZAG:		Zigzag();						break;
	case FILL_COMBS:		Combs();						break;
	case FILL_BRICKH:		BricksH();						break;
	case FILL_BRICKV:		BricksV();						break;
	case FILL_BRICKDU:		Bricks045();					break;
	case FILL_BRICKDD:		Bricks315();					break;
	case FILL_TEXTURE1:		Texture1();						break;
	case FILL_TEXTURE2:		Texture2();						break;
	case FILL_WAVES1:		Arcs(FILL_WAVES1);				break;
	case FILL_SCALES:		Arcs(FILL_SCALES);				break;
	case FILL_SHINGLES:		Arcs(FILL_SHINGLES);			break;
	case FILL_WAVES2:		Waves2(0);						break;
	case FILL_HERRING:		Herringbone();					break;
	case FILL_CIRCLES:		Circles();						break;
	case FILL_GRASS:		Grass();						break;
	case FILL_FOAM:			Foam();							break;
	case FILL_RECS:			Recs();							break;
	case FILL_HASH:			Hash();							break;
	case FILL_WATER:		Waves2(1);						break;
	case FILL_LIGHT3D:		CircGrad();						break;
		}

	//clean up
	if(ho == HO_POLYGON) {
		if(HatchDef.plg.pts) free(HatchDef.plg.pts);
		HatchDef.plg.pts = NULL;
		}
	ho = HO_NONE;
	MkPolyLine(NULL, out);
	return PrepareParent(true);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// collect line segments to a polyline command
#define MK_PL_MAX 1000
bool
HatchOut::MkPolyLine(POINT *p, anyOutput *o)
{
	static POINT pl[MK_PL_MAX];
	static long npt = 0;

	if(p && o) {
		if(!npt) {
			memcpy(pl, p, 2*sizeof(POINT));
			npt = 2;
			return true;
			}
		if(p[0].x != pl[npt-1].x || p[0].y != pl[npt-1].y) {
			o->oPolyline(pl, npt);
			memcpy(pl, p, 2*sizeof(POINT));
			npt = 2;
			return true;
			}
		AddToPolygon(&npt, pl, p+1);
		if(npt > (MK_PL_MAX-1)) {
			npt = 0;
			return o->oPolyline(pl, MK_PL_MAX);
			}
		return true;
		}
	if(!p && !o) {
		npt = 0;
		return true;
		}
	if(!p && o && npt) {
		o->oPolyline(pl, npt);
		npt = 0;
		return true;
      }
	return false;
}

//use Bresenham's algorithm to draw lines
//Ref: P.S. Heckbert (1990) "Digital Line Drawing", in: Graphic Gems
//   (A.S. Glassner, ed.); Academic Press, Inc.,
//   ISBN 0-12-286165-5
bool
HatchOut::HatchLine(POINT p1, POINT p2)
{
	int d, ax, ay, sx, sy, dx, dy;
	bool bPen;
	POINT tr[2];

	dx = p2.x - p1.x;
	if ( p2.x < p1.x) { 	ax = (-dx)<<1;		sx = -1;		}
	else {					ax = dx <<1;		sx = 1;		}
	dy = p2.y - p1.y;
	if (p2.y < p1.y) {	ay = (-dy)<<1;		sy = -1;		}
	else {					ay = dy<<1;			sy = 1;		}
	tr[0].x = tr[1].x = p1.x;					tr[0].y = tr[1].y = p1.y;
	if(IsInside(p1)) bPen = true;
	else bPen = false;
	if (ax > ay) {													// x dominant
		d = ay - (ax >>1);
		for ( ; ; ) {
			if(bPen) {
				if(tr[1].x == p2.x) return MkPolyLine(tr, out);
				if (d >= 0) {tr[1].y += sy;	d -= ax;}
				tr[1].x += sx;
				if(!IsInside(tr[1])) {
					bPen = false;
					MkPolyLine(tr, out);
					tr[0].x = tr[1].x;	tr[0].y = tr[1].y;
					}
				}
			else {
				if(tr[0].x == p2.x) return true;
				if (d >= 0) {tr[0].y += sy;	d -= ax;}
				tr[0].x += sx;
				if(IsInside(tr[0])){
					bPen = true;
					tr[1].x = tr[0].x;	tr[1].y = tr[0].y;
					}
				}
			d += ay;
			}
		}
	else {															// y dominant
		d = ax - (ay >>1);
		for ( ; ; ) {
			if (bPen){
				if (tr[1].y == p2.y) return MkPolyLine(tr, out);
				if (d >= 0) {tr[1].x += sx; d -= ay;}
				tr[1].y += sy;
				if(!IsInside(tr[1])) {
					bPen = false;
					MkPolyLine(tr, out);
					tr[0].x = tr[1].x;	tr[0].y = tr[1].y;
					}
				}
			else {
				if (tr[0].y == p2.y) return true;
				if (d >= 0) {tr[0].x += sx; d -= ay;}
				tr[0].y += sy;
				if(IsInside(tr[0])) {
					bPen = true;
					tr[1].x = tr[0].x;	tr[1].y = tr[0].y;
					}
				}
			d += ax;
			}
		}
}

//use circular Bresenham's algorithm to draw arcs
//Ref: C. Montani, R. Scopigno (1990) "Speres-To-Voxel Conversion", in:
//   Graphic Gems (A.S. Glassner ed.) Academic Press, Inc.; 
//   ISBN 0-12-288165-5 
bool
HatchOut::HatchArc(int ix, int iy, int r, int qad, bool start)
{
	int x, y, q, di, de, lim;
	bool bInside;
	static POINT tr[2];

	if(r < 1) r = 1;
	if(start) {
		tr[1].x = ix-r;		tr[1].y = iy;
		}
	for(q = 0; q < qad; q++) {
		x = lim = 0;	y = r;	di = 2*(1-r);
		bInside = IsInside(tr[1]);
		while (y >= lim){
			if(di < 0) {
				de = 2*di + 2*y -1;
				if(de > 0) {
					x++;	y--;	di += (2*x -2*y +2);
					}
				else {
					x++;	di += (2*x +1);
					}
				}
			else {
				de = 2*di -2*x -1;
				if(de > 0) {
					y--;	di += (-2*y +1);
					}
				else {
					x++;	y--;	di += (2*x -2*y +2);
					}
				}
			tr[0].x = tr[1].x;		tr[0].y = tr[1].y;
			switch(q) {
			case 0:	tr[1].x = ix-y;		tr[1].y = iy+x;	break;
			case 1: tr[1].x = ix+x;		tr[1].y = iy+y;	break;
			case 2: tr[1].x = ix+y;		tr[1].y = iy-x;	break;
			case 3:	tr[1].x = ix-x;		tr[1].y = iy-y;	break;
				}
			if(IsInside(tr[1])){
				if(bInside)MkPolyLine(tr, out);
				bInside = true;
				}
			else {
				if(bInside) MkPolyLine(0L, out);
				bInside = false;
				}
			}
		}
	return true;
}

bool
HatchOut::IsInside(POINT p)
{
	long tmp1, tmp2, tmp3, tmp4;

	if(out->OC_type != OC_HIMETRIC && (p.x < DeskRect.left || p.x > DeskRect.right || p.y < DeskRect.top
		|| p.y >DeskRect.bottom)) return false;
	switch(ho){
	case HO_RECT:
		if(p.x > HatchDef.rec.rec.left && p.x < HatchDef.rec.rec.right &&
			p.y > HatchDef.rec.rec.top && p.y < HatchDef.rec.rec.bottom)
			return true;
		return false;
	case HO_CIRCLE:
		tmp1 = p.x-HatchDef.cir.centre.x;
		tmp2 = p.y-HatchDef.cir.centre.y;
		if((tmp1 * tmp1 + tmp2 * tmp2) < HatchDef.cir.sr)
			return true;
		return false;
	case HO_ELLIPSE:
		tmp1 = p.x-HatchDef.ell.centre.x;
		tmp2 = p.y-HatchDef.ell.centre.y;
		tmp3 = HatchDef.ell.iy;
		tmp4 = HatchDef.ell.ix;
		if((unsigned long)(tmp1 * tmp1 * tmp3 * tmp3 + tmp2 * tmp2 * tmp4 * tmp4) < HatchDef.ell.sab)
			return true;
		return false;
	case HO_BIGELLYPSE:
		tmp1 = HatchDef.bell.fo[0].x - p.x;			tmp1 *= tmp1;
		tmp2 = HatchDef.bell.fo[0].y - p.y;			tmp2 *= tmp2;
		tmp3 = HatchDef.bell.fo[1].x - p.x;			tmp3 *= tmp3;
		tmp4 = HatchDef.bell.fo[1].y - p.y;			tmp4 *= tmp4;
		return (isqr(tmp1+tmp2)+isqr(tmp3+tmp4)) < HatchDef.bell.rsab;
	case HO_POLYGON:
		return IsInPolygon(&p, HatchDef.plg.pts, HatchDef.plg.cp);
		}
	return false;
}

void
HatchOut::Lines000()
{
	int y, yinc;
	POINT Line[2];

	if(2>(yinc = iround(ybase*.8)))yinc = 2;
	Line[0].x = UseRect.left;
	Line[1].x = UseRect.right;
	for(y = UseRect.top; y < UseRect.bottom; y += yinc) {
		Line[0].y = Line[1].y = y;
		HatchLine(Line[0], Line[1]);
		}
}

void
HatchOut::Lines090()
{
	int x, xinc;
	POINT Line[2];

	if(2>(xinc = iround(xbase*.8)))xinc = 2;
	Line[0].y = UseRect.top;
	Line[1].y = UseRect.bottom;
	for(x = UseRect.left; x < UseRect.right; x += xinc) {
		Line[0].x = Line[1].x = x;
		HatchLine(Line[0], Line[1]);
		}
}

void
HatchOut::Lines045()
{
	int x, y, y1, xinc, yinc;
	POINT Line[2];

	if(3>(xinc=iround(xbase*1.2)))xinc=3;		if(3>(yinc=iround(ybase*1.2)))yinc=3;
	Line[1].x = x = UseRect.right;
	Line[0].y = Line[1].y = y = UseRect.bottom;
	while(x > UseRect.left) {
		Line[0].x = x = x-xinc;
		if(y > UseRect.top) Line[1].y = y = y-yinc;
		else Line[1].x -= xinc;
		HatchLine(Line[0], Line[1]);
		}
	y1 = Line[0].y;
	while(y1 > UseRect.top) {
		Line[0].y = y1 = y1-yinc;
		if(y > UseRect.top) Line[1].y = y = y-yinc;
		else Line[1].x -= xinc;
		HatchLine(Line[0], Line[1]);
		}
}

void
HatchOut::Lines315()
{
	int x, y, y1, xinc, yinc;
	POINT Line[2];

	if(3>(xinc=iround(xbase*1.2)))xinc= 3;	if(3>(yinc=iround(ybase*1.2)))yinc= 3;
	Line[1].x = x = UseRect.right;
	Line[0].y = Line[1].y = y = UseRect.top;
	while (x > UseRect.left) {
		Line[0].x = x = x-xinc;
		if(y < UseRect.bottom) Line[1].y = y = y+yinc;
		else Line[1].x -= xinc;
		HatchLine(Line[0], Line[1]);
		}
	y1 = Line[0].y;
	while(y1 < UseRect.bottom) {
		Line[0].y = y1 = y1+yinc;
		if(y < UseRect.bottom) Line[1].y = y = y+yinc;
		else Line[1].x -= xinc;
		HatchLine(Line[0], Line[1]);
		}
}

void
HatchOut::Stipple(int type)
{
	int x, y, xinc, yinc, level, xspac, yspac;
	POINT Line[2];

	if(!(xinc = iround(xbase*0.48)))xinc = 1;	if(!(yinc = iround(ybase*0.48)))yinc = 1;
	if(!(xspac = iround(xbase*0.56)))xspac = 1;	if(!(yspac = iround(ybase*0.56)))yspac = 1;
	level = 0;
	for(x = UseRect.left; x < UseRect.right; x += xspac*2) {
		level &= 0x1;
		for(y = UseRect.top; y < UseRect.bottom; y += yspac*4) {
			if(type < FILL_STIPPLE3) {
				Line[0].x = x;
				Line[0].y = level? y+yinc+yspac*2 : y+yinc;
				Line[1].x = Line[0].x+xinc;		Line[1].y = Line[0].y-yinc;
				if(type == FILL_STIPPLE1)HatchLine(Line[0], Line[1]);
				Line[0].x = Line[1].x;	Line[0].y = Line[1].y;
				Line[1].x += xinc;		Line[1].y += yinc;
				if(type == FILL_STIPPLE1)HatchLine(Line[0], Line[1]);
				Line[0].x = Line[1].x;	Line[0].y = Line[1].y;
				Line[1].x -= xinc;		Line[1].y += yinc;
				HatchLine(Line[0], Line[1]);
				Line[0].x = Line[1].x;	Line[0].y = Line[1].y;
				Line[1].x -= xinc;				Line[1].y -= yinc;
				HatchLine(Line[0], Line[1]);
				}
			else if(type <= FILL_STIPPLE5) {
				Line[0].x = x;
				Line[0].y =Line[1].y = level? y+yinc+yspac*2 : y+yinc;
				Line[1].x = Line[0].x+xinc*2;
				if(type == FILL_STIPPLE3 || type == FILL_STIPPLE4)HatchLine(Line[0], Line[1]);
				Line[0].x = Line[1].x = x+xinc;	
				Line[0].y = Line[1].y -yinc;
				Line[1].y += yinc;
				if(type == FILL_STIPPLE3 || type == FILL_STIPPLE5)HatchLine(Line[0], Line[1]);
				}
			}
		level++;
		}
}

void
HatchOut::Zigzag()
{
	int yinc, ix, iy;
	POINT Line[2];

	if(3>(yinc = iround(ybase)))yinc=3;
	if(2>(iy = iround(ybase*.8)))iy=2;		if(2>(ix = iround(xbase*.8)))ix=2;
	Line[0].x = Line[1].x = UseRect.left;
	Line[0].y = Line[1].y = UseRect.top;
	while(Line[0].y < UseRect.bottom +iy) {
		while(Line[1].x < UseRect.right) {
			Line[1].y -= iy;					Line[1].x += ix;
			HatchLine(Line[0], Line[1]);
			Line[0].x = Line[1].x;			Line[1].x += ix;
			Line[0].y = Line[1].y;			Line[1].y += iy;
			HatchLine(Line[0], Line[1]);
			Line[0].x = Line[1].x;			Line[0].y = Line[1].y;
			}
		Line[0].x = Line[1].x = UseRect.left;
		Line[0].y = Line[1].y += yinc;
		}
}

void
HatchOut::Combs()
{
	int x, y, xinc, yinc;
	POINT Line[2], Next;

	if(!(yinc = iround(ybase*.4)))yinc = 1;
	if(2 >(xinc = iround(xbase*.69282))) xinc = 2;		//exact yinc *sin(60�)*2
	y = UseRect.top + yinc;
	while(y < UseRect.bottom + yinc*2) {
		Line[0].y = Line[1].y = y;
		Line[0].x = Line[1].x = UseRect.left-xinc;
		while(Line[1].x < UseRect.right && Line[1].y >= UseRect.top){
			Line[1].y -= (yinc*2);
			HatchLine(Line[0], Line[1]);
			Line[0].x = Line[1].x;			Line[1].x += xinc;
			Line[0].y = Line[1].y;			Line[1].y -= yinc;
			HatchLine(Line[0], Line[1]);
			Line[0].x = Line[1].x;			Line[0].y = Line[1].y;
			Line[1].x += xinc;				Line[1].y += yinc;
			HatchLine(Line[0], Line[1]);
			Line[1].x -= xinc;				Line[1].y -= yinc;
			}
		y += yinc*6;
		}
	Next.x = x = UseRect.left-xinc;					Next.y = y;
	while(x < UseRect.right) {
		Line[0].y = Line[1].y = Next.y;
		Line[0].x = Line[1].x = x;
		while(Line[1].x < UseRect.right&& Line[1].y >= UseRect.top){
			Line[1].y -= (yinc*2);
			HatchLine(Line[0], Line[1]);
			Line[0].x = Line[1].x;			Line[1].x += xinc;
			Line[0].y = Line[1].y;			Line[1].y -= yinc;
			HatchLine(Line[0], Line[1]);
			Line[0].x = Line[1].x;			Line[0].y = Line[1].y;
			Line[1].x += xinc;				Line[1].y += yinc;
			HatchLine(Line[0], Line[1]);
			Line[1].x -= xinc;				Line[1].y -= yinc;
			}
		x += xinc*2;
		}
}

void
HatchOut::BricksH()
{
	int i, j, y, yinc, xinc;
	POINT Line[2];

	if(4>(xinc=iround(xbase*1.6)))xinc=4;	if(2>(yinc=iround(ybase*0.8)))yinc=2;
	for(y = UseRect.top, j = 0; y < UseRect.bottom; y += yinc, j++) {
		Line[0].x = UseRect.left;
		Line[1].x = UseRect.right;
		Line[0].y = Line[1].y = y;
 		HatchLine(Line[0], Line[1]);
		Line[0].y ++;								Line[1].y += yinc;
		for (i = (j&1)?UseRect.left:UseRect.left+xinc/2;i<UseRect.right;i+=xinc){
			Line[0].x = Line[1].x = i;
			HatchLine(Line[0], Line[1]);
			}
		}
}

void
HatchOut::BricksV()
{
	int i, j, x, yinc, xinc;
	POINT Line[2];

	if(2>(xinc=iround(xbase*0.8)))xinc=2;	if(4>(yinc=iround(ybase*1.6)))yinc=4;
	for(x = UseRect.left, j= 0; x < UseRect.right; x += xinc, j++) {
		Line[0].y = UseRect.top;
		Line[1].y = UseRect.bottom;
		Line[0].x = Line[1].x = x;
		HatchLine(Line[0], Line[1]);
		Line[0].x ++;								Line[1].x += xinc;
		for (i = (j&1)?UseRect.top:UseRect.top+yinc/2;i<UseRect.bottom;i+=yinc){
			Line[0].y = Line[1].y = i;
			HatchLine(Line[0], Line[1]);
			}
		}
}

void
HatchOut::Bricks045()
{
	int xinc, yinc, bwx, bwy;
	POINT Line[2];

	if(2>(xinc=iround(xbase*.7)))xinc = 2;	if(2>(yinc=iround(ybase*.7)))yinc = 2;
	bwx = xinc *2;						bwy = yinc *2;
	Line[0].x = UseRect.left - xinc;
	Line[0].y = UseRect.top;
	while((Line[0].y < UseRect.bottom + bwy)) {
		while(Line[0].x < UseRect.right) {
			Line[1].x = Line[0].x + bwx;
			Line[1].y = Line[0].y - bwy;
			HatchLine(Line[0], Line[1]);
			Line[0].x = Line[1].x;
			Line[0].y = Line[1].y;
			Line[1].x = Line[0].x + xinc;
			Line[1].y = Line[0].y + yinc;
			HatchLine(Line[0], Line[1]);
			Line[0].y += bwy;
			}
		Line[0].y += bwy;
		Line[0].x = UseRect.left - xinc;
		}
}

void
HatchOut::Bricks315()
{
	int xinc, yinc, bwx, bwy;
	POINT Line[2];

	if(2>(xinc=iround(xbase*.7)))xinc = 2;	if(2>(yinc=iround(ybase*.7)))yinc = 2;
	bwx = xinc *2;							bwy = yinc *2;
	Line[0].x = UseRect.left - xinc;
	Line[0].y = UseRect.top -bwy;
	while((Line[0].y < UseRect.bottom)) {
		while(Line[0].x < UseRect.right) {
			Line[1].x = Line[0].x + bwx;
			Line[1].y = Line[0].y + bwy;
			HatchLine(Line[0], Line[1]);
			Line[0].x = Line[1].x;
			Line[0].y = Line[1].y;
			Line[1].x = Line[0].x + xinc;
			Line[1].y = Line[0].y - yinc;
			HatchLine(Line[0], Line[1]);
			Line[0].y -= bwy;
			}
		Line[0].y += bwy;
		Line[0].x = UseRect.left - xinc;
		}
}

void
HatchOut::Texture1()
{
	int j, xinc, yinc;
	POINT Line[2];

	if(!(xinc = iround(xbase*0.4)))xinc = 1;	if(!(yinc = iround(ybase*0.4)))yinc = 1;
	Line[0].x = UseRect.left - xinc;
	Line[0].y = UseRect.top - yinc;
	j = 0;
	while((Line[0].y < UseRect.bottom)) {
		while(Line[0].x < UseRect.right) {
			Line[1].x = Line[0].x;
			Line[1].y = Line[0].y + (yinc *2);
			HatchLine(Line[0], Line[1]);
			Line[0].x += xinc;
			Line[0].y += yinc;
			Line[1].x = Line[0].x + (xinc *2);
			Line[1].y = Line[0].y;
			HatchLine(Line[0], Line[1]);
			Line[0].x += xinc*3;
			Line[0].y -= yinc;
			}
		j++;
		Line[0].y += yinc *2;
		Line[0].x = UseRect.left - xinc;
		if(j &0x01) Line[0].x += (xinc * 2);
		}
}


void
HatchOut::Texture2()
{
	int j, xinc, yinc;
	POINT Line[2];

	if(2>(xinc=iround(xbase*.6)))xinc= 2;	if(2>(yinc=iround(ybase*.6)))yinc= 2;
	Line[0].x = UseRect.left - xinc*2;		Line[0].y = UseRect.top - yinc*2;
	j = 0;
	while((Line[0].y < UseRect.bottom)) {
		while(Line[0].x < UseRect.right) {
			Line[1].x = Line[0].x;			Line[1].y = Line[0].y + (yinc *3);
			HatchLine(Line[0], Line[1]);
			Line[0].x += xinc;				Line[1].x = Line[0].x;
			HatchLine(Line[0], Line[1]);
			Line[0].y += yinc;				Line[1].x = Line[0].x + (xinc *3);
			Line[1].y = Line[0].y;
			HatchLine(Line[0], Line[1]);
			Line[0].y += yinc;				Line[1].y = Line[0].y;
			HatchLine(Line[0], Line[1]);
			Line[0].x += xinc*3;			Line[0].y -= yinc*2;
			}
		j++;
		Line[0].y += yinc *2;		Line[0].x = UseRect.left - xinc*2;
		if(j &0x01) Line[0].x += (xinc *2);
		}
}

void
HatchOut::Arcs(int type)
{
	int i, j, level, ix, iy;

	if(type == FILL_SHINGLES) {
		iy = iround(ybase*1.6);			ix = iround(xbase*.8);
		}
	else {
		iy = iround(ybase*.8);			ix = iround(xbase*.8);
		}
	if(iy < 2) iy = 2;				if(ix < 2) ix = 2;
	UseRect.right += ix;		UseRect.top -= (iy<<1);	UseRect.bottom += (iy<<1);
	for(i = UseRect.top, level = 0; i < UseRect.bottom; i+= iy, level++) {
		if(type == FILL_WAVES1) {
			HatchArc(UseRect.left, i, ix, 0, true);
			for(j = UseRect.left; j < UseRect.right; j += ix*2) HatchArc(j, i, ix, 2, false);
			i += iy/3;
			}
		else if(type == FILL_SCALES) {
			HatchArc(UseRect.left, i, ix, 0, true);
			for(j = UseRect.left; j < UseRect.right; j += ix*2)
				HatchArc((level &1) ? j+ix:j, i, ix, 2, false);
			i++;
			}
		else {
			for(j = UseRect.left; j < UseRect.right; j += ix*2){
				HatchArc((level &1) ? j+ix:j, i-1, ix, 0, true);
				HatchArc((level &1) ? j+ix:j, i + iy/2, ix, 2, false);
				}
			i++;
			}
		}
}

void
HatchOut::Waves2(int type)					//hatch using sine waves
{
	int i, j, level, y, ix, yinc, *pts;
	POINT Line[2];
	double dtmp;

	if(3>(yinc = iround(type?ybase*.8 : ybase*1.2)))yinc = 3;	
	if(type == 0 && 14>(ix = iround(xbase*2.5)))ix = 14;
	else if(type == 1 && 7>(ix = iround(xbase*1.2)))ix = 7;
	if(!(pts = (int *)malloc(ix * sizeof(int))))return;
	for(i = 0; i < ix; i++) {
		dtmp = sin(6.283185307/((double)ix/(double)i));
		if(type == 1) dtmp /= 2.0;
		pts[i] = dtmp > 0.0 ? iround(0.3*ybase*dtmp) : iround(0.3*ybase*dtmp);
		}
	UseRect.bottom += yinc;					UseRect.right++;
	for(y = UseRect.top, level = 0; y <= UseRect.bottom; y += yinc, level++){
		Line[0].x = UseRect.left;			Line[1].x = UseRect.left+1;
		Line[0].y = y;						Line[1].y = y+pts[1];
		if(type == 0) for(j = 2; Line[0].x < UseRect.right; j++) {
			HatchLine(Line[0], Line[1]);
			Line[0].x = Line[1].x;			Line[1].x++;
			Line[0].y = Line[1].y;			Line[1].y = y + pts[j%ix];
			}
		else if(type == 1) {
			if(level & 1) {
				Line[0].x += (ix + (ix>>1));		Line[1].x = Line[0].x +1;
				}
			for(j = 2; Line[0].x < UseRect.right; j++){
				HatchLine(Line[0], Line[1]);
				Line[0].x = Line[1].x;
				Line[0].y = Line[1].y;				Line[1].y = y + pts[j%ix];
				if((j-1) % ix) Line[1].x++;
				else {
					HatchLine(Line[0], Line[1]);
					Line[1].x += (ix << 1);			Line[0].x = Line[1].x -1;
					}
				}
			}
		}
	free(pts);
}

void
HatchOut::Herringbone()
{
	int ix1, ix2, iy1, iy2, y;
	POINT Line[2];

	if(2>(ix1 = iround(xbase*.6)))ix1 = 2;		ix2 = ix1*4;
	if(2>(iy1 = iround(ybase*.6)))iy1 = 2;		iy2 = iy1*4;
	for(y = UseRect.top; y <= UseRect.bottom + iy2; y += iy1*2) {
		Line[0].x = UseRect.left-ix2;		Line[1].x = Line[0].x + ix2;
		Line[0].y = y;						Line[1].y = Line[0].y - iy2;
		do {
			HatchLine(Line[0], Line[1]);
			Line[0].x = Line[1].x - ix1;	Line[1].x = Line[0].x + ix2;
			Line[0].y = Line[1].y - iy1;	Line[1].y = Line[0].y + iy2;
			HatchLine(Line[0], Line[1]);
			Line[0].x = Line[1].x - ix1;	Line[1].x = Line[0].x + ix2;
			Line[0].y = Line[1].y + iy1;	Line[1].y = Line[0].y - iy2;
			} while(Line[0].x <= UseRect.right);
		}
}

void
HatchOut::Circles()
{
	int x, y, r, level, xspac, yspac;

	if(2 > (xspac = iround(+xbase*.8))) xspac = 2;
	if(4 > (yspac = iround(ybase*1.38564))) yspac = 4;
	if(1 > (r = iround(xbase*.4)))r = 1;
	level = 0;
	UseRect.bottom += yspac;		UseRect.right += r;
	for(x = UseRect.left; x < UseRect.right; x += xspac) {
		level &= 0x1;
		for(y = UseRect.top; y < UseRect.bottom; y += yspac*2) {
			HatchArc(x, level? y+yspac : y, r, 4, true);
			}
		level++;
		}
}

void
HatchOut::Grass()
{
	int i, count, dh, dw;
	double xsize, ysize;
	long idum = -1;
	POINT pts[2];

	xsize = xbase *2.0;		ysize = ybase*2.0;
	if(xsize < 4.0) xsize = 4.0;	if(ysize < 4.0) ysize = 4.0;
	IncrementMinMaxRect(&UseRect, (int)xsize);
	count = (UseRect.right -UseRect.left)*(UseRect.bottom-UseRect.top);
	i = (int)(xsize*ysize*0.15);		if(i) count /= i;
	dh = UseRect.bottom-UseRect.top;	dw = UseRect.right-UseRect.left;
	for(i = 0; i < count; i++) {
		pts[0].x = UseRect.left+(int)(dw*ran2(&idum));
		pts[0].y = UseRect.top+(int)(dh*ran2(&idum));
		pts[1].x = pts[0].x + (int)(ran2(&idum)*xsize);
		pts[1].y = pts[0].y + (int)(ran2(&idum)*ysize);
		if(pts[0].x != pts[1].x || pts[0].y != pts[1].y) HatchLine(pts[0], pts[1]);
		}
}

void
HatchOut::Foam()
{
	int i, count, dh, dw;
	double xsize;
	long idum = -1;

	xsize = xbase *0.9;
	if(xsize < 2.0) xsize = 2.0;
	IncrementMinMaxRect(&UseRect, (int)xsize);
	count = (UseRect.right -UseRect.left)*(UseRect.bottom-UseRect.top);
	count /= (int)(xsize*xsize);
	dh = UseRect.bottom-UseRect.top;	dw = UseRect.right-UseRect.left;
	for(i = 0; i < count; i++) {
		HatchArc(UseRect.left+(int)(dw*ran2(&idum)), UseRect.top+(int)(dh*ran2(&idum)),
			(int)(ran2(&idum)*ran2(&idum)*xsize +xsize*.2), 4, true);
		}
}

void
HatchOut::Recs()
{
	int i, count, dh, dw;
	double xsize, ysize;
	long idum = -1;
	POINT Line[5];

	xsize = xbase *2.8;	ysize = ybase *2.8;
	if(xsize < 4.0) xsize = 4.0;		if(ysize < 4.0) ysize = 4.0;
	IncrementMinMaxRect(&UseRect, (int)xsize);
	count = (UseRect.right -UseRect.left)*(UseRect.bottom-UseRect.top);
	i = (int)(floor(xsize*ysize*.4));	if(i) count /= i;
	dh = UseRect.bottom-UseRect.top;	dw = UseRect.right-UseRect.left;
	for(i = 0; i < count; i++) {
		Line[0].x = Line[3].x = Line[4].x = UseRect.left+(int)(dw*ran2(&idum));
		Line[0].y = Line[1].y = Line[4].y = UseRect.top+(int)(dh*ran2(&idum));
		Line[1].x = Line[2].x = Line[0].x + (int)(ran2(&idum)*xsize +xsize*.2);
		Line[2].y = Line[3].y = Line[0].y + (int)(ran2(&idum)*ysize +ysize*.2);
		HatchLine(Line[0], Line[1]);			HatchLine(Line[1], Line[2]);
		HatchLine(Line[2], Line[3]);			HatchLine(Line[3], Line[4]);
		}
}

void
HatchOut::Hash()
{
	int i, dh, dw, cx, cy, xinc, yinc;
	double xsize, ysize, mix, miy;
	long idum = -1;
	POINT Line[5];

	xsize = xbase * 0.9;						ysize = ybase * 0.9;
	xinc = iround(xsize * 3.3);					yinc = iround(ysize * 2.2);
	mix = xbase *.5;							miy = ybase *.5;
	dw = iround(xbase > 5 ? xbase/2.0 : 2.0);	dh = iround(ybase > 5 ? xbase/2.0 : 2.0);
	if(xsize < 2.0) xsize = 2.0;		if(ysize < 2.0) ysize = 2.0;
	IncrementMinMaxRect(&UseRect, (int)xsize);
	for(i = 0, cy = UseRect.top; cy < UseRect.bottom; i++, cy += yinc) {
		for(cx = (i & 1) ? UseRect.left:UseRect.left+xinc/2; cx < UseRect.right; cx += xinc) { 
			Line[0].x = Line[1].x = cx;
			Line[0].y = cy - iround(ran2(&idum) * ysize + miy);
			Line[1].y = cy + iround(ran2(&idum) * ysize + miy);
			HatchLine(Line[0], Line[1]);
			Line[0].x = Line[1].x = cx + dw;
			Line[0].y = cy - iround(ran2(&idum) * ysize + miy);
			Line[1].y = cy + iround(ran2(&idum) * ysize + miy);
			HatchLine(Line[0], Line[1]);
			Line[0].y = Line[1].y = cy;
			Line[0].x = cx - iround(ran2(&idum) * xsize + mix);
			Line[1].x = cx + iround(ran2(&idum) * xsize + mix);
			HatchLine(Line[0], Line[1]);
			Line[0].y = Line[1].y = cy + dh;
			Line[0].x = cx - iround(ran2(&idum) * xsize + mix);
			Line[1].x = cx + iround(ran2(&idum) * xsize + mix);
			HatchLine(Line[0], Line[1]);
			}
		}
}

void
HatchOut::CircGrad()
{
	int i;
	double f;
	LineDEF ld = {0.0, 1.0, 0x0, 0x0};

	for(i = 1; i < circ_grad.r; i++) {
		f = (((double)i)/((double)circ_grad.r));
		f = f*f;
		ld.color = IpolCol(dFillCol, dFillCol2, f);
		out->SetLine(&ld);
		HatchArc(circ_grad.cx, circ_grad.cy, i, 4, true);
		}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// export to *.tif file (tag image file)
// This code is based on information from the following book
// G. Born, 'Referenzhandbuch Dateiformate', 
//     Addison-Wesley ISBN 3-89319-815-6
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class ExportTif:public anyOutput {
public:
	ExportTif(GraphObj *g, char *FileName, DWORD flags, double res, double wi, double he);
	~ExportTif();
	bool StartPage();
	bool EndPage();

private:
	int w, h;
	anyOutput *bmo;
	char *name;
	int oFile;
};

ExportTif::ExportTif(GraphObj *g, char *FileName, DWORD flags, double res, double wi, double he)
{
	hres = vres = res;
	name = 0L;							bmo = 0L;
	DeskRect.left = DeskRect.top = 0;
	DeskRect.right = DeskRect.bottom = 0x4fffffff;
	dFillCol = 0xffffffffL;
	if(g && FileName) {
		w = un2ix(wi);		h = un2iy(he);
		if(bmo = NewBitmapClass(w, h, hres, vres)){
			bmo->VPorg.fy = -co2fiy(g->GetSize(SIZE_GRECT_TOP));
			bmo->VPorg.fx = -co2fix(g->GetSize(SIZE_GRECT_LEFT));
			bmo->Erase(0x00ffffffL);
			g->DoPlot(bmo);
			}
		name = (char*)memdup(FileName, (int)strlen(FileName)+1, 0);
		}
	oFile = 0;
}

ExportTif::~ExportTif()
{
	if(name) free(name);
	if(bmo) DelBitmapClass(bmo);
}

bool
ExportTif::StartPage()
{
	unsigned char header[] = {
		0x49, 0x49,							//intel byte order
		0x2a, 0x00,							//version 4.2
		0x08, 0x00, 0x00, 0x00,				//the image file directory just follows
		0x0f, 0x00,							//number of tags in IFD
		0xfe, 0x00, 0x04, 0x00, 0x01, 0x00, //new subfile tag
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x01, 0x04, 0x00, 0x01, 0x00,	//image width tag (pixels)
		0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
		0x01, 0x01, 0x04, 0x00, 0x01, 0x00,	//image height tag (pixels)
		0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
		0x02, 0x01, 0x03, 0x00, 0x01, 0x00,	//BitsPerSample tag
		0x00, 0x00, 0x08, 0x00, 0x00, 0x00, //   ...8 bits per sample
		0x03, 0x01, 0x03, 0x00, 0x01, 0x00, //compression tag
		0x00, 0x00, 0x01, 0x00, 0x00, 0x00,	//   ...no compression
		0x06, 0x01, 0x03, 0x00, 0x01, 0x00, //photometric interpretation
		0x00, 0x00, 0x02, 0x00, 0x00, 0x00,	//   ...its RGB
		0x11, 0x01, 0x04, 0x00, 0x01, 0x00,	//strip (image) offset
		0x00, 0x00, 0x82, 0x00, 0x00, 0x00,	//   ... number of strips and offset to data
		0x12, 0x01, 0x03, 0x00, 0x01, 0x00,	//orientation tag
		0x00, 0x00, 0x01, 0x00, 0x00, 0x00,	//   ...its top/left
		0x15, 0x01, 0x03, 0x00, 0x01, 0x00,	//samples per pixel
		0x00, 0x00, 0x03, 0x00, 0x00, 0x00,	//   ... 3 samples (colors) for RGB
		0x17, 0x01, 0x04, 0x00, 0x01, 0x00,	//strip byte count
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x1a, 0x01, 0x05, 0x00, 0x01, 0x00,	//horizontal resolution
		0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
		0x1b, 0x01, 0x05, 0x00, 0x01, 0x00,	//vertical resolution
		0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
		0x1c, 0x01, 0x03, 0x00, 0x01, 0x00,	//planar configuration
		0x00, 0x00, 0x01, 0x00, 0x01, 0x00,	//   ... one layer
		0x28, 0x01, 0x03, 0x00, 0x01, 0x00,	//resolution units
		0x00, 0x00, 0x02, 0x00, 0x01, 0x00,	//   ... dots per inch
		0x31, 0x01, 0x02, 0x00, 0x01, 0x00,	//Software
		0x00, 0x00, 0x00, 0x00, 0x01, 0x00	//   ... RLPlot ...
		};
	DWORD res_info[4] = {iround(hres), 0x01, iround(vres), 0x01};
	char prog_name[20];
	int cb;

	if(name && bmo) {
#ifdef USE_WIN_SECURE
		if(_sopen_s(&oFile, name, O_RDWR | O_BINARY | O_CREAT | O_TRUNC, 
			0x40, S_IWRITE) || oFile < 0){
			ErrorBox("Could not open output file");
			return false;
			}
#else
		if(-1 ==(oFile = open(name, O_RDWR | O_BINARY | O_CREAT | O_TRUNC,
			S_IWRITE | S_IREAD))){
			ErrorBox("Could not open output file");
			return false;
			}
#endif
		*((int*)(header+30))= w;		*((int*)(header+42))= h;
		*((DWORD*)(header+126)) = (DWORD)(w * h * 3);
		*((int*)(header+90)) = sizeof(header) + sizeof(res_info) + 20;
		*((int*)(header+138)) = sizeof(header);
		*((int*)(header+150)) = sizeof(header)+8;
		*((int*)(header+186)) = sizeof(header)+16;
		cb = rlp_strcpy(prog_name, 20, "RLPlot ");
		rlp_strcpy(prog_name+cb, 20-cb, SZ_VERSION);
#ifdef USE_WIN_SECURE
		_write(oFile, &header, sizeof(header));
		_write(oFile, &res_info, sizeof(res_info));
		_write(oFile, &prog_name, 20);

#else
		write(oFile, &header, sizeof(header));
		write(oFile, &res_info, sizeof(res_info));
		write(oFile, &prog_name, 20);
#endif
		return true;
		}
	return false;
}

bool
ExportTif::EndPage()
{
	int i, j, c;
	DWORD pix;
	unsigned char *pix_data, *cpix = (unsigned char*)&pix;

	if(bmo && (pix_data = (unsigned char*)malloc(3072))){
		for(i = c = 0; i < h; i++) {
			for(j = 0; j < w; j++) {
				bmo->oGetPix(j, i, &pix);			pix_data[c++] = cpix[0];
				pix_data[c++] = cpix[1];			pix_data[c++] = cpix[2];
				if(c >= 3072) {
#ifdef USE_WIN_SECURE
					_write(oFile, pix_data, 3072);
#else
					write(oFile, pix_data, 3072);
#endif
					c = 0;
					}
				}
			}
#ifdef USE_WIN_SECURE
		_write(oFile, pix_data, c);
#else
		write(oFile, pix_data, c);
#endif
		free(pix_data);
		}
#ifdef USE_WIN_SECURE
	_close(oFile);
#else
	close(oFile);
#endif
	oFile = -1;
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Entry point to export graph to tag image file
void DoExportTif(GraphObj *g, char *FileName, DWORD flags)
{
	ExportTif *ex;
	double res, width, height;

	if(!g || !FileName) return;
	res = 98.0;
	width = g->GetSize(SIZE_GRECT_RIGHT) - g->GetSize(SIZE_GRECT_LEFT);
	height = g->GetSize(SIZE_GRECT_BOTTOM) - g->GetSize(SIZE_GRECT_TOP);
	if(GetBitmapRes(&res, &width, &height, "Export Tag Image File")){
		ex = new ExportTif(g, FileName, flags, res, width, height);
		if(ex->StartPage())	ex->EndPage();
		delete(ex);
		}
}	
