//Axes.cpp, Copyright 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
//
// This module contains most of the axis-object and related stuff 
//   like ticks and grid lines.
#include "rlplot.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

extern char TmpTxt[];
extern Default defs;
extern GraphObj *CurrGO, *TrackGO;			//Selected Graphic Objects
extern Axis **CurrAxes;
extern UndoObj Undo;


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// define an object for each grid line
GridLine::GridLine(GraphObj *par, DataObj *d, int which, DWORD df):
	GraphObj(par, d)
{
	FileIO(INIT_VARS);
	type = which;
	flags = df;
	if(flags & AXIS_RADIAL) type |= DL_CIRCULAR;	//default to circular grid
	Id = GO_GRIDLINE;
	if(parent) parent->Command(CMD_GET_GRIDLINE, &LineDef, 0L);
	bModified = false;
}

GridLine::GridLine(int src):GraphObj(0L, 0L)
{
	FileIO(INIT_VARS);
	if(src == FILE_READ) {
		FileIO(FILE_READ);
		}
	bModified = false;
}

GridLine::~GridLine()
{
	int i;

	if(bModified) Undo.InvalidGO(this);
	if(cpts) free(cpts);		cpts = 0L;
	if(gl1) free(gl1);			gl1 = 0L;
	if(gl2) free(gl2);			gl2 = 0L;
	if(gl3) free(gl3);			gl3 = 0L;
	if(ls) {
		for(i = 0; i < 3; i++) if(ls[i]) delete(ls[i]);
		free(ls);				ls = 0L;
		}
	if(mo) DelBitmapClass(mo);	mo = 0L;
}

void
GridLine::DoPlot(anyOutput *o)
{
	int tmp, ix, iy, ir;
	AxisDEF *axdef;

	if(!parent || !o) return;
	o->SetLine(&LineDef);
	if(mo) DelBitmapClass(mo);	mo = 0L;
	if(!type) type = DL_LEFT | DL_BOTTOM;
	if(type & DL_CIRCULAR) {
		axdef = (AxisDEF*)((Axis*)(parent->parent))->GetAxis();
		ix = o->co2ix(axdef->Center.fx + parent->GetSize(SIZE_GRECT_LEFT));
		iy = o->co2iy(axdef->Center.fy + parent->GetSize(SIZE_GRECT_TOP));
		ir = abs((int)(parent->GetSize(SIZE_YBASE))-iy);
		ncpts = 0;
		cpts = MakeArc(ix, iy, ir, 0x0f, &ncpts);
		SetMinMaxRect(&rDims, ix-ir, iy-ir, ix+ir, iy+ir);
		IncrementMinMaxRect(&rDims, 6 + o->un2ix(LineDef.width));
		o->oPolyline(cpts, (int)ncpts);
		return;
		}
	if(parent && parent->Id == GO_TICK) {
		pts[0].x = pts[1].x = pts[2].x = pts[3].x = (long)parent->GetSize(SIZE_XBASE);
		pts[0].y = pts[1].y = pts[2].y = pts[3].y = (long)parent->GetSize(SIZE_YBASE);
		if(type & DL_LEFT) {
			tmp = o->fx2ix(parent->GetSize(SIZE_BOUNDS_LEFT));
			if(tmp < pts[0].x) pts[0].x = tmp;
			if(tmp > pts[1].x) pts[1].x = tmp;
			}
		if(type & DL_RIGHT) {
			tmp = o->fx2ix(parent->GetSize(SIZE_BOUNDS_RIGHT));
			if(tmp < pts[0].x) pts[0].x = tmp;
			if(tmp > pts[1].x) pts[1].x = tmp;
			}
		if(type & DL_YAXIS) {
			tmp = iround(parent->GetSize(SIZE_YAXISX));
			if(tmp < pts[0].x) pts[0].x = tmp;
			if(tmp > pts[1].x) pts[1].x = tmp;
			}
		if(type & DL_TOP) {
			tmp = o->fy2iy(parent->GetSize(SIZE_BOUNDS_TOP));
			if(tmp < pts[2].y) pts[2].y = tmp;
			if(tmp > pts[3].y) pts[3].y = tmp;
			}
		if(type & DL_BOTTOM) {
			tmp = o->fy2iy(parent->GetSize(SIZE_BOUNDS_BOTTOM));
			if(tmp < pts[2].y) pts[2].y = tmp;
			if(tmp > pts[3].y) pts[3].y = tmp;
			}
		if(type & DL_XAXIS) {
			tmp = iround(parent->GetSize(SIZE_XAXISY));
			if(tmp < pts[2].y) pts[2].y = tmp;
			if(tmp > pts[3].y) pts[3].y = tmp;
			}
		if(pts[0].x != pts[1].x || pts[0].y != pts[1].y){
			o->oPolyline(pts, 2);
			SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
			}
		if(pts[2].x != pts[3].x || pts[2].y != pts[3].y){
			o->oPolyline(pts+2, 2);
			SetMinMaxRect(&rDims, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
			}
		IncrementMinMaxRect(&rDims, 6 + o->un2ix(LineDef.width));
		}
}

void
GridLine::DoMark(anyOutput *o, bool mark)
{
	if(mark){
		if(mo) DelBitmapClass(mo);					mo = 0L;
		memcpy(&mrc, &rDims, sizeof(RECT));
		IncrementMinMaxRect(&mrc, 6 + o->un2ix(LineDef.width));
		mo = GetRectBitmap(&mrc, o);
		if(type & DL_CIRCULAR) {
			InvertLine(cpts, ncpts, &LineDef, &rDims, o, mark);
			}
		else {
			if(pts[0].x != pts[1].x || pts[0].y != pts[1].y)InvertLine(pts, 2, &LineDef, &rDims, o, mark);
			if(pts[2].x != pts[3].x || pts[2].y != pts[3].y)InvertLine(pts+2, 2, &LineDef, &rDims, o, mark);
			}
		}
	else RestoreRectBitmap(&mo, &mrc, o);
}

bool
GridLine::Command(int cmd, void *tmpl, anyOutput *o)
{
	MouseEvent *mev;
	POINT p1;

	switch(cmd){
	case CMD_SET_DATAOBJ:
		Id = GO_GRIDLINE;
		return true;
	case CMD_SCALE:
		LineDef.patlength *= ((scaleINFO*)tmpl)->sy.fy;
		LineDef.width *= ((scaleINFO*)tmpl)->sy.fy;
		return true;
	case CMD_SETSCROLL:
	case CMD_SET_GO3D:
	case CMD_REDRAW:
		if(parent) return parent->Command(cmd, tmpl, o);
		return false;
	case CMD_SET_GRIDLINE:
		if(tmpl) memcpy(&LineDef, tmpl, sizeof(LineDEF));
		return true;
	case CMD_SET_GRIDTYPE:
		if(tmpl)type = *((int*)tmpl);
		return true;
	case CMD_MOUSE_EVENT:
		mev = (MouseEvent *) tmpl;
		switch (mev->Action) {
		case MOUSE_LBUP:
			if(IsInRect(&rDims, p1.x = mev->x, p1.y = mev->y)) {
				if(cpts && (type & DL_CIRCULAR) && IsCloseToPL(p1, cpts, ncpts)) {
					o->ShowMark(CurrGO = this, MRK_GODRAW);
					return true;
					}
				else if(!(type & DL_CIRCULAR)){
					o->ShowMark(CurrGO = this, MRK_GODRAW);
					return true;
					}
				}
			break;
			}
		return false;
		}
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Gridline for axes and plots in 3D space
GridLine3D::GridLine3D(GraphObj *par, DataObj *d, int which, DWORD df):
	GridLine(par, d, which, df)
{
	Id = GO_GRIDLINE3D;
}

GridLine3D::GridLine3D(int src):GridLine(src)
{
}

GridLine3D::~GridLine3D()
{
	int i;

	if(cpts) free(cpts);		cpts = 0L;
	if(gl1) free(gl1);			gl1 = 0L;
	if(gl2) free(gl2);			gl2 = 0L;
	if(gl3) free(gl3);			gl3 = 0L;
	if(ls) {
		for(i = 0; i < 6; i++) if(ls[i]) delete(ls[i]);
		free(ls);				ls = 0L;
		}
	if(mo) DelBitmapClass(mo);	mo = 0L;
}

void
GridLine3D::DoMark(anyOutput *o, bool mark)
{
	int i;
	POINT3D *gl;

	if(mark){
		if(mo) DelBitmapClass(mo);					mo = 0L;
		if(ls){
			memcpy(&mrc, &rDims, sizeof(RECT));
			IncrementMinMaxRect(&mrc, 6 + o->un2ix(LineDef.width));
			mo = GetRectBitmap(&mrc, o);
			for(i = 0; i < 6; i++) if(ls[i]) { 
				switch(i) {
				case 0:				case 1:
					gl = gl1;		break;
				case 2:		case 3:
					gl = gl2;		break;
				case 4:		case 5:
					gl = gl3;		break;
					}
				if(gl) {
					if(gl[0].x && gl[0].y && gl[1].x && gl[1].y) {
						pts[0].x = gl[0].x;		pts[1].x = gl[1].x;
						pts[0].y = gl[0].y;		pts[1].y = gl[1].y;
						InvertLine(pts, 2, &LineDef, &mrc, o, mark);
						}
					if(gl[2].x && gl[2].y && gl[3].x && gl[3].y) {
						pts[0].x = gl[2].x;		pts[1].x = gl[3].x;
						pts[0].y = gl[2].y;		pts[1].y = gl[3].y;
						InvertLine(pts, 2, &LineDef, &mrc, o, mark);
						}
					}
				}
			}
		}
	else if(mo)	RestoreRectBitmap(&mo, &mrc, o);
}


void
GridLine3D:: DoPlot(anyOutput *o)
{
	fPOINT3D p1, p2, pn;
	int i;

	if(!parent || !o) return;
	if(mo) DelBitmapClass(mo);	mo = 0L;
	if(!gl1) gl1 = (POINT3D*)calloc(4, sizeof(POINT3D));
	if(!gl2) gl2 = (POINT3D*)calloc(4, sizeof(POINT3D));
	if(!gl3) gl3 = (POINT3D*)calloc(4, sizeof(POINT3D));
	if(!ls) ls = (line_segment**)calloc(6, sizeof(line_segment*));
	if(!(o->ActualSize(&rDims)))return;
	Swap(rDims.left, rDims.right);	Swap(rDims.top, rDims.bottom);
	if(gl1 && gl2 && gl3 && ls) {
		for(i = 0; i < 6; i++) if(ls[i]) {
			delete(ls[i]);		ls[i] = 0L;
			}
		if(type & 0x01) {
			pn.fx = parent->GetSize(SIZE_BOUNDS_XMIN);	pn.fy = parent->GetSize(SIZE_BOUNDS_YMIN);
			pn.fz = parent->GetSize(SIZE_MINE);			o->fvec2ivec(&pn, &p1);
			pn.fx = parent->GetSize(SIZE_BOUNDS_XMAX);	o->fvec2ivec(&pn, &p2);
			gl1[0].x = iround(p1.fx);					gl1[0].y = iround(p1.fy);
			gl1[1].x = iround(p2.fx);					gl1[1].y = iround(p2.fy);
			gl1[0].z = iround(p1.fz);					gl1[1].z = iround(p2.fz);
			if(ls[0] = new line_segment(this, data, &LineDef, &gl1[0], &gl1[1]))
				ls[0]->DoPlot(o);
			UpdateMinMaxRect(&rDims, gl1[0].x, gl1[0].y);
			UpdateMinMaxRect(&rDims, gl1[1].x, gl1[1].y);
			}
		if(type & 0x02) {
			pn.fx = parent->GetSize(SIZE_BOUNDS_XMIN);	pn.fy = parent->GetSize(SIZE_BOUNDS_YMIN);
			pn.fz = parent->GetSize(SIZE_MINE);			o->fvec2ivec(&pn, &p1);
			pn.fy = parent->GetSize(SIZE_BOUNDS_YMAX);	o->fvec2ivec(&pn, &p2);
			gl1[2].x = iround(p1.fx);					gl1[2].y = iround(p1.fy);
			gl1[3].x = iround(p2.fx);					gl1[3].y = iround(p2.fy);
			gl1[2].z = iround(p1.fz);					gl1[3].z = iround(p2.fz);
			if(ls[1] = new line_segment(this, data, &LineDef, &gl1[2], &gl1[3]))
				ls[1]->DoPlot(o);
			UpdateMinMaxRect(&rDims, gl1[2].x, gl1[2].y);
			UpdateMinMaxRect(&rDims, gl1[3].x, gl1[3].y);
			}
		if(type & 0x04) {
			pn.fx = parent->GetSize(SIZE_BOUNDS_XMIN);	pn.fy = parent->GetSize(SIZE_MINE);
			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMIN);	o->fvec2ivec(&pn, &p1);
			pn.fx = parent->GetSize(SIZE_BOUNDS_XMAX);	o->fvec2ivec(&pn, &p2);
			gl2[0].x = iround(p1.fx);					gl2[0].y = iround(p1.fy);
			gl2[1].x = iround(p2.fx);					gl2[1].y = iround(p2.fy);
			gl2[0].z = iround(p1.fz);					gl2[1].z = iround(p2.fz);
			if(ls[2] = new line_segment(this, data, &LineDef, &gl2[0], &gl2[1]))
				ls[2]->DoPlot(o);
			UpdateMinMaxRect(&rDims, gl2[0].x, gl2[0].y);
			UpdateMinMaxRect(&rDims, gl2[1].x, gl2[1].y);
			}
		if(type & 0x08) {
			pn.fx = parent->GetSize(SIZE_BOUNDS_XMIN);	pn.fy = parent->GetSize(SIZE_MINE);
			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMIN);	o->fvec2ivec(&pn, &p1);
			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMAX);	o->fvec2ivec(&pn, &p2);
			gl2[2].x = iround(p1.fx);					gl2[2].y = iround(p1.fy);
			gl2[3].x = iround(p2.fx);					gl2[3].y = iround(p2.fy);
			gl2[2].z = iround(p1.fz);					gl2[3].z = iround(p2.fz);
			if(ls[3] = new line_segment(this, data, &LineDef, &gl2[2], &gl2[3]))
				ls[3]->DoPlot(o);
			UpdateMinMaxRect(&rDims, gl2[2].x, gl2[2].y);
			UpdateMinMaxRect(&rDims, gl2[3].x, gl2[3].y);
			}
		if(type & 0x10) {
			pn.fx = parent->GetSize(SIZE_MINE);			pn.fy = parent->GetSize(SIZE_BOUNDS_YMIN);
			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMIN);	o->fvec2ivec(&pn, &p1);
			pn.fy = parent->GetSize(SIZE_BOUNDS_YMAX);	o->fvec2ivec(&pn, &p2);
			gl3[0].x = iround(p1.fx);					gl3[0].y = iround(p1.fy);
			gl3[1].x = iround(p2.fx);					gl3[1].y = iround(p2.fy);
			gl3[0].z = iround(p1.fz);					gl3[1].z = iround(p2.fz);
			if(ls[4] = new line_segment(this, data, &LineDef, &gl3[0], &gl3[1]))
				ls[4]->DoPlot(o);
			UpdateMinMaxRect(&rDims, gl3[0].x, gl3[0].y);
			UpdateMinMaxRect(&rDims, gl3[1].x, gl3[1].y);
			}
		if(type & 0x20) {
			pn.fx = parent->GetSize(SIZE_MINE);			pn.fy = parent->GetSize(SIZE_BOUNDS_YMIN);
			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMIN);	o->fvec2ivec(&pn, &p1);
			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMAX);	o->fvec2ivec(&pn, &p2);
			gl3[2].x = iround(p1.fx);					gl3[2].y = iround(p1.fy);
			gl3[3].x = iround(p2.fx);					gl3[3].y = iround(p2.fy);
			gl3[2].z = iround(p1.fz);					gl3[3].z = iround(p2.fz);
			if(ls[5] = new line_segment(this, data, &LineDef, &gl3[2], &gl3[3]))
				ls[5]->DoPlot(o);
			UpdateMinMaxRect(&rDims, gl3[2].x, gl3[2].y);
			UpdateMinMaxRect(&rDims, gl3[3].x, gl3[3].y);
			}
		}
	IncrementMinMaxRect(&rDims, 6 + o->un2ix(LineDef.width));
}

bool
GridLine3D::Command(int cmd, void *tmpl, anyOutput *o)
{
	int i;

	switch(cmd) {
	case CMD_SET_DATAOBJ:
		Id = GO_GRIDLINE3D;
		return true;
	case CMD_MOUSE_EVENT:
		if(tmpl && ls) switch (((MouseEvent *)tmpl)->Action) {
		case MOUSE_LBUP:
			for(i = 0; i < 6; i++) if(ls[i] && 
				ls[i]->ObjThere(((MouseEvent *)tmpl)->x, ((MouseEvent *)tmpl)->y)) {
				o->ShowMark(this, MRK_GODRAW);
				return true;
				}
			}
		break;
	default:
		return GridLine::Command(cmd, tmpl, o);
		}
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Radial Gridline for polar plots: spokes for the plot
GridRadial::GridRadial(GraphObj *par, DataObj *d, int which, DWORD df):
	GridLine(par, d, which, df)
{
	Id = GO_GRIDRADIAL;
}

GridRadial::GridRadial(int src):GridLine(src)
{
}

GridRadial::~GridRadial()
{
	if(mo) DelBitmapClass(mo);	mo = 0L;
}

void
GridRadial::DoPlot(anyOutput *o)
{
	AxisDEF *axdef;

	if(!parent || !o) return;
	if(mo) DelBitmapClass(mo);	mo = 0L;
	o->SetLine(&LineDef);
	if(parent->Id == GO_TICK && parent->parent && parent->parent->Id == GO_AXIS) {
		axdef = (AxisDEF*)((Axis*)(parent->parent))->GetAxis();
		pts[0].x = iround(parent->GetSize(SIZE_XBASE));
		pts[0].y = iround(parent->GetSize(SIZE_YBASE));
		pts[1].x = o->co2ix(axdef->Center.fx + parent->GetSize(SIZE_GRECT_LEFT));
		pts[1].y = o->co2iy(axdef->Center.fy + parent->GetSize(SIZE_GRECT_TOP));
		SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
		IncrementMinMaxRect(&rDims, 6 + o->un2ix(LineDef.width));
		o->oPolyline(pts, 2);
		}
}

void
GridRadial::DoMark(anyOutput *o, bool mark)
{
	if(mark) {
		if(mo) DelBitmapClass(mo);					mo = 0L;
		memcpy(&mrc, &rDims, sizeof(RECT));
		IncrementMinMaxRect(&mrc, 6 + o->un2ix(LineDef.width));
		mo = GetRectBitmap(&mrc, o);
		InvertLine(pts, 2, &LineDef, &rDims, o, mark);
		}
	else if(mo)	RestoreRectBitmap(&mo, &mrc, o);
}

bool
GridRadial::Command(int cmd, void *tmpl, anyOutput *o)
{
	POINT p1;

	switch(cmd) {
	case CMD_SET_DATAOBJ:
		Id = GO_GRIDRADIAL;
		return true;
	case CMD_MOUSE_EVENT:
		if(tmpl) switch (((MouseEvent *)tmpl)->Action) {
		case MOUSE_LBUP:
			if(IsInRect(&rDims, p1.x=((MouseEvent *)tmpl)->x, p1.y=((MouseEvent *)tmpl)->y)){
				if(IsCloseToPL(p1, pts, 2)) {
					o->ShowMark(this, MRK_GODRAW);
					return true;
					}
				}
			}
		break;
	default:
		return GridLine::Command(cmd, tmpl, o);
		}
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Each axis tick is a graphic object managing tick labels and grid lines
Tick::Tick(GraphObj *par, DataObj *d, double val, DWORD Flags):GraphObj(par, d)
{
	FileIO(INIT_VARS);
	value = val;			flags = Flags;
	Id = GO_TICK;			bModified = false;
}

Tick::Tick(int src):GraphObj(0L, 0L)
{
	FileIO(INIT_VARS);
	if(src == FILE_READ) {
		FileIO(FILE_READ);
		}
	bModified = false;
}

Tick::~Tick()
{
	int i;

	if(Polygons) {
		for(i = 0; i < numPG; i++) if(Polygons[i]) DeleteGO(Polygons[i]);
		free(Polygons);			Polygons = 0L;	numPG = 0;
		}
	Command(CMD_FLUSH, 0L, 0L);
	if(mo) DelBitmapClass(mo);		mo = 0L;
	if(bModified) Undo.InvalidGO(this);
	if(seg) free(seg);			seg = 0L;
}
	
double
Tick::GetSize(int select)
{
	switch(select){
	case SIZE_LB_XPOS:	return lbx;
	case SIZE_XBASE:	return fix;
	case SIZE_LB_YPOS:	return lby;
	case SIZE_YBASE:	return fiy;
	case SIZE_ZBASE:	return fiz;
	case SIZE_LB_XDIST:
		if(parent && parent->Id == GO_AXIS) return parent->GetSize(SIZE_TLB_XDIST);
		return 0.0f;
	case SIZE_LB_YDIST:
		if(parent && parent->Id == GO_AXIS) return parent->GetSize(SIZE_TLB_YDIST);
		return 0.0f;
	case SIZE_MINE:		return value;
	default:
		if(parent) return parent->GetSize(select);
		}
	return 0.0;
}

bool
Tick::SetSize(int select, double value)
{
	switch(select & 0xfff) {
	case SIZE_AXIS_TICKS:
		size = value;
		break;
	case SIZE_LB_XDIST:		case SIZE_LB_YDIST:
		if(label)return label->SetSize(select, value);
		break;
	case SIZE_TICK_ANGLE:
		angle = value;
		}
	return false;
}

bool
Tick::SetColor(int select, DWORD col)
{
	switch(select & 0xfff) {
	case COL_AXIS:
		if(label)label->SetColor((select & UNDO_STORESET) ? 
			COL_TEXT | UNDO_STORESET : COL_TEXT, col);
		return true;
	case COL_BG:
		if(label) return label->SetColor(select, col);
		return false;
		}
	return false;
}

void
Tick::DoMark(anyOutput *o, bool mark)
{
	if(!bValidTick) return;
	if(mark){
		if(mo) DelBitmapClass(mo);					mo = 0L;
		memcpy(&mrc, &rDims, sizeof(RECT));
		IncrementMinMaxRect(&mrc, 6 + (parent && parent->Id == GO_AXIS) ? o->un2ix(((Axis*)parent)->axline.width):o->iLine);
		mo = GetRectBitmap(&mrc, o);
		InvertLine(pts, 2, defs.GetOutLine(), &rDims, o, mark);
		}
	else RestoreRectBitmap(&mo, &mrc, o);
}

bool
Tick::Command(int cmd, void *tmpl, anyOutput *o)
{
	MouseEvent *mev;
	TextDEF *LabelDef;
	GraphObj **tmpPlots;
	AxisDEF *axis;
	DWORD pg_color;
	int i;

	switch(cmd){
	case CMD_SCALE:
		if(label) label->Command(cmd, tmpl, o);
		if(Grid) Grid->Command(cmd, tmpl, o);
		size *= ((scaleINFO*)tmpl)->sy.fy;
		if(Polygons) for(i = 0; i < numPG; i++) if(Polygons[i]) (Polygons[i])->Command(cmd, tmpl, o);;
		break;
	case CMD_ADDTOLINE:
		return StoreSeg((lfPOINT*)tmpl);
	case CMD_RECALC:
		if(Polygons) Undo.DropListGO(this, (GraphObj***)&Polygons, &numPG, UNDO_CONTINUE);
		n_seg = 0;		bModified = true;
		return true;
	case CMD_SET_AXDEF:
		if(axis = (AxisDEF*)tmpl) {
			flags = (flags & AXIS_MINORTICK) | axis->flags;
			}
		break;
	case CMD_FLUSH:
		if(Grid) DeleteGO(Grid);		Grid = 0L;
		if(label) DeleteGO(label);		label = 0L;
		if(name) free(name);			name = 0L;
		if(ls) delete(ls);				ls = 0L;
		return true;
	case CMD_SET_TICKSTYLE:
		flags &= ~0x07;
		flags |= (0x07 & *((DWORD*)tmpl));
		return true;
	case CMD_UPDPG:
		if(parent && parent->Id == GO_AXIS) pg_color = ((Axis*)parent)->GradColor(value);
		else pg_color = 0x00ffffff;
		if(Polygons) for(i = 0; i < numPG; i++) if(Polygons[i]) {
			Polygons[i]->SetColor(COL_POLYGON, pg_color);
			}
		return true;
	case CMD_DRAWPG:
		if(Polygons) for(i = 0; i < numPG; i++) if(Polygons[i]) {
			Polygons[i]->DoPlot(o);
			}
		// we lost the line definition from the parent axis
		if(parent) parent->Command(CMD_RESET_LINE, 0L, o);
		return true;
	case CMD_TICK_TYPE:
		if(tmpl) type = *((int*)tmpl);
		return true;
	case CMD_SETSCROLL:		case CMD_REDRAW:
		//this commands are usually issued from a child or from Undo
		bModified = true;
		return parent ? parent->Command(CMD_REDRAW, 0L, o) : false;
	case CMD_MUTATE:
		bModified = true;
		if(!parent || !(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
		if(label == tmpPlots[0]) {
			Undo.MutateGO((GraphObj**)&label, tmpPlots[1], 0L, o);
			return true;
			}
		break;
	case CMD_DELOBJ:
		bModified = true;
		if(parent && tmpl && o) {
			if(tmpl == Grid) {
				Undo.DeleteGO((GraphObj**)(&Grid), 0L, o);
				flags &= ~AXIS_GRIDLINE;
				return parent->Command(CMD_REDRAW, 0L, o);
				}
			if(tmpl == label) {
				Undo.DeleteGO((GraphObj**)(&label), 0L, o);
				label = 0L;
				return parent->Command(CMD_REDRAW, 0L, o);
				}
			if(DeleteGOL((GraphObj***) &Polygons, numPG, (GraphObj *)tmpl, o)) 
				return parent->Command(CMD_REDRAW, 0L, o);
			}
		return false;
	case CMD_SET_GO3D:		case CMD_GET_GRIDLINE:
		if(parent) return parent->Command(cmd, tmpl, o);
		return false;
	case CMD_SET_GRIDTYPE:
		if(tmpl && *((int*)tmpl)) gl_type = *((int*)tmpl);
	case CMD_SET_GRIDLINE:
		if(Grid && tmpl) return Grid->Command(cmd, tmpl, o);
		return false;
	case CMD_SET_DATAOBJ:
		Id = GO_TICK;
		if(Grid) Grid->Command(cmd, tmpl, o);
		if(label) label->Command(cmd, tmpl, o);
		if(Polygons) for(i = 0; i < numPG; i++) if(Polygons[i]) (Polygons[i])->Command(cmd, tmpl, o);;
		return true;
	case CMD_TEXTTHERE:
		if(label && label->Command(cmd, tmpl, o)) return true;
		return false;
	case CMD_MOUSE_EVENT:
		if((flags & AXIS_GRIDLINE) && Grid && Grid->Command(cmd, tmpl, o)) return true;
		if(label && label->Command(cmd, tmpl, o)) return true;
		mev = (MouseEvent *) tmpl;
		switch (mev->Action) {
		case MOUSE_LBUP:
			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO && 
				IsCloseToLine(&pts[0], &pts[1], mev->x, mev->y)) {
				o->ShowMark(this, MRK_GODRAW);
				return true;
				}
			if(Polygons && !CurrGO) for(i = numPG-1; i >= 0; i--)
				if(Polygons[i]) if(Polygons[i]->Command(cmd, tmpl, o))return true;
			break;
			}
		return false;
	case CMD_TLB_TXTDEF:
		if(label) return label->Command(CMD_SETTEXTDEF, tmpl, o);
		return false;
	case CMD_GETTEXT:
		if(label) return label->Command(cmd, tmpl, o);
		return false;
	case CMD_SETTEXT:
		if(label) return label->Command(cmd, tmpl, o);
		if(!(LabelDef = (TextDEF *)calloc(1, sizeof(TextDEF))))return false;
		LabelDef->ColTxt = parent ? parent->GetColor(COL_AXIS) : defs.Color(COL_AXIS);
		LabelDef->ColBg = parent ? parent->GetColor(COL_BG) : defs.Color(COL_AXIS);
		LabelDef->RotBL = LabelDef->RotCHAR = 0.0f;
		LabelDef->fSize = parent ? parent->GetSize(SIZE_TICK_LABELS) : DefSize(SIZE_TICK_LABELS);
		switch(flags & 0x70) {
		case AXIS_USER:		LabelDef->Align = TXA_VCENTER | TXA_HCENTER;	break;
		case AXIS_LEFT:		LabelDef->Align = TXA_VCENTER | TXA_HRIGHT;		break;
		case AXIS_RIGHT:	LabelDef->Align = TXA_VCENTER | TXA_HLEFT;		break;
		case AXIS_TOP:		LabelDef->Align = TXA_VBOTTOM | TXA_HCENTER;	break;
		case AXIS_BOTTOM:	LabelDef->Align = TXA_VTOP | TXA_HCENTER;		break;
		default:			LabelDef->Align = TXA_VTOP | TXA_HRIGHT;		break;
			}
		LabelDef->Style = TXS_NORMAL;
		LabelDef->Mode = TXM_TRANSPARENT;
		LabelDef->Font = FONT_HELVETICA;
		LabelDef->text = tmpl && *((char*)tmpl) ? (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1, 0) : 0L;
		label = new Label(this, 0L, fix, fiy, LabelDef, LB_X_PARENT | LB_Y_PARENT);
		if(LabelDef->text) free(LabelDef->text);
		delete (LabelDef);
		return true;
		}
	return false;
}

void
Tick::Track(POINT *p, anyOutput *o)
{
	POINT tpts[2];
	int iw;

	if(!bValidTick || !parent || !o) return;
	iw = 6 + o->un2ix(o->LineWidth);
	defs.UpdRect(o, rDims.left, rDims.top, rDims.right, rDims.bottom);
	tpts[0].x = pts[0].x+p->x;	tpts[0].y = pts[0].y+p->y;
	tpts[1].x = pts[1].x+p->x;	tpts[1].y = pts[1].y+p->y;
	defs.UpdRect(o, tpts[0].x-iw, tpts[0].y-iw, tpts[1].x-iw, tpts[1].y-iw);
	defs.UpdRect(o, tpts[0].x+iw, tpts[0].y+iw, tpts[1].x+iw, tpts[1].y+iw);
	o->ShowLine(tpts, 2, parent->GetColor(COL_AXIS));
	if((flags & AXIS_MINORTICK) == 0 && label) label->Track(p, o);
}

void
Tick::DoPlot(double six, double csx, anyOutput *o)
{
	fPOINT3D dp1, dp2;
	POINT3D ip2, p31, p32;

	if(!parent || parent->Id != GO_AXIS) return;
	if(mo) DelBitmapClass(mo);		mo = 0L;
	if(ls) delete(ls);				ls = 0L;
	ip2.x = ip2.y = ip2.z = 0;
	if(!(bValidTick = ((Axis*)parent)->GetValuePos(value, &fix, &fiy, &fiz, o))) return;
	lbx = fix;		lby = fiy;
	if(flags & AXIS_ANGULAR) {
		dp1.fx = o->co2fix(parent->GetSize(SIZE_XCENT)+parent->GetSize(SIZE_GRECT_LEFT));
		dp1.fy = o->co2fiy(parent->GetSize(SIZE_YCENT)+parent->GetSize(SIZE_GRECT_TOP));
		dp1.fz = o->un2fix(parent->GetSize(SIZE_DRADIUS));
		six = (fix - dp1.fx)/dp1.fz;		csx = (dp1.fy - fiy)/dp1.fz;
		lbx += (o->un2fix(DefSize(SIZE_AXIS_TICKS)*3.0*six));
		lby -= (o->un2fiy(DefSize(SIZE_AXIS_TICKS)*3.0*csx));
		}
	switch(type & 0x0f) {
	case 1:		lsi = sin(angle/57.29577951);	lcsi = cos(angle/57.29577951);	break;
	default:	lsi = -csx;		lcsi = six;		break;
		}
	if(flags & AXIS_MINORTICK) {
		ip2.x = o->un2ix(0.5 * size * lcsi);		ip2.y = o->un2iy(0.5 * size * lsi);
		}
	else {
		ip2.x = o->un2ix(size * lcsi);				ip2.y = o->un2iy(size * lsi);
		}
	if(flags & AXIS_3D){
		dp1.fx = dp1.fy = dp1.fz = 0.0;
		switch(type & 0x0f){
		case 2:		dp1.fx = size;	dp1.fy = dp1.fz = 0.0;		break;
		case 3:		dp1.fy = -size;	dp1.fx = dp1.fz = 0.0;		break;
		case 4:		dp1.fz = size;	dp1.fx = dp1.fy = 0.0;		break;
			}
		if(dp1.fx != dp1.fy || dp1.fx != dp1.fz) {
			if(flags & AXIS_MINORTICK) {
				dp1.fx *= 0.5;		dp1.fy *= 0.5;	dp1.fz *= 0.5;
				}
			o->uvec2ivec(&dp1, &dp2);
			ip2.x = iround(dp2.fx);		ip2.y = iround(dp2.fy);
			ip2.z = iround(dp2.fz);
			}
		}
	switch (flags &0x03) {
	case AXIS_NOTICKS:
		return;							//no ticks
	case AXIS_POSTICKS:					//ticks are positive
		break;
	case AXIS_NEGTICKS:					//ticks are negative
		ip2.x *= -1;			ip2.y *= -1;		break;
	case AXIS_SYMTICKS:					//symmetrical ticks around axis: process later
		break;
		}
	pts[1].x = iround(fix);	pts[1].y = iround(fiy);
	p31.z = p32.z = iround(fiz);
	if((flags &0x03) == AXIS_SYMTICKS) {	//tick is symetrical !
		pts[1].x -= (ip2.x >>1);		pts[1].y -= (ip2.y >>1);
		p31.z -= (ip2.z >>1);			p32.z = p31.z;
		}
	p31.x = p32.x = pts[0].x = pts[1].x;	p31.y = p32.y = pts[0].y = pts[1].y;
	pts[1].x += ip2.x;			pts[1].y += ip2.y;			p32.z += ip2.z;
	p32.x += ip2.x;				p32.y += ip2.y;
	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
	IncrementMinMaxRect(&rDims, 6 + o->un2ix(o->LineWidth));
	if(parent && parent->Id == GO_AXIS && (flags & AXIS_3D)){
		if(ls = new line_segment(this, data, &((Axis*)parent)->axline, &p31, &p32)){
			ls->DoPlot(o);
			}
		}
	else o->oSolidLine(pts);
	if(!(flags & AXIS_MINORTICK)){
		if(flags & AXIS_GRIDLINE) {
			if(!Grid){
				if(flags & AXIS_3D) {
					Grid = new GridLine3D(this, data, gl_type, flags);
					}
				else if((flags & AXIS_ANGULAR) == AXIS_ANGULAR){
					Grid = new GridRadial(this, data, gl_type, flags);
					}
				else {
					Grid = new GridLine(this, data, gl_type, flags);
					}
				}
			if(Grid) Grid->DoPlot(o);
			// we lost the line definition from the parent axis
			if(parent) parent->Command(CMD_RESET_LINE, 0L, o);
			}
		if(label){
			if(flags & AXIS_3D) label->SetSize(SIZE_ZPOS, fiz); 
			label->DoPlot(o);
			}
		}
}

//return true if two points are close together
#define X_TOL 1.0E-12
#define Y_TOL 1.0E-12
bool
Tick::CmpPoints(double x1, double y1, double x2, double y2)
{
	if(fabs(x2 - x1) > X_TOL) return false;
	if(fabs(y2 - y1) > Y_TOL) return false;
	return true;
}
#undef X_TOL
#undef Y_TOL

bool
Tick::StoreSeg(lfPOINT *line)
{
	int i;

	if(n_seg >= s_seg) {
		if(!(seg = (double*)realloc(seg, ((s_seg += 50)<<2)*sizeof(double))))return false;
		}
	i = n_seg << 2;
	seg[i++] = line[0].fx;		seg[i++] = line[0].fy;
	seg[i++] = line[1].fx;		seg[i++] = line[1].fy;
	n_seg++;			return false;
}

//collect line segments to polygons
bool
Tick::ProcSeg()
{
	lfPOINT *pg;
	int i, j, l, n, n0, n1, n_pg, level;
	bool cont1, cont2;
	FillDEF pg_fill = {0, 0x0, 1.0, 0L, 0x0L};
	LineDEF pg_line = {defs.GetSize(SIZE_HAIRLINE), 1.0, 0x0L, 0x0L};

	if(n_seg < 3) {
		n_seg = 0;	return false;
		}
	if(!(pg = (lfPOINT*)malloc((n_seg+2)*sizeof(lfPOINT)))) return false;
	if(!parent || parent->Id != GO_AXIS) return false;
	pg_fill.color = pg_fill.color2 = ((Axis*)parent)->GradColor(value);
	pg_line.color = ((pg_fill.color & 0x00fefefeL)>>1);
	while(n_seg > 2) {
		pg[0].fx = seg[0];	pg[0].fy = seg[1];
		pg[1].fx = seg[2];	pg[1].fy = seg[3];
		n0 = level = 0; n = 1;	n_pg = 2;
		do {
			do {
				cont2 = cont1 = false;		n1 = (n<<2);
				if(CmpPoints(pg[n_pg-1].fx, pg[n_pg-1].fy, seg[n1], seg[n1+1])) {
					pg[n_pg].fx = seg[n1+2];	pg[n_pg].fy = seg[n1+3];
					n++;	n_pg++;		cont2=true;
					}
				else if(CmpPoints(pg[n_pg-1].fx, pg[n_pg-1].fy, seg[n1+2], seg[n1+3] )) {
					pg[n_pg].fx = seg[n1];		pg[n_pg].fy = seg[n1+1];
					n++;	n_pg++;		cont2=true;
					}
				else if(CmpPoints(pg[0].fx, pg[0].fy, seg[n1], seg[n1+1])) {
					for(i = n_pg; i > 0; i--) {
						pg[i].fx = pg[i-1].fx;	pg[i].fy = pg[i-1].fy;
						}
					pg[0].fx = seg[n1+2];		pg[0].fy = seg[n1+3];
					n++;	n_pg++;		cont2=true;
					}
				else if(CmpPoints(pg[0].fx, pg[0].fy, seg[n1+2], seg[n1+3])) {
					for(i = n_pg; i > 0; i--) {
						pg[i].fx = pg[i-1].fx;	pg[i].fy = pg[i-1].fy;
						}
					pg[0].fx = seg[n1];			pg[0].fy = seg[n1+1];
					n++;	n_pg++;		cont2=true;
					}
				if(cont2) level = 0;
				}while(cont2 && n_pg < s_seg && n < n_seg);
			if(n > n0 && n < n_seg) {
				for(i = n0<<2, j = n<<2, l = (n_seg<<2); j < l; i++, j++) {
					seg[i] = seg[j];
					}
				n_seg -= (n-n0);	n0 = n = 0;
				}
			else if(n == n_seg){
				n_seg = n0;			n0 = n = 0;
				}
			else {
				n0++;				n++;
				}
			if(CmpPoints(pg[0].fx, pg[0].fy, pg[n_pg-1].fx, pg[n_pg-1].fy)) cont1 = false;
			else if(n < n_seg && n_seg) cont1 = true;
			else {
				if(n_seg && level < 3) {
					level++;	n = 0;	cont1 = true;
					}
				else cont1 = false;
				}
			}while(cont1);
		if(n_pg > 2) {
			if(CmpPoints(pg[0].fx, pg[0].fy, pg[n_pg-1].fx, pg[n_pg-1].fy)) n_pg--;
			if(n_pg > 2) {
				Polygons = (DataPolygon**)realloc(Polygons, (numPG+2)*sizeof(DataPolygon*));
				if(Polygons[numPG] = new DataPolygon(this, data, (lfPOINT*)memdup(pg, n_pg*sizeof(lfPOINT), 0), n_pg, 0L)){
					Polygons[numPG]->type = 12;
					Polygons[numPG]->Command(CMD_SET_LINE, &pg_line, 0L);
					Polygons[numPG++]->Command(CMD_PG_FILL, &pg_fill, 0L);
					}
				}
			}
		}
	free(pg);	n_seg = 0;	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Axes are graphic objects containing ticks
Axis::Axis(GraphObj *par, DataObj *d, AxisDEF *ax, DWORD flags):
	GraphObj(par, d)
{
	if(!(axis = (AxisDEF*)malloc(sizeof(AxisDEF))))return;
	FileIO(INIT_VARS);
	if(flags & AXIS_3D) GridLine.pattern = 0L;
	if(ax->owner){
		if(axis) free(axis);
		axis = ax;
		}
	else {
		if(axis) {
			memcpy((void*)axis, (void*)ax, sizeof(AxisDEF));
			axis->owner = (void*)this;
			}
		}
	axis->flags = flags;
	if ((flags & AXIS_ANGULAR) || (flags & AXIS_RADIAL)) {
		GridLine.color = colAxis;
		GridLine.pattern = 0x0;
		}
	Id = GO_AXIS;
}

Axis::Axis(int src):GraphObj(0L, 0L)
{
	if(!(axis = (AxisDEF*)malloc(sizeof(AxisDEF))))return;
	FileIO(INIT_VARS);
	if(src == FILE_READ) {
		FileIO(FILE_READ);
		}
}

Axis::~Axis()
{
	Undo.InvalidGO(this);
	if(axis && axis->owner == (void*)this){
		if(axis->breaks) free(axis->breaks);
		free(axis);
		}
	Command(CMD_FLUSH, 0L, 0L);
	if(ssMATval) free(ssMATval);	if(ssMATlbl) free(ssMATlbl);
	if(ssMITval) free(ssMITval);	ssMATval = ssMATlbl = ssMITval = 0L;
	if(axisLabel) DeleteGO(axisLabel);	axisLabel = 0L;
	if(mo) DelBitmapClass(mo);			mo = 0L;
	if(atv) delete atv;					atv = 0L;
}

double
Axis::GetSize(int select)
{
	switch(select) {
	case SIZE_LB_XDIST:			return lbdist.fx;
	case SIZE_LB_YDIST:			return lbdist.fy;
	case SIZE_TLB_XDIST:		return tlbdist.fx;
	case SIZE_TLB_YDIST:		return tlbdist.fy;
	case SIZE_LB_XPOS:			return(flim[0].fx + flim[1].fx)/2.0f;
	case SIZE_LB_YPOS:			return(flim[0].fy + flim[1].fy)/2.0f;
	case SIZE_TICK_LABELS:		return sizAxTickLabel;
	case SIZE_AXIS_TICKS:		return sizAxTick;
	case SIZE_AXIS_LINE:		return sizAxLine;
	case SIZE_XPOS:				return axis->loc[0].fx;
	case SIZE_XPOS+1:			return axis->loc[1].fx;
	case SIZE_YPOS:				return axis->loc[0].fy;
	case SIZE_YPOS+1:			return axis->loc[1].fy;
	case SIZE_ZPOS:				return axis->loc[0].fz;
	case SIZE_ZPOS+1:			return axis->loc[1].fz;
	case SIZE_XCENT:			return axis->Center.fx;
	case SIZE_YCENT:			return axis->Center.fy;
	case SIZE_RADIUS1:	case SIZE_RADIUS2:	case SIZE_DRADIUS:
		return axis->Radius;
	case SIZE_BOUNDS_XMIN:		case SIZE_BOUNDS_XMAX:		case SIZE_BOUNDS_YMIN:
	case SIZE_BOUNDS_YMAX:		case SIZE_BOUNDS_ZMIN:		case SIZE_BOUNDS_ZMAX:
		if(parent) return parent->GetSize(select);
		break;
		}
	return DefSize(select);
}

DWORD
Axis::GetColor(int select)
{
	switch(select){
	case COL_AXIS:
		return colAxis;
		}
	if(parent) return parent->GetColor(select);
	else return defs.Color(select);
}

bool
Axis::SetSize(int select, double value)
{
	int i;

	switch(select & 0xfff) {
	case SIZE_AXIS_LINE:
		 sizAxLine = value;
		 break;
	case SIZE_LB_XDIST:
		lbdist.fx = value;
		if(axisLabel)axisLabel->SetSize(select,value);
		break;
	case SIZE_LB_YDIST:
		lbdist.fy = value;
		if(axisLabel)axisLabel->SetSize(select,value);
		break;
	case SIZE_TLB_XDIST:		case SIZE_TLB_YDIST:
	case SIZE_AXIS_TICKS:		case SIZE_TICK_ANGLE:
		switch (select){
		case SIZE_TLB_XDIST:
			tlbdist.fx = value;			select = SIZE_LB_XDIST;
			break;
		case SIZE_TLB_YDIST:
			tlbdist.fy = value;			select = SIZE_LB_YDIST;
			break;
		case SIZE_AXIS_TICKS:
			sizAxTick = value;
			break;
		case SIZE_TICK_ANGLE:
			tick_angle = value;
			break;
			}
		if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++) 
			if(Ticks[i]) Ticks[i]->SetSize(select, value);
		break;
	default:
		return false;
		}
	return true;
}

bool
Axis::SetColor(int select, DWORD col)
{
	int i;

	switch(select & 0xfff) {
	case COL_AXIS:
		if(colAxis == col) return true;
		if(select & UNDO_STORESET){
			Undo.ValDword(this, &colAxis, UNDO_CONTINUE);
			Undo.ValDword(this, &tlbdef.ColTxt, UNDO_CONTINUE);
			}
		colAxis = col;
		if (axis && (axis->flags & AXIS_ANGULAR) || (axis->flags & AXIS_RADIAL)) {
			GridLine.color = colAxis;
			}
		if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++)
			if(Ticks[i]) Ticks[i]->SetColor(select, colAxis);
		if(axisLabel) axisLabel->SetColor((select & UNDO_STORESET) ? 
			COL_TEXT | UNDO_STORESET : COL_TEXT, col);
		tlbdef.ColTxt = col;
		return true;
	case COL_BG:
		if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++)
			if(Ticks[i]) Ticks[i]->SetColor(select, col);
		if(axisLabel) axisLabel->SetColor(select, col);
		return true;
		}
	return false;
}

void
Axis::DoPlot(anyOutput *o)
{
	lfPOINT fp1, fp2;
	fPOINT3D loc, cu1, cu2, rc;
	double tmp, dx, dy;
	int i, j, ix, iy;
	fRECT scaleRC;
	AxisDEF tmp_axis;

	if(!o || !parent) return;
	if(mo)DelBitmapClass(mo);		mo = 0L;
	if(l_segs){
		for (i = 0; i < nl_segs; i++) if(l_segs[i]) delete(l_segs[i]);
		free(l_segs);		l_segs = 0L;		nl_segs = 0;
		}
	dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
	if(!type) {
		if(fabs(axis->loc[1].fx - axis->loc[0].fx) > 
			fabs(axis->loc[1].fy - axis->loc[0].fy)) {
			if((axis->flags) & AXIS_3D){
				if(fabs(axis->loc[1].fz - axis->loc[0].fz) > 
					fabs(axis->loc[1].fx - axis->loc[0].fx)) type = 3;
				else type = 1;
				}
			else type = 1;
			}
		else {
			if((axis->flags) & AXIS_3D){
				if(fabs(axis->loc[1].fz - axis->loc[0].fz) > 
					fabs(axis->loc[1].fy - axis->loc[0].fy)) type = 3;
				else type = 2;
				}
			else type = 2;
			}
		}
	//find default type for grid lines
	if(!gl_type){
		if(axis->flags & AXIS_3D) {
			switch(type) {
			case 1:		gl_type = 0x30;			break;
			case 2:		gl_type = 0x0c;			break;
			case 3:		gl_type = 0x03;			break;
			default:	gl_type = 0x26;			break;
				}
			}
		else {
			gl_type = type == 2 ? DL_LEFT | DL_RIGHT : DL_TOP | DL_BOTTOM;
			}
		}
	if(!Ticks && (axis->flags & 0x03) != AXIS_NOTICKS)CreateTicks();
	if(axis->owner == this && !scaleOut) scaleOut = new anyOutput();
	if(scaleOut) {
		// set scaling information in scaleOut
		scaleOut->hres = o->hres;			scaleOut->vres = o->vres;
		scaleOut->VPscale = o->VPscale;
		scaleOut->VPorg.fx = o->VPorg.fx;	scaleOut->VPorg.fy = o->VPorg.fy;
		memcpy(&tmp_axis, axis, sizeof(AxisDEF));
		tmp_axis.loc[0].fx += dx;		tmp_axis.loc[1].fx += dx;
		tmp_axis.loc[0].fy += dy;		tmp_axis.loc[1].fy += dy;
		if(IsPlot3D(parent) && (axis->flags & AXIS_3D)) {
			//set 3D information in scaleOut
			cu1.fx = axis->loc[0].fx +dx;	cu1.fy = axis->loc[0].fy +dy;
			cu1.fz = axis->loc[0].fz;		cu2.fx = axis->loc[1].fx +dx;
			cu2.fy = axis->loc[1].fy +dy;	cu2.fz = axis->loc[1].fz;
			rc.fx = parent->GetSize(SIZE_XCENTER) +dx;
			rc.fy = parent->GetSize(SIZE_YCENTER) +dy;
			rc.fz = parent->GetSize(SIZE_ZCENTER);
			scaleOut->SetSpace(&cu1, &cu2, defs.cUnits, ((Plot3D*)parent)->RotDef, &rc, 
				&tmp_axis, &tmp_axis, &tmp_axis);
			}
		else {
			// set axis and rectangle in scaleOut
			scaleRC.Xmin = scaleRC.Xmax = dx;
			scaleRC.Ymin = scaleRC.Ymax = dy;
			scaleRC.Xmin += axis->loc[0].fx;		scaleRC.Ymin += axis->loc[1].fy;
			scaleRC.Xmax += axis->loc[1].fx;		scaleRC.Ymax += axis->loc[0].fy;
			scaleOut->SetRect(scaleRC, o->units, &tmp_axis, &tmp_axis);
			}
		}
	axline.width = sizAxLine;
	axline.patlength = 1.0;
	axline.color = colAxis;
	axline.pattern = 0L;		//solid line, no pattern
	o->SetLine(&axline);
	if(axis->flags & AXIS_ANGULAR) {
		pts[1].x = i = o->un2ix(axis->Radius);		pts[1].y = j = o->un2iy(axis->Radius);
		pts[0].x = ix = o->co2ix(axis->Center.fx + dx);
		pts[0].y = iy = o->co2iy(axis->Center.fy + dy);
		o->oCircle(ix-i, iy-j, ix+i, iy+j);
		if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++) 
			if(Ticks[i]) Ticks[i]->DoPlot(0.0, 1.0, o);
		rDims.left = ix-i;	rDims.right = ix +i;
		rDims.top = iy -i;	rDims.bottom = iy+i;
		IncrementMinMaxRect(&rDims, 6 + o->un2ix(sizAxLine));
		return;
		}
	if(axis->flags & AXIS_3D){
		loc.fx= axis->loc[0].fx;	loc.fy= axis->loc[0].fy;	loc.fz = axis->loc[0].fz;	
		o->cvec2ivec(&loc, &flim[0]);
		loc.fx= axis->loc[1].fx;	loc.fy= axis->loc[1].fy;	loc.fz = axis->loc[1].fz;	
		o->cvec2ivec(&loc, &flim[1]);
		}
	else {
		flim[0].fz = flim[1].fz = 0.0;
		if(parent) switch(axis->flags & 0x70) {
		case AXIS_USER:				//leave unchanged
			fp1.fx = axis->loc[0].fx;		fp1.fy = axis->loc[0].fy;
			fp2.fx = axis->loc[1].fx;		fp2.fy = axis->loc[1].fy;
			break;
		case AXIS_LEFT:
			if(axis->flags & AXIS_X_DATA) {
				axis->loc[0].fx = axis->loc[1].fx =	fp1.fx = fp2.fx = 
					parent->GetSize(SIZE_BOUNDS_LEFT);
				}
			else {
				axis->loc[0].fx = axis->loc[1].fx =	fp1.fx = fp2.fx = 
					parent->GetSize(SIZE_DRECT_LEFT);
				}
			if(axis->flags & AXIS_Y_DATA) {
				fp1.fy = parent->GetSize(SIZE_BOUNDS_BOTTOM);
				fp2.fy = parent->GetSize(SIZE_BOUNDS_TOP);
				}
			else {
				fp1.fy = parent->GetSize(SIZE_DRECT_TOP);
				fp2.fy = parent->GetSize(SIZE_DRECT_BOTTOM);
				}
			break;
		case AXIS_RIGHT:
			if(axis->flags & AXIS_X_DATA) {
				axis->loc[0].fx = axis->loc[1].fx = fp1.fx = fp2.fx = 
					parent->GetSize(SIZE_BOUNDS_RIGHT);
				}
			else {
				axis->loc[0].fx = axis->loc[1].fx =	fp1.fx = fp2.fx = 
					parent->GetSize(SIZE_DRECT_RIGHT);
				}
			if(axis->flags & AXIS_Y_DATA) {
				fp1.fy = parent->GetSize(SIZE_BOUNDS_BOTTOM);
				fp2.fy = parent->GetSize(SIZE_BOUNDS_TOP);
				}
			else {
				fp1.fy = parent->GetSize(SIZE_DRECT_TOP);
				fp2.fy = parent->GetSize(SIZE_DRECT_BOTTOM);
				}
			break;
		case AXIS_TOP:
			if(axis->flags & AXIS_Y_DATA) {
				axis->loc[0].fy = axis->loc[1].fy = fp1.fy = fp2.fy = 
					parent->GetSize(SIZE_BOUNDS_TOP);
				}
			else {
				axis->loc[0].fy = axis->loc[1].fy = fp1.fy = fp2.fy = 
					parent->GetSize(SIZE_DRECT_TOP);
				}
			if(axis->flags & AXIS_X_DATA) {
				fp1.fx = parent->GetSize(SIZE_BOUNDS_LEFT);
				fp2.fx = parent->GetSize(SIZE_BOUNDS_RIGHT);
				}
			else {
				fp1.fx = parent->GetSize(SIZE_DRECT_LEFT);
				fp2.fx = parent->GetSize(SIZE_DRECT_RIGHT);
				}
			break;
		case AXIS_BOTTOM:
			if(axis->flags & AXIS_Y_DATA) {
				axis->loc[0].fy = axis->loc[1].fy = fp1.fy = fp2.fy =
					parent->GetSize(SIZE_BOUNDS_BOTTOM);
				}
			else {
				axis->loc[0].fy = axis->loc[1].fy = fp1.fy = fp2.fy = 
					parent->GetSize(SIZE_DRECT_BOTTOM);
				}
			if(axis->flags & AXIS_X_DATA) {
				fp1.fx = parent->GetSize(SIZE_BOUNDS_LEFT);
				fp2.fx = parent->GetSize(SIZE_BOUNDS_RIGHT);
				}
			else {
				fp1.fx = parent->GetSize(SIZE_DRECT_LEFT);
				fp2.fx = parent->GetSize(SIZE_DRECT_RIGHT);
				}
			break;
			}
		if(axis->flags & AXIS_X_DATA) {
			flim[0].fx = o->fx2fix(fp1.fx);		flim[1].fx = o->fx2fix(fp2.fx);
			}
		else {
			flim[0].fx = o->co2fix(fp1.fx + dx);	flim[1].fx = o->co2fix(fp2.fx + dx);
			axis->loc[0].fx = fp1.fx;	axis->loc[1].fx = fp2.fx;
			}
		if(axis->flags & AXIS_Y_DATA) {
			flim[0].fy = o->fy2fiy(fp1.fy);		flim[1].fy = o->fy2fiy(fp2.fy);
			}
		else {
			flim[0].fy = o->co2fiy(fp1.fy + dy);	flim[1].fy = o->co2fiy(fp2.fy + dy);
			axis->loc[0].fy = fp1.fy;	axis->loc[1].fy = fp2.fy;
			}
		}
	pts[0].x = iround(flim[0].fx);		pts[1].x = iround(flim[1].fx);
	pts[0].y = iround(flim[0].fy);		pts[1].y = iround(flim[1].fy);
	pts3D[0].x = pts[0].x;				pts3D[1].x = pts[1].x;
	pts3D[0].y = pts[0].y;				pts3D[1].y = pts[1].y;
	pts3D[0].z = iround(flim[0].fz);	pts3D[1].z = iround(flim[1].fz);
	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
	IncrementMinMaxRect(&rDims, 6 + o->un2ix(sizAxLine));
	//calculate sine and cosine for ticks in any direction of axis
	si = flim[1].fy - flim[0].fy;
	tmp = (flim[1].fx - flim[0].fx);
	si = fabs(si) > 0.0001 ? si/sqrt(si*si + tmp*tmp) : 0.0;
	csi = flim[1].fx - flim[0].fx;
	tmp = (flim[1].fy - flim[0].fy);
	csi = fabs(csi) > 0.0001 ? csi/sqrt(csi*csi + tmp*tmp) : 0.0;
	//draw gradient bar, breaks and axis line
	if(type == 4) GradientBar(o);
	else if(axis->breaks && axis->nBreaks)DrawBreaks(o);
	else if((axis->flags) & AXIS_3D){
		if(!(l_segs = (line_segment**)calloc(1, sizeof(line_segment*))))return;
		l_segs[0] = new line_segment(this, data, &axline, &pts3D[0], &pts3D[1]);
		nl_segs = 1;
		if(l_segs[0])l_segs[0]->DoPlot(o);
		}
	else o->oSolidLine(pts);
	//now execute the draw routine of label and every tick
	if(axisLabel){
		if(axis->flags & AXIS_3D) axisLabel->SetSize(SIZE_ZPOS, (pts3D[0].z + pts3D[1].z)>>1); 
		axisLabel->DoPlot(o);
		}
	if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++) 
		if(Ticks[i] && Ticks[i]->GetSize(SIZE_MINE) >= (axis->min - 1.0e-16)
		&& Ticks[i]->GetSize(SIZE_MINE) <= (axis->max + 1.0e-16)) Ticks[i]->DoPlot(si, csi, o);
}

bool
Axis::Command(int cmd, void *tmpl, anyOutput *o)
{
	MouseEvent *mev;
	GraphObj **tmpPlots;
	void *sv_ptr;
	scaleINFO *scale;
	int i;

	switch (cmd) {
	case CMD_SCALE:
		if(!Ticks && (axis->flags & 0x03) != AXIS_NOTICKS)CreateTicks();
		scale = (scaleINFO*)tmpl;
		lbdist.fx *= scale->sx.fy;			lbdist.fy *= scale->sy.fy;
		tlbdist.fx *= scale->sx.fy;			tlbdist.fy *= scale->sy.fy;
		sizAxTickLabel *= scale->sy.fy;		sizAxTick *= scale->sy.fy;
		sizAxLine *= scale->sy.fy;			brksymsize *= scale->sy.fy;
		brkgap *= scale->sy.fy;				GridLine.patlength *= scale->sy.fy;
		GridLine.width *= scale->sy.fy;		tlbdef.fSize *= scale->sy.fy;
		axis->loc[0].fx *= scale->sx.fy;	axis->loc[1].fx *= scale->sx.fy;
		axis->loc[0].fy *= scale->sy.fy;	axis->loc[1].fy *= scale->sy.fy;
		axis->loc[0].fz *= scale->sz.fy;	axis->loc[1].fz *= scale->sz.fy;
		axis->Center.fx *= scale->sx.fy;	axis->Center.fy *= scale->sy.fy;
		axis->Radius *= scale->sy.fy;		tlbdef.iSize = 0;
		if(axisLabel) axisLabel->Command(cmd, tmpl, o);
		if(Ticks) for(i = 0; i < NumTicks; i++) if(Ticks[i]) Ticks[i]->Command(cmd, tmpl, o);
		return true;
	case CMD_SET_DATAOBJ:
		Id = GO_AXIS;
		data = (DataObj*)tmpl;
		if(Ticks) for(i = 0; i< NumTicks; i++) if(Ticks[i]) Ticks[i]->Command(cmd, tmpl, o);
		if(axisLabel) axisLabel->Command(cmd, tmpl, o);
	case CMD_TICK_TYPE:
		if(cmd == CMD_TICK_TYPE){
			if(tmpl) tick_type = *((int*)tmpl);
			else return false;
			}
	case CMD_SET_TICKSTYLE:
		if(cmd == CMD_SET_TICKSTYLE){
			axis->flags &= ~0x07;		axis->flags |= (*((DWORD*)tmpl) & 0x07);
			}
	case CMD_SET_GRIDTYPE:
		if(cmd == CMD_SET_GRIDTYPE && tmpl && *((int*)tmpl)) {
			gl_type = *((int*)tmpl);
			}
	case CMD_TLB_TXTDEF:			//do all ticks
		if(Ticks) for(i = 0; i< NumTicks; i++) if(Ticks[i]) Ticks[i]->Command(cmd, tmpl, o);
		if(cmd == CMD_TLB_TXTDEF && tmpl) {
			memcpy(&tlbdef, tmpl, sizeof(TextDEF));
			tlbdef.text = 0L;
			}
		return true;
	case CMD_SET_GRIDLINE:
		if(tmpl) memcpy(&GridLine, tmpl, sizeof(LineDEF));
		return true;
	case CMD_MRK_DIRTY:
		if((type & 0xf) == 4 && !o) cmd = CMD_UPDPG;
		else return false;
	case CMD_DRAWPG:	case CMD_UPDPG:
		if((type & 0xf) == 4 && Ticks) {
			if(axis->flags & AXIS_INVERT){
				for(i = NumTicks-1; i >= 0; i--) if(Ticks[i]) Ticks[i]->Command(cmd, tmpl, o);
				}
			else {
				for(i = 0; i< NumTicks; i++) if(Ticks[i]) Ticks[i]->Command(cmd, tmpl, o);
				}
			}
		return true;
	case CMD_GET_GRIDLINE:
		if(tmpl) memcpy(tmpl, &GridLine, sizeof(LineDEF));
		return true;
	case CMD_MOUSE_EVENT:
		mev = (MouseEvent *) tmpl;
		switch (mev->Action) {
		case MOUSE_LBUP:
			if(!CurrGO && ObjThere(mev->x, mev->y)) {
				o->ShowMark(this, MRK_GODRAW);
				return true;
				}
			break;
			}
		if(axisLabel && axisLabel->Command(cmd, tmpl, o)) return true;
		if(Ticks) for(i = (NumTicks-1); i >= 0; i--)
			if(Ticks[i] && Ticks[i]->Command(cmd, tmpl, o)) return true;
		return false;
	case CMD_TEXTTHERE:
		if(axisLabel && axisLabel->Command(cmd, tmpl, o)) return true;
		if(Ticks) for(i = 0; i < NumTicks; i++) {
			if(Ticks[i] && Ticks[i]->Command(cmd, tmpl, o)) return true;
			}
		return false;
	case CMD_SET_AXDEF:
		if(axis = (AxisDEF*)tmpl) {
			if(axis && axis->owner == (void*)this) {
				if(axis->breaks) free(axis->breaks);
				free(axis);
				}
			if(Ticks) for(i = 0; i < NumTicks; i++)
				if(Ticks[i]) {
					Ticks[i]->Command(cmd, tmpl, o);
					Ticks[i]->Command(CMD_SET_GRIDTYPE, (void*) &gl_type, 0L);
					Ticks[i]->Command(CMD_TLB_TXTDEF, &tlbdef, 0L);
					if(axis->flags & AXIS_GRIDLINE) Ticks[i]->Command(CMD_SET_GRIDLINE, &GridLine, 0L);
					Ticks[i]->SetSize(SIZE_TICK_ANGLE, tick_angle);
					Ticks[i]->SetSize(SIZE_AXIS_TICKS, sizAxTick);
					Ticks[i]->SetSize(SIZE_LB_XDIST, tlbdist.fx);
					Ticks[i]->SetSize(SIZE_LB_YDIST, tlbdist.fy);
					Ticks[i]->Command(CMD_TICK_TYPE, &tick_type, 0L);
					}
			return true;
			}
		return false;
	case CMD_CAN_CLOSE:
		if(axis->owner == (void*)this) return true;
		return false;
	case CMD_DROP_LABEL:
		if(axisLabel)DeleteGO(axisLabel);
		if(axisLabel = (Label*)tmpl) {
			axisLabel->parent = this;
			}
		return true;
	case CMD_DELOBJ:
		o->HideMark();
		if(!tmpl || !parent) return false;
		if(DeleteGOL((GraphObj***) &Ticks, NumTicks, (GraphObj *)tmpl, o)) 
			return parent->Command(CMD_REDRAW, 0L, o);
		if(tmpl == (void*)axisLabel) {
			Undo.DeleteGO((GraphObj**)(&axisLabel), 0L, o);
			return parent->Command(CMD_REDRAW, 0L, o);
			}
		if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++){ 
			if(Ticks[i] && Ticks[i]->Command(cmd, tmpl, o))
				return parent->Command(CMD_REDRAW, 0L, o);
			}
		break;
	case CMD_MUTATE:
		if(!parent || !(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
		if(axisLabel == tmpPlots[0]) {
			Undo.MutateGO(&axisLabel, tmpPlots[1], 0L, o);
			return true;
			}
		break;
	case CMD_REPL_GO:
		if(!(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
		if(Ticks) for(i = 0; i < NumTicks; i++) if(Ticks[i] && Ticks[i] == tmpPlots[0]){
			return bModified = ReplaceGO((GraphObj**)&Ticks[i], tmpPlots);
			}
		if(axisLabel && axisLabel == tmpPlots[0])
			return bModified = ReplaceGO((GraphObj**)&axisLabel, tmpPlots);
		return false;
	case CMD_MOVE:
		if(moveable) {
			bModified = true;
			Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
			}
	case CMD_UNDO_MOVE:
		if(moveable) {
			axis->loc[0].fx += ((lfPOINT*)tmpl)[0].fx;	axis->loc[0].fy +=  ((lfPOINT*)tmpl)[0].fy;
			axis->loc[1].fx += ((lfPOINT*)tmpl)[0].fx;	axis->loc[1].fy +=  ((lfPOINT*)tmpl)[0].fy;
			}
		CurrGO = this;
		return parent->Command(CMD_REDRAW, 0, o);
	case CMD_UPDATE:
		UpdateTicks();
	case CMD_SETSCROLL:
		cmd = CMD_REDRAW;
	case CMD_REDRAW:
		bModified = true;
	case CMD_SET_GO3D:
		if(parent) return parent->Command(cmd, tmpl, o);
		return false;
	case CMD_RESET_LINE:
		return o->SetLine(&axline);
	case CMD_FLUSH:
		if(Ticks) {
			for(i = 0; i < NumTicks; i++) if(Ticks[i]) DeleteGO(Ticks[i]);
			free(Ticks);	NumTicks = 0;	Ticks = 0L;
			}
		if(l_segs){
			for (i = 0; i < nl_segs; i++) if(l_segs[i]) delete(l_segs[i]);
			free(l_segs);		l_segs = 0L;	nl_segs = 0;
			}
		if(scaleOut) delete(scaleOut);
		scaleOut = drawOut = 0L;
		return true;
	case CMD_RECALC:
		if(Ticks) {
			for(i = 0; i < NumTicks; i++) if(Ticks[i]) Ticks[i]->Command(cmd, tmpl, o);
			return true;
			}
		break;
	case CMD_AUTOSCALE:		//we receive this command to update ticks after rescaling
		if(axis && (AxisDEF*)tmpl == axis && (axis->flags & AXIS_AUTOSCALE) && 
			(axis->flags & AXIS_AUTOTICK)) return Command(CMD_FLUSH, tmpl, o);
		break;
	case CMD_SAVE_TICKS:
		SavVarInit(200 * NumTicks);
		if(Ticks) for(i = 0; i < NumTicks; i++) {
			if(Ticks[i]) Ticks[i]->FileIO(SAVE_VARS);
			}
		sv_ptr = SavVarFetch();
		Undo.SavVarBlock(this, &sv_ptr, 0);
		if(axis->flags & AXIS_AUTOTICK){
			Undo.ValDword(this, &axis->flags, UNDO_CONTINUE);
			axis->flags &= ~(AXIS_AUTOTICK | AXIS_AUTOSCALE);
			}
		bModified = true;
		return true;
		}
	return false;
}

void * 
Axis::ObjThere(int x, int y)
{
	int i;
	POINT p1 = {x, y};

	if(axis->flags & AXIS_ANGULAR) {
		i = (x - pts[0].x) * (x - pts[0].x) + (y - pts[0].y) * (y - pts[0].y);
		i = isqr(i) - pts[1].x;
		if(i < 4 && i > -4) return this;
		}
	else if((type & 0x04) == 4 && IsInPolygon(&p1, gradient_box, 5)) return this;
	else if(IsInRect(&rDims, x, y) && IsCloseToLine(&pts[0], &pts[1], x, y)) {
		return this;
		}
	return 0L;
}

void
Axis::Track(POINT *p, anyOutput *o)
{
	POINT tpts[5];
	int i, iw;

	if(!parent || !o) return;
	iw = 6 + o->un2ix(sizAxLine);
	defs.UpdRect(o, rDims.left, rDims.top, rDims.right, rDims.bottom);
	tpts[0].x = pts[0].x+p->x;	tpts[0].y = pts[0].y+p->y;
	tpts[1].x = pts[1].x+p->x;	tpts[1].y = pts[1].y+p->y;
	defs.UpdRect(o, tpts[0].x-iw, tpts[0].y-iw, tpts[1].x-iw, tpts[1].y-iw);
	defs.UpdRect(o, tpts[0].x+iw, tpts[0].y+iw, tpts[1].x+iw, tpts[1].y+iw);
	if(type & 0x04) {
		for(i = 0; i < 5; i++) {
			tpts[i].x = gradient_box[i].x + p->x;
			tpts[i].y = gradient_box[i].y + p->y;
			defs.UpdRect(o, tpts[i].x-iw, tpts[i].y-iw, tpts[i].x+iw, tpts[i].y+iw);
			}
		o->ShowLine(tpts, 5, colAxis);
		}
	else o->ShowLine(tpts, 2, colAxis);
	if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++) {
		if(Ticks[i] && Ticks[i]->GetSize(SIZE_MINE) >= (axis->min - 1.0e-16)
			&& Ticks[i]->GetSize(SIZE_MINE) <= (axis->max + 1.0e-16)) Ticks[i]->Track(p, o);
		}
}

DWORD
Axis::GradColor(double val)
{
	DWORD retcol = 0x00cbcbcbL;
	double y, f;

	y = (val-axis->min)/(axis->max-axis->min);
	if(axis->flags & AXIS_INVERT) y = 1.0-y;
	if(grad_type & 0x10) y = 1.0-y;
	switch(grad_type & 0x0f) {
	case 0:
		retcol = gCol_0;		break;
	case 1:		case 4:
		retcol = 0x00000000L;
		if(y >= 1.0) retcol = 0x000000ffL;
		else if(y >= 0.75) {
			f = (y - 0.75)*1024.0;		retcol |= 0x000000ffL;
			retcol |= (((f < 255.0)?((int)(255.0-f)):0)<<8);
			}
		else if(y >= 0.5) {
			f = (y - 0.5)*1024.0;		retcol |= 0x0000ff00L;
			retcol |= ((f < 255.0)?((int)f):0);
			}
		else if(y >= 0.0 && (grad_type &0x0f) == 4) {
			f = y*512.0;
			retcol |= (((f < 255.0)?((int)f):0)<<8);
			retcol |= (((f < 255.0)?((int)(255.0-f)):255)<<16);
			}
		else if(y >= 0.25) {
			f = (y - 0.25)*1024.0;		retcol |= 0x0000ff00L;
			retcol |= (((f < 255.0)?((int)(255.0-f)):255)<<16);
			}
		else if(y >= 0.0) {
			f = y*1024.0;			retcol |= 0x00ff0000L;
			retcol |= (((f < 255.0)?((int)f):0)<<8);
			}
		else retcol = 0x00ff0000L;
		break;
	case 2:
		retcol = 0x00000000L;
		if(y >= 1.0) retcol = 0x00ffffffL;
		else if(y >= (5.0/6.0)) {
			f = (y - (5.0/6.0))*1536.0;	retcol |= 0x000000ffL;
			retcol |= (((f < 255.0)?((int)f):0xff)<<8);
			retcol |= (((f < 255.0)?((int)f):0xff)<<16);
			}
		else if(y >= (4.0/6.0)) {
			f = (y - (4.0/6.0))*1536.0;	retcol |= 0x000000ffL;
			retcol |= (((f < 255.0)?((int)(255.0-f)):0)<<8);
			}
		else if(y >= (3.0/6.0)) {
			f = (y - (3.0/6.0))*1536.0;	retcol |= 0x0000ff00L;
			retcol |= ((f < 255.0)?((int)f):0xff);
			}
		else if(y >= (2.0/6.0)) {
			f = (y - (2.0/6.0))*1536.0;	retcol |= 0x0000ff00L;
			retcol |= (((f < 255.0)?((int)(255.0-f)):0xff)<<16);
			}
		else if(y >= (1.0/6.0)) {
			f = (y - (1.0/6.0))*1536.0;	retcol |= 0x00ff0000L;
			retcol |= (((f < 255.0)?((int)f):0xff)<<8);
			}
		else if(y >= 0.0) {
			f = y*1536.0;
			retcol |= (((f < 255.0)?((int)f):0xff)<<16);
			}
		else retcol = 0x0L;
		break;
	case 3:
		retcol = IpolCol(gCol_2, gCol_1, y);
		}
	if(gTrans & 0xff000000) {
		retcol = (retcol & 0x00ffffff) | (gTrans & 0xff000000);
		}
	return retcol;
}

void
Axis::SetTick(long idx, double val, DWORD flags, char *txt)
{
	char *l;

	if((flags & AXIS_ANGULAR) && !(flags & 0x03)) flags |= AXIS_POSTICKS;
	Ticks[idx] = new Tick(this, data, val, flags);
	if(!txt) {
		WriteNatFloatToBuff(TmpTxt, val);
		l = (char*)memdup(TmpTxt+1, (int)strlen(TmpTxt+1)+1, 0);
		}
	else l = (char*)memdup(txt, (int)strlen(txt)+1, 0);
	if(!gl_type) {
		}
	if(Ticks[idx]) {
		Ticks[idx]->Command(CMD_SET_GRIDTYPE, (void*) &gl_type, 0L);
		if(l) Ticks[idx]->Command(CMD_SETTEXT, l, 0L);
		Ticks[idx]->Command(CMD_TLB_TXTDEF, &tlbdef, 0L);
		if(flags & AXIS_GRIDLINE) Ticks[idx]->Command(CMD_SET_GRIDLINE, &GridLine, 0L);
		Ticks[idx]->SetSize(SIZE_TICK_ANGLE, tick_angle);
		Ticks[idx]->SetSize(SIZE_AXIS_TICKS, sizAxTick);
		Ticks[idx]->Command(CMD_TICK_TYPE, &tick_type, 0L);
		}
	if(l) free(l);
}

void
Axis::mkTimeAxis()
{
	int nstep, mode;
	double span, val;
	rlp_datetime start, step;
	static char *tick_formats[] = {"y", "Y", "W", "x", "Z.V.", "d", "H:M", "d"};

	span = axis->max - axis->min;
	memset(&step, 0, sizeof(rlp_datetime));		parse_datevalue(&start, axis->Start);
	start.hours=start.minutes=start.doy = 0;	start.seconds = val = 0.0;
	if(span > 60.0) start.dow = 1;
	if(span > 24000.0) {
		step.year = start.month = start.dom = 1;
		nstep = 2 + (int)(span / 364.0);		mode = 0;
		}
	else if(span > 700.0) {
		step.month = start.month = start.dom = 1;
		nstep = 2 + (int)(span / 28.0);			mode = 1;
		}
	else if(span > 300.0) {
		step.month = start.dom = 1;
		nstep = 2 + (int)(span / 28.0);			mode = 2;
		}
	else if(span > 150.0) {
		step.month = start.dom = 1;
		nstep = 2 + (int)(span / 28.0);			mode = 3;
		}
	else if(span > 60.0) {
		step.dom = 1;
		nstep = 6 + (int)(span/7.0);			mode = 4;
		}
	else if(span > 8.0) {
		step.dom = 1;
		nstep = 2+(int)(span);					mode = 5;
		}
	else if(span > 2.0) {
		step.hours = 6;
		nstep = 4+(int)(span*4.0);				mode = 6;
		}
	else if(span > 0.5) {
		step.hours = (span > 0.9 ? 2 : 1);
		nstep = 4+(int)(span*24.0);				mode = 7;
		}
	else if(span > 0.05) {
		step.minutes = (span > 0.3 ? (span > 0.4 ? 30 : 15) : (span <= 0.1 ? 5 : 10));
		nstep = 100;							mode = 8;
		}
	else if(span > 0.005) {
		step.minutes = 1;
		nstep = 100;							mode = 8;
		}
	else return;
	if(nstep < 50) nstep = 50;					add_date(&start, 0L);
	if(!(Ticks = (Tick**)calloc(nstep, sizeof(Tick*))))return;
	for(NumTicks = 0; NumTicks < nstep && val <= axis->max;	NumTicks++) {
		val = date2value(&start);
		switch(mode) {
		case 0:
			if((start.year%10) == 0) SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[0]));
			else NumTicks--;					break;
		case 1:
			if(start.month == 1){
				if(span <= 6000.0 || (span <= 12000.0 && (start.year%5) == 0) || (span <= 24000.0 && (start.year%10) == 0))
					SetTick(NumTicks, val, axis->flags, date2text(&start, 
					(span > 3000.0 && span < 12000.0)?tick_formats[0] : tick_formats[1]));
				else NumTicks--;
				}
			else if(span <= 1500.0) SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, 
				date2text(&start, tick_formats[4]));
			else NumTicks--;					break;
		case 2:
			SetTick(NumTicks, val, axis->flags, date2text(&start, start.month==1 ? tick_formats[0]:tick_formats[2]));
			break;
		case 3:
			SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[3]));
			break;
		case 4:
			if(start.dom == 1)SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[3]));
			else if(start.dow == 1) SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, date2text(&start, tick_formats[4]));
			else NumTicks--;					break;
		case 5:
			if(start.dow == 1) SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[4]));
			else if(span < 30.0)SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, date2text(&start, tick_formats[7]));
			else NumTicks--;					break;
		case 6:
			if(start.hours == 0) SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[4]));
			else SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, date2text(&start, tick_formats[6]));
			break;
		case 7:
			if(start.hours == 0) SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[5]));
			else if((start.hours % 6) == 0) SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[6]));
			else SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, date2text(&start, tick_formats[6]));
			break;
		case 8:
			if(start.minutes == 0){
				if(span <= 0.3 || (start.hours %2) == 0) SetTick(NumTicks, val, axis->flags, 
					date2text(&start, tick_formats[6]));
				else SetTick(NumTicks, val, axis->flags, "");
				}
			else SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, date2text(&start, tick_formats[6]));
			break;
			}
		add_date(&start, &step);
		}
}

void
Axis::CreateTicks()
{
	int i, n, nstep;
	char *format, *tick_label;
	double fVal, tmp;
	DWORD flags;

	if(axis->min == -HUGE_VAL || axis->min == HUGE_VAL) return;
	if(axis->max == -HUGE_VAL || axis->max == HUGE_VAL) return;
	if(axis->min >= axis->max) return;
	if(Ticks) {
		Undo.DropListGO(this, (GraphObj***)&Ticks, &NumTicks, 0L);
		}
	Command(CMD_FLUSH, 0L, 0L);
	if((axis->flags & 0xf000) == AXIS_LOG) {	//log-axis
		if(axis->Start > defs.min4log) tmp = log10(axis->Start);
		else switch (type){
			case 1:								//x axis
			case 2:								//y axis
			case 3:								//z axis
				axis->Start = tmp = log10(base4log(axis, type-1));
				if(axis->Start <= 0.0) axis->Start = 1.0;
				break;
			default: return;
			}
		n = (int)(log10(axis->max) - tmp);
		Ticks = (Tick**)calloc(100, sizeof(Tick*));
		for(NumTicks=0, i=(int)floor(tmp); NumTicks <90; i++){
			SetTick(NumTicks++, pow(10.0, i), axis->flags, 0L);
			if(n < 5) {
				flags = n ? axis->flags | AXIS_MINORTICK : axis->flags;
				SetTick(NumTicks++, 5.0*pow(10.0, i), axis->flags, 0L);
				SetTick(NumTicks++, 3.0*pow(10.0, i), flags, 0L);
				SetTick(NumTicks++, 4.0*pow(10.0, i), flags, 0L);
				SetTick(NumTicks++, 6.0*pow(10.0, i), flags, 0L);
				SetTick(NumTicks++, 7.0*pow(10.0, i), flags, 0L);
				SetTick(NumTicks++, 8.0*pow(10.0, i), flags, 0L);
				SetTick(NumTicks++, 9.0*pow(10.0, i), flags, 0L);
				if(n < 3) SetTick(NumTicks++, 2.0*pow(10.0, i), axis->flags, 0L);
				else SetTick(NumTicks++, 2.0*pow(10.0, i), axis->flags | AXIS_MINORTICK, 0L);
				}
			else {
				SetTick(NumTicks++, 5.0*pow(10.0, i), axis->flags | AXIS_MINORTICK, 0L);
				}
			}
		}
	else {										//linear axis
		if((axis->flags & 0xf000) == AXIS_DATETIME) {
			mkTimeAxis();		return;
			}
		if(atv && (nstep = atv->Count()) && (Ticks = (Tick**)calloc(nstep+1, sizeof(Tick*)))) {
			for(NumTicks = i = n = 0; NumTicks < nstep && atv->GetItem(i, &tick_label, &fVal);	i++) {
				SetTick(NumTicks, fVal, axis->flags, tick_label);
				NumTicks++;		n += (int)strlen(tick_label);
				}
			type = type;
#ifdef _WINDOWS
			if(type == 1 && n > 40) {
				tlbdef.RotBL = n >100 ? 90.0 : 45.0;		tlbdef.Align = TXA_HRIGHT | TXA_VCENTER;
				tlbdist.fy = sizAxTick;						SetSize(SIZE_TLB_YDIST, tlbdist.fy);
				Command(CMD_TLB_TXTDEF, &tlbdef, 0L);
				}
#else
			if(type == 1 && n > 30) {
				tlbdef.RotBL = n >70 ? 90.0 : 45.0;			tlbdef.Align = TXA_HRIGHT | TXA_VCENTER;
				tlbdist.fy = sizAxTick;						SetSize(SIZE_TLB_YDIST, tlbdist.fy);
				Command(CMD_TLB_TXTDEF, &tlbdef, 0L);
				}
#endif
			return;
			}
		if((axis->flags & 0xf000) == AXIS_RECI) {
			NiceStep(axis, 8);
			format = GetNumFormat(floor(log10(axis->Step)));
			fVal = axis->Start;
			}
		else {
			NiceStep(axis, 4);
			format = GetNumFormat(floor(log10(fabs(axis->max - axis->min))));
			fVal = axis->Start;
			}
		nstep = 1+(int)((axis->max-axis->min)/axis->Step);
		Ticks = (Tick**)calloc(nstep+2, sizeof(Tick*));
		if(!Ticks) return;
		for(NumTicks = 0; NumTicks < nstep && fVal <= axis->max;
			NumTicks++, fVal += axis->Step) {
#ifdef USE_WIN_SECURE
			sprintf_s(TmpTxt, TMP_TXT_SIZE, format, fVal);
#else
			sprintf(TmpTxt, format, fVal);
#endif
			SetTick(NumTicks, fVal, axis->flags, TmpTxt);
			}
		}
}

void
Axis::ManuTicks(double sa, double st, int n, DWORD flags)
{
	int j, m;
	char *format;
	double fVal, mival, mist;

	Command(CMD_FLUSH, 0L, 0L);
	mist = st/(double)(n+1);
	m = (int)(((axis->max-axis->min) / st)*(double)(n+1))+n+2;
	format = GetNumFormat(floor(log10(st)));
	Ticks = (Tick**)calloc(m, sizeof(Tick*));
	for(NumTicks = 0, fVal = sa; NumTicks < m && fVal <= axis->max; NumTicks++) {
#ifdef USE_WIN_SECURE
		sprintf_s(TmpTxt, TMP_TXT_SIZE, format, fVal);
#else
		sprintf(TmpTxt, format, fVal);
#endif
		SetTick(NumTicks, fVal, flags, TmpTxt);
		for(j = 0; j < n; j++) {
			mival = fVal+mist*(double)j +mist;
			if(mival < axis->max) {
#ifdef USE_WIN_SECURE
				sprintf_s(TmpTxt, TMP_TXT_SIZE, format, mival);
#else
				sprintf(TmpTxt, format, mival);
#endif
				NumTicks++;
				SetTick(NumTicks, mival, flags | AXIS_MINORTICK, TmpTxt);
				}
			}
		fVal += st;
		}
}

bool
Axis::GetValuePos(double val, double *fix, double *fiy, double *fiz, anyOutput *op)
{
	double temp, tmp1 = 1.0;
	int i;
	bool bRet = true;
	anyOutput *o;
	fPOINT3D p1, p2;
	lfPOINT fdp, fip;
	AxisDEF caxis;

	*fix = *fiy = *fiz = 0.0;
	if(!op || !parent || (val < axis->min && val < axis->max) || 
		(val > axis->min && val > axis->max)) return false;
	for(i = 0; i < axis->nBreaks && bRet; i++) 
		if((val >= axis->breaks[i].fx && val <= axis->breaks[i].fy) ||
			(val <= axis->breaks[i].fx && val >= axis->breaks[i].fy)) bRet = false;
	if(axis->owner == this && scaleOut)	o = scaleOut;
	else o = op;
	if(axis->flags & AXIS_3D) {
		//Get a copy of the axis because GetAxisFac() modifies its contents
		memcpy(&caxis, axis, sizeof(AxisDEF));
		p1.fx = op->un2fix(axis->loc[1].fx - caxis.loc[0].fx);
		p1.fy = op->un2fiy(axis->loc[1].fy - caxis.loc[0].fy);
		p1.fz = op->un2fiz(axis->loc[1].fz - caxis.loc[0].fz);
		tmp1 = sqrt(p1.fx*p1.fx + p1.fy*p1.fy + p1.fz*p1.fz);
		p1.fx /= tmp1;		p1.fy /= tmp1;		p1.fz /= tmp1;
		tmp1 = GetAxisFac(&caxis, tmp1, (type&0xf)-1);
		temp = TransformValue(&caxis, val, true);
		temp = (temp - caxis.min)*tmp1;
		if(axis->flags & AXIS_INVERT) {
			p1.fx = op->fix2un(op->un2fix(axis->loc[1].fx) - p1.fx*temp);
			p1.fy = op->fiy2un(op->un2fiy(axis->loc[1].fy) - p1.fy*temp);
			p1.fz = op->fix2un(op->un2fiz(axis->loc[1].fz) - p1.fz*temp);
			}
		else {
			p1.fx = op->fix2un(p1.fx*temp+op->un2fix(axis->loc[0].fx));
			p1.fy = op->fiy2un(p1.fy*temp+op->un2fiy(axis->loc[0].fy));
			p1.fz = op->fix2un(p1.fz*temp+op->un2fiz(axis->loc[0].fz));
			}
		op->cvec2ivec(&p1, &p2);
		*fix = p2.fx;	*fiy = p2.fy;	*fiz = p2.fz;
		if((p2.fx < (flim[0].fx-1) && p2.fx < (flim[1].fx-1)) ||
			(p2.fx > (flim[0].fx+1) && p2.fx > (flim[1].fx+1)) ||
			(p2.fy < (flim[0].fy-1) && p2.fy < (flim[1].fy-1)) ||
			(p2.fy > (flim[0].fy+1) && p2.fy > (flim[1].fy+1)) ||
			(p2.fz < (flim[0].fz-1) && p2.fz < (flim[1].fz-1)) ||
			(p2.fz > (flim[0].fz+1) && p2.fz > (flim[1].fz+1))) bRet = false;
		return bRet;
		}
	else if(axis->flags & AXIS_ANGULAR) {
		fdp.fx = val;				fdp.fy = parent->GetSize(SIZE_BOUNDS_TOP);
		op->fp2fip(&fdp, &fip);		*fix = fip.fx;			*fiy = fip.fy;
		return bRet;
		}
	else if((type & 0x0f) == 1 && fabs(flim[1].fx-flim[0].fx)>10.0) {	//x dominant
		tmp1 = (o->fx2fix(val)-flim[0].fx)/(flim[1].fx-flim[0].fx);
		*fix = flim[0].fx - tmp1*(flim[0].fx - flim[1].fx);
		*fiy = flim[0].fy - tmp1*(flim[0].fy - flim[1].fy);
		}
	else if(((type & 0x0f) == 2  || (type &0x0f) == 4) && fabs(flim[1].fy-flim[0].fy)>10.0){	//y dominant
		tmp1 = (o->fy2fiy(val)-flim[1].fy)/(flim[0].fy-flim[1].fy);
		*fix = flim[1].fx + tmp1*(flim[0].fx - flim[1].fx);
		*fiy = flim[1].fy + tmp1*(flim[0].fy - flim[1].fy);
		}
	else return false;
	if(tmp1 > -1.0e-15 && tmp1 < 1.0 + 1.0e-15) return bRet;
	return false;
}

void 
Axis::BreakSymbol(POINT3D *p1, double dsi, double dcsi, bool connect, anyOutput *o)
{
	POINT *sym = 0L, *csym = 0L;
	static POINT lp;
	int j, n = 0;
	double tmp;

	switch (brksym){
	case 2:
		n = o->un2ix(brksymsize);
		if(!(sym = (POINT*)calloc(2, sizeof(POINT))))return;
		if(!(csym = (POINT*)calloc(2, sizeof(POINT)))){
			free(sym);
			return;
			}
		sym[0].x = (int)((-(n>>1))*dsi) + (int)((n>>2)*dcsi); 
		sym[0].y = (int)((-(n>>1))*dcsi) + (int)((n>>2)*dsi);
		sym[1].x = (int)((n>>1)*dsi) - (int)((n>>2)*dcsi); 
		sym[1].y = (int)((n>>1)*dcsi) - (int)((n>>2)*dsi);
		n = 2;
		break;
	case 3:
		n = o->un2ix(brksymsize);
		if(!(sym = (POINT*)calloc(2, sizeof(POINT))))return;
		if(!(csym = (POINT*)calloc(2, sizeof(POINT)))){
			free(sym);
			return;
			}
		sym[0].x = (int)((-(n>>1))*dsi); 
		sym[0].y = (int)((-(n>>1))*dcsi);
		sym[1].x = (int)((n>>1)*dsi); 
		sym[1].y = (int)((n>>1)*dcsi);
		n = 2;
		break;
	case 4:
		n = o->un2ix(brksymsize);
		if(!(sym = (POINT*)calloc(n, sizeof(POINT))))return;
		if(!(csym = (POINT*)calloc(n, sizeof(POINT)))){
			free(sym);
			return;
			}
		for(j = 0; j< n; j++) {
			tmp = (double)j*6.283185307/(double)n;
			tmp = sin(tmp)*(double)n/6.0;
			sym[j].x = (int)((j-(n>>1))*dsi) + (int)(tmp*dcsi); 
			sym[j].y = (int)((j-(n>>1))*dcsi) + (int)(tmp*dsi);
			}
		break;
		}
	if(sym && csym && n) {
		if(brksym == 3 && connect) {
			csym[0].x = lp.x;				csym[0].y = lp.y;
			csym[1].x = sym[0].x + p1->x;	csym[1].y = sym[0].y + p1->y;
			o->oPolyline(csym, 2);
			}
		for(j = 0; j < n; j++) {
			csym[j].x = sym[j].x + p1->x;
			csym[j].y = sym[j].y + p1->y;
			}
		o->oPolyline(csym, n);
		lp.x = csym[n-1].x;				lp.y = csym[n-1].y;
		}
	if(sym) free(sym);			if(csym) free(csym);
}

void
Axis::DrawBreaks(anyOutput *o)
{
	fPOINT3D *pts, np, tmp_p;
	double dx, dy, dz, d, dn, da, lsi, lcsi;
	POINT pbs[2];
	int i, j;

	dx = flim[0].fx > flim[1].fx ? flim[1].fx : flim[0].fx;
	dy = flim[0].fy > flim[1].fy ? flim[1].fy : flim[0].fy;
	dz = flim[0].fz > flim[1].fz ? flim[0].fz + (flim[0].fz - flim[1].fz)*50.0 : 
		flim[1].fz + (flim[1].fz - flim[0].fz)*50.0;
	if(axis->flags & AXIS_3D){
		if(!(l_segs = (line_segment**)calloc(2+axis->nBreaks, sizeof(line_segment*))))return;
		nl_segs = 0;
		}
	if(!(pts = (fPOINT3D*)calloc(2+axis->nBreaks, sizeof(fPOINT3D))))return;
	memcpy(pts, &flim[0], sizeof(fPOINT3D));
	for (i = 1; i < (2+axis->nBreaks); i++) {
		switch (i) {
		case 1:
			memcpy(&np, &flim[1], sizeof(fPOINT3D));
			break;
		default:
			GetValuePos(axis->breaks[i-2].fx, &np.fx, &np.fy, &np.fz, o);
			break;
			}
		for(j = 0; j < i; j++) {
			dn = (d = np.fx-dx) * d;		dn += ((d = np.fy-dy) * d);
			dn += ((d = np.fz-dz) * d);		da = (d = pts[j].fx-dx) * d;		
			da += ((d = pts[j].fy-dy)*d);	da += ((d = pts[j].fz-dz)*d);
			if(dn < da) {
				memcpy(&tmp_p, &pts[j], sizeof(fPOINT3D));
				memcpy(&pts[j], &np, sizeof(fPOINT3D));
				memcpy(&np, &tmp_p, sizeof(fPOINT3D));
				}
			}
		memcpy(&pts[i], &np, sizeof(fPOINT3D));
		}
	for(i = 1; i < (2+axis->nBreaks); i++) {
		dn = (d = pts[i].fx-pts[i-1].fx) * d;		dn += ((d = pts[i].fy-pts[i-1].fy) * d);
		dn += ((d = pts[i].fz-pts[i-1].fz) * d);	dn = sqrt(dn);
		da = o->un2fix(brkgap/2.0);
		if(dn > 0.01) {
			np.fx = da * (pts[i].fx-pts[i-1].fx)/dn;
			np.fy = da * (pts[i].fy-pts[i-1].fy)/dn;
			np.fz = da * (pts[i].fz-pts[i-1].fz)/dn;
			d = (pts[i].fx - pts[i-1].fx) * (pts[i].fx - pts[i-1].fx);
			d += ((pts[i].fy - pts[i-1].fy) * (pts[i].fy - pts[i-1].fy));
			d = sqrt(d);			if(d < 1.0) d = 1.0;
			lsi = (pts[i].fy - pts[i-1].fy)/d;
			lcsi = (pts[i].fx -pts[i-1].fx)/d;
			if(i == 1) {
				pts3D[0].x = pbs[0].x = iround(pts[i-1].fx);
				pts3D[0].y = pbs[0].y = iround(pts[i-1].fy);
				pts3D[0].z = iround(pts[i-1].fz);
				}
			else {
				pts3D[0].x = pbs[0].x = iround(pts[i-1].fx + np.fx);
				pts3D[0].y = pbs[0].y = iround(pts[i-1].fy + np.fy);
				pts3D[0].z = iround(pts[i-1].fz + np.fz);
				BreakSymbol(&pts3D[0], lsi, -lcsi, true, o);
				}
			if(i == (1+axis->nBreaks)) {
				pts3D[1].x = pbs[1].x = iround(pts[i].fx);
				pts3D[1].y = pbs[1].y = iround(pts[i].fy);
				pts3D[1].z = iround(pts[i].fz);
				}
			else {
				pts3D[1].x = pbs[1].x = iround(pts[i].fx - np.fx);
				pts3D[1].y = pbs[1].y = iround(pts[i].fy - np.fy);
				pts3D[1].z = iround(pts[i].fz - np.fz);
				BreakSymbol(&pts3D[1], lsi, -lcsi, false, o);
				}
			if(axis->flags & AXIS_3D) {
				if(l_segs[nl_segs] = new line_segment(this, data, &axline, &pts3D[0], &pts3D[1])){
					l_segs[nl_segs]->DoPlot(o);			nl_segs++;
					}
				}
			else o->oSolidLine(pbs);
			}
		}
	free(pts);
}

void 
Axis::UpdateTicks()
{
	int i, j, k, l;
	double tmpval;
	AccRange *rT=0L, *rL=0L, *rMT=0L;

	if(!ssMATval || !(rT = new AccRange(ssMATval))) return;
	if(Ticks) {
		Undo.DropListGO(this, (GraphObj***)&Ticks, &NumTicks, 0L);
		}
	if(ssMATlbl) rL = new AccRange(ssMATlbl);
	if(ssMITval) rMT = new AccRange(ssMITval);
	if(!(Ticks = ((Tick**)calloc(rT->CountItems()+ (( rMT != 0L) ? rMT->CountItems() : 0) +4,
		sizeof(Tick*))))) return;
	rT->GetFirst(&i, &j);		if(!(rT->GetNext(&i, &j)))return;
	if(rL) rL->GetFirst(&k, &l);
	NumTicks =0;
	do{
		if(rL) rL->GetNext(&k, &l);
		if(data->GetValue(j, i, &tmpval)) {
			if(!(Ticks[NumTicks] = new Tick(this, data, tmpval, axis->flags)))return;
			if(rL) {
				if(Ticks[NumTicks] && data->GetText(l, k, TmpTxt, TMP_TXT_SIZE)) 
					Ticks[NumTicks]->Command(CMD_SETTEXT, TmpTxt, 0L);
				}
			Ticks[NumTicks]->SetSize(SIZE_TICK_ANGLE, tick_angle);
			Ticks[NumTicks]->Command(CMD_TICK_TYPE, &tick_type, 0L);
			}
		NumTicks++;
		}while(rT->GetNext(&i, &j));
	if(rMT) {
		if(rMT->GetFirst(&i, &j) && rMT->GetNext(&i, &j)) do {
			if(data->GetValue(j, i, &tmpval) &&	0L!=(Ticks[NumTicks] = new Tick(this, data, 
				tmpval, axis->flags | AXIS_MINORTICK))) {
				Ticks[NumTicks]->SetSize(SIZE_TICK_ANGLE, tick_angle);
				Ticks[NumTicks]->Command(CMD_TICK_TYPE, &tick_type, 0L);
				NumTicks++;
				}
			}while(rMT->GetNext(&i, &j));
		}
	Command(CMD_TLB_TXTDEF, &tlbdef, 0L);	SetSize(SIZE_TLB_XDIST, tlbdist.fx);
	SetSize(SIZE_TLB_YDIST, tlbdist.fy);
	if(rT) delete(rT);	if(rL) delete(rL);	if(rMT) delete(rMT);
}

void
Axis::GradientBar(anyOutput *o)
{
	FillDEF gf = {0, 0x0, 1.0, 0L, 0x0};
	LineDEF gl = {0.0, 1.0, 0x0, 0x0};
	POINT gpt[5];
	double v_val, fix, fiy, fiz;
	int i, iw;

	if(!o || !Ticks || NumTicks < 2) return;
	iw = o->un2ix(defs.GetSize(SIZE_AXIS_TICKS)*2);
	GetValuePos(axis->min, &fix, &fiy, &fiz, o);
	gpt[0].x = gpt[3].x = gpt[4].x = (iround(fix)-iw);
	gpt[0].y = gpt[1].y = gpt[4].y = iround(fiy);
	memcpy(&gradient_box, &gpt, sizeof(POINT)*5);		
	gf.color = gf.color2 = gl.color = GradColor(axis->min);
	for(i = 0; i < NumTicks; i++) if(Ticks[i]) {
		if(GetValuePos(v_val = Ticks[i]->GetSize(SIZE_MINE), &fix, &fiy, &fiz, o)) {
			gpt[1].x = gpt[2].x = iround(fix);		gpt[2].y = gpt[3].y = iround(fiy);
			o->SetLine(&gl);	o->SetFill(&gf);	
			if(gpt[1].y != gpt[2].y) o->oPolygon(gpt, 5, 0L);
			gpt[0].x= gpt[3].x= gpt[4].x= (pts[1].x-iw);	gpt[0].y = gpt[1].y = gpt[4].y = gpt[2].y;
			gf.color = gf.color2 = gl.color = GradColor(v_val);
			}
		}
	if(GetValuePos(axis->max, &fix, &fiy, &fiz, o)) {
		gpt[1].x = gpt[2].x = gradient_box[1].x = gradient_box[2].x = iround(fix);
		gpt[2].y = gpt[3].y = gradient_box[2].y = gradient_box[3].y = iround(fiy);
		o->SetLine(&gl);	o->SetFill(&gf);	
		if(gpt[1].y != gpt[2].y) {
			o->oPolygon(gpt, 5, 0L);
			}
		UpdateMinMaxRect(&rDims, gpt[0].x, gpt[1].y);
		o->SetLine(&axline);
		o->oPolyline(gradient_box, 5);
		}
	else o->SetLine(&axline);
}
