// Cartesian.H,v 0.9
//
// Copyright 2000 by Roman Kantor.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public License
// version 2 as published by the Free Software Foundation.
//
// This library is distributed  WITHOUT ANY WARRANTY;
// WITHOUT even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.


#include "Cartesian.H"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <FL/fl_draw.H>
#include <FL/Fl.H>
#include <FL/Fl_Group.H>




static const int		CANVAS_BORDER = 0;                //gap between the graphics and surrounding"box"
static const int        AXIS_BORDER = 0;                  //gap between axis drawing(i.e.axis line) and its "box"
static const int        MINOR_INTERVAL = 0;               //0 stands for automatic choice in default_*_intervals array
static const int        MINOR_SEPARATION = 18;            
static const int        MAJOR_STEP = 5;
static const int        LABEL_STEP = 10;
static const int        LABEL_SIZE = CA_DEFAULT_LABEL_SIZE;
static const Fl_Font    LABEL_FONT = FL_HELVETICA;


static const int MAX_LABEL_FORMAT = 16;
static const int MAX_LABEL_LENGTH = 32;


static const int NO_LIN_DEFAULTS=3;
static const double default_lin_intervals[NO_LIN_DEFAULTS] = {1, 2, 5};
static const int default_lin_major_steps[NO_LIN_DEFAULTS] = {5, 5, 2};
static const int default_lin_label_steps[NO_LIN_DEFAULTS] = {10, 5, 4};

static const int NO_LOG_DEFAULTS = 3;
static const double default_log_intervals[NO_LOG_DEFAULTS] = {1, 2, 5};
static const int default_log_major_steps[NO_LOG_DEFAULTS] = {5, 5, 2};
static const int default_log_label_steps[NO_LOG_DEFAULTS] = {10, 5, 2};



/// float drawings for more precise placement (especialy for PS output for Fl_Device!) /////

static inline void ca_rect(double x, double y, double w, double h){
	fl_begin_loop();
	fl_vertex(x,y);
	fl_vertex(x+w,y);
	fl_vertex(x+w,y+h);
	fl_vertex(x,y+h);
	fl_end_loop();
};

static inline void ca_rectf(double x, double y, double w, double h){
	fl_begin_polygon();
	fl_vertex(x,y);
	fl_vertex(x+w,y);
	fl_vertex(x+w,y+h);
	fl_vertex(x,y+h);
	fl_end_polygon();
};

static inline void ca_loop(double x1, double y1, double x2, double y2, double x3, double y3){
	fl_begin_loop();
	fl_vertex(x1,y1); fl_vertex(x2,y2); fl_vertex(x3,y3);
	fl_end_loop();
};

static inline void ca_polygon(double x1, double y1, double x2, double y2, double x3, double y3){
	fl_begin_polygon();
	fl_vertex(x1,y1); fl_vertex(x2,y2); fl_vertex(x3,y3);
	fl_end_polygon();
};

static inline void ca_loop(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4){
	fl_begin_loop();
	fl_vertex(x1,y1); fl_vertex(x2,y2); fl_vertex(x3,y3); fl_vertex(x4,y4);
	fl_end_loop();
};

static inline void ca_polygon(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4){
	fl_begin_polygon();
	fl_vertex(x1,y1); fl_vertex(x2,y2); fl_vertex(x3,y3); fl_vertex(x4,y4);
	fl_end_polygon();
};

static inline void ca_text(const char  *label, double x, double y){
	fl_draw(label,(int)(x+.5),(int)(y+.5));
};
static inline void ca_point(double x, double y){
	fl_point((int)(x+.5),(int)(y+.5));
};

/*
static inline void ca_pie(double x, double y, double w, double h, double a1, double a2){
	fl_pie((int)(x+.5), (int)(y+.5),(int)(w+.5),(int)(h+.5),0,270.0);
};
*/

static inline void ca_filled_circle(double x, double y, double r){
	fl_begin_polygon();
	//fl_arc(x,y,r,0,360);
	fl_circle(x,y,r);
	fl_end_polygon();
};

static inline void ca_text(const char  *label, double x, double y, double w, double h, Fl_Align align){
	fl_draw(label, (int)(x+.5), (int)(y+.5), (int)(w+.5), (int)(h+.5), align);
};






////////////////////    Ca_Axis_    ////////////////////////////

void Ca_Axis_::minimum(double x){
    min_=x;
    if(!valid_){
        max_=x;
        valid_=1;
    }
    damage(CA_DAMAGE_ALL);
    canvas_->damage(CA_DAMAGE_ALL);
	update();

};

void Ca_Axis_::maximum(double x){
    max_=x;
    if(!valid_){
        min_=x;
        valid_=1;
    }
    damage(CA_DAMAGE_ALL);
    canvas_->damage(CA_DAMAGE_ALL);
	update();

};


int Ca_Axis_::update(){

    double _k=k_;
	double _q=q_;
    min_pos_=min_pos();
    max_pos_=max_pos();
    if (min_==max_)
        k_=0;
    else
		if(scale_ & CA_LOG){
			k_=(max_pos_-min_pos_)/(log(max_)-log(min_));
			q_=min_pos_-k_*log(min_);
		}else{
			k_=(max_pos_-min_pos_)/(max_-min_);
			q_=min_pos_;
		}

    if((_k!=k_)||(_q!=q_))
        return 1;
    else
        return 0;
};

void Ca_Axis_::rescale_move(int when, double  x){
    
	
    if((when&CA_WHEN_MAX)&&(x>max_)){
		if(scale_ & CA_LOG)
			min_ *=x/max_;
		else
			min_ += x-max_;
		max_=x;
        damage(CA_DAMAGE_ALL);
        canvas_->damage(CA_DAMAGE_ALL);
        
    }
    if((when&CA_WHEN_MIN)&&(x<min_)){
		if(scale_ & CA_LOG)
			max_ *=x/min_;
        else
			max_ -= min_-x;
        min_=x;
        damage(CA_DAMAGE_ALL);
        canvas_->damage(CA_DAMAGE_ALL);
    }
    valid_=1;
};

double Ca_Axis_::position(double value){

    if (k_==0) return (min_pos_+max_pos_)/2;
	if(scale_ & CA_LOG)
		return (int)(q_+k_*log(value));
	else
		return min_pos_+k_*(value-min_);
};

double Ca_Axis_::value(double pos){
    if (max_==min_)
        return min_;
	if(scale_ & CA_LOG)
		return exp(min_ +(pos-q_)/k_);
	else
		return (min_ +(pos-min_pos_)/k_);
};



int Ca_Axis_::next_tick(int &tick_index, double &tick_value, int &tick_order, double &interval ){
        

	////////// I know snakes are evil creatures, but sometimes they work so there is such a serpent....
	////////// How many if...else can be in in a function? this is going to be a record in the G. book of r.

	static int number_per_order;
    double _tick_interval;
    double minor_number_;


	if(scale_ & CA_LOG){   /////////////     begin logarithmic   /////////////////
		if (!interval){
            tick_order=(int)(floor(log10(min_)));
            if (tick_interval_!=0){
                interval=fabs(tick_interval_);
                number_per_order=(int)floor(10/interval+0.5);
            }else{
                number_per_order=(int)(abs(min_pos_-max_pos_)/(tick_separation_*log10(max_/min_)));
                if(number_per_order<=1){
                    label_step_=major_step_=3;
                    tick_order = 3*(tick_order/3);
                    interval=1;
                    number_per_order=0;
                }else{
                    int _no_per_o=number_per_order;
                    for(int i=NO_LOG_DEFAULTS-1;i>=0;i--){
                        major_step_=default_log_major_steps[i];
                        label_step_=default_log_label_steps[i];
                        interval=default_log_intervals[i];
                        number_per_order=(int)floor(10/interval+0.5);
                        if((10/interval)>=_no_per_o)
                            break;
                    }
                }
            }
            tick_index=number_per_order;
            tick_order--;
            tick_value=pow(10,tick_order);
            interval*=tick_value;
            
            if(!number_per_order){
                tick_order--;
                tick_value /=10;
                tick_index=1;
            }else
                tick_value *=10;
            return 1;
        }else{
            if (tick_value>(max_)){
                tick_index-=1;
                return 0;
            }else{
                if(number_per_order){
                    if(tick_index==number_per_order){
                        tick_order++;
                        interval*=10;
                        if(number_per_order<10){
                            tick_index=1;
                            tick_value=interval;
                        }else{
                            tick_index=2;
                            tick_value=2*interval;
                        }
                    }else{
                        tick_value +=interval;
                        tick_index++;
                    }
                }else{
                    tick_order++;
                    tick_index++;
                    tick_value *=10;
                }
                return 1;
            }
        }

	}else{     ///////////////     begin linear       //////////////////////
        if (!interval){
            minor_number_= (double)abs(min_pos_-max_pos_)/(double)tick_separation_;
            _tick_interval=tick_interval_;
            if (tick_interval_<0){
                interval=_tick_interval=-_tick_interval;
                tick_order=(int)floor(log10(_tick_interval));
            }else{
                if(_tick_interval!=0){
                    tick_order=(int)floor(log10(fabs(max_-min_)/minor_number_));
                    interval= pow(10,tick_order) * _tick_interval;
                }else
                    for(int i=NO_LIN_DEFAULTS-1;i>=0;i--){
                        tick_order=(int)floor(log10(fabs(max_-min_)/minor_number_));
                        interval= pow(10,tick_order)*(_tick_interval=default_lin_intervals[i]);
                        major_step_=default_lin_major_steps[i];
                        label_step_=default_lin_label_steps[i];
                        if(((max_-min_)/interval)>=minor_number_)
                            break;
                    }
            }
            tick_value = floor(minimum()/interval);
            tick_value *= interval;
            tick_index=(int) floor((tick_value /interval)+0.5);
            return 1;
        }else{
            if (tick_value>(max_)){
                tick_index=-1;
                return 0;
            }else{
                tick_value +=interval;
                tick_index++;
                return 1;
            }
        }
	}		/////   Uf, this is the end of the leg-less beast!   //////
};



void Ca_Axis_::rescale(int when, double  x){
    if(!valid_){
        max_=x;
        min_=x;
        damage(CA_DAMAGE_ALL);
        canvas_->damage(CA_DAMAGE_ALL);
        valid_=1;
        return;
    }

    if((when&CA_WHEN_MAX)&&(x>max_)){
        max_=x;
        damage(CA_DAMAGE_ALL);
        canvas_->damage(CA_DAMAGE_ALL);
    }
    if((when&CA_WHEN_MIN)&&(x<min_)){
        min_=x;
        damage(CA_DAMAGE_ALL);
        canvas_->damage(CA_DAMAGE_ALL);
    }
};

Ca_Axis_::Ca_Axis_(int x, int y, int w, int h, const char * label)
	:Fl_Box(x,y,w,h,label),
	scale_(CA_LIN), valid_(0), label_format_(0),
	minor_grid_color_(FL_BLACK), major_grid_color_(FL_BLACK), label_grid_color_(FL_BLACK),
	minor_grid_style_(FL_SOLID), major_grid_style_(FL_SOLID), label_grid_style_(FL_SOLID),
	minor_grid_width_(0), major_grid_width_(0), label_grid_width_(0), 
	minor_grid_dashes_(0), major_grid_dashes_(0),label_grid_dashes_(0),
	grid_visible_(0), tick_interval_(MINOR_INTERVAL), tick_separation_(MINOR_SEPARATION),
	tick_length_(0), tick_width_(0), major_step_(MAJOR_STEP),label_step_(LABEL_STEP),
	axis_align_(CA_BOTTOM),label_font_face_(FL_HELVETICA), label_font_size_(LABEL_SIZE),
	min_(0),max_(0),min_pos_(0),max_pos_(0),border_(AXIS_BORDER),axis_color_(FL_BLACK)
	
{
	box(FL_NO_BOX);
    canvas_=Ca_Canvas::current();
    previous_axis_=canvas_->last_axis_;
    canvas_->last_axis_=this;
	labelsize(LABEL_SIZE);
};

Ca_Axis_::~Ca_Axis_(){
	
	if(!canvas_)return;
    if (canvas_->last_axis_==this)
        canvas_->last_axis_=previous_axis_;
    else{
        Ca_Axis_ *axis=canvas_->last_axis_;
        while(axis){
            if(axis->previous_axis_==this){
                axis->previous_axis_=previous_axis_;
                break;
            }
			axis=axis->previous_axis_;
		}
    };
};



///////////////////////  Ca_X_Axis  ///////////////////////////////////////////////

int Ca_X_Axis::min_pos(){
	if(scale_&CA_REV)
		return canvas_->x()+canvas_->w()-canvas_->border()+Fl::box_dx(canvas_->box())-Fl::box_dw(canvas_->box());
	else
		return canvas_->x()+canvas_->border()+Fl::box_dx(canvas_->box());
};

int Ca_X_Axis::max_pos(){
	if(scale_&CA_REV)
		return canvas_->x()+canvas_->border()+Fl::box_dx(canvas_->box());
	else
		return canvas_->x()+canvas_->w()-canvas_->border()+Fl::box_dx(canvas_->box())-Fl::box_dw(canvas_->box());
		
};
		

void Ca_X_Axis::draw(){
    int tick_index=-1;
    double tick_value;
    int tick_order;//, tick_number;
    double _interval=0;
    const char * label_format=label_format_;
    if(damage()|FL_DAMAGE_ALL)
        draw_label();
    if (damage()&(FL_DAMAGE_ALL|CA_DAMAGE_ALL)){
        update();
		if (box()==FL_NO_BOX){
            fl_color(parent()->color());
            fl_rectf(x(),y(),w(),h());
        }else
			draw_box();
        if(!valid_) return;
        fl_font(label_font_face_,label_font_size_);
        int a, b, l1, l2, m1, m2, l ,_y,_w,_h; //temporary coordinates for ticks
        double _pos,_x;
        fl_clip(x()+Fl::box_dx(box()),y()+Fl::box_dy(box()),w()-Fl::box_dw(box()),h()-Fl::box_dh(box()));
		fl_color(axis_color_);
        a=y()+Fl::box_dh(box())+border_;
        b=a+h()-Fl::box_dh(box())-2*border_;
		
        switch(axis_align_ & CA_ALIGNMENT){
            case CA_BOTTOM:
                l=l1=m1=a;
				if(axis_align_&CA_NO_TICS)
					m2=m1;
				else
					if (tick_length_)
						m2=m1+tick_length_;
					else
						m2=m1+label_font_size_;
                l2=(m1+m2)/2;
                break;
            case CA_TOP:
                l=l2=m2=b-1;
				if(axis_align_&CA_NO_TICS)
					m1=m2;
				else
					if (tick_length_)
						m1=m2-tick_length_;
					else
						m1=m2-label_font_size_;
                l1=(m1+m2)/2;
                break;
            case CA_CENTER:
                m1=a;
                m2=b;
                l=(a+b)/2;
                l1=(a+l)/2;
                l2=(l+b)/2;
                break;
            default:
                m1=0;
                m2=0;
                l=0;
                l1=0;
                l2=0;
                break;
        }
		fl_line_style(FL_SOLID|FL_CAP_FLAT,tick_width_);
        if(axis_align_ & CA_LINE){
			fl_begin_line();
			fl_vertex(min_pos(),l);
			fl_vertex(max_pos(),l);
			fl_vertex(canvas_->x()+Fl::box_dx(canvas_->box()),l);
			fl_vertex(canvas_->x()+canvas_->w()+Fl::box_dx(canvas_->box())-Fl::box_dw(canvas_->box()),l);
			//fl_vertex(x()+border_,l);
			//fl_vertex(x()+w()-border_,l);
			fl_end_line();
		}
        while(next_tick(tick_index, tick_value, tick_order, _interval)){
			_pos=position(tick_value);
			if(scale_&CA_REV){
				if(_pos<max_pos_-canvas_->border()) break;
				if(_pos>min_pos_+canvas_->border()) continue;
			}else{
				if(_pos<min_pos_-canvas_->border()) continue;
				if(_pos>max_pos_+canvas_->border()) break;
			}
			if(!(axis_align_&CA_NO_TICS)){
				if(tick_index % major_step_){
					fl_begin_loop();
					fl_vertex(_pos,l1);
					fl_vertex(_pos,l2);
					fl_end_loop();
				}else{
					fl_begin_loop();
					fl_vertex(_pos,m1);
					fl_vertex(_pos,m2);
					fl_end_loop();
				}
			}
                
            if(!((tick_index % label_step_)|(axis_align_&CA_NO_LABELS))){
                char label[MAX_LABEL_LENGTH];
                char _label_format[MAX_LABEL_FORMAT];
                if(!label_format){
                    int _tick_order;
                    if (tick_order>=0)
                        _tick_order=0;
                    else
                        _tick_order=-tick_order;
                    sprintf(_label_format,"%s.%if","%",_tick_order);
                }
                else
                    strcpy(_label_format,label_format);
                sprintf(label, _label_format,tick_value);
                fl_measure(label,_w,_h);
                _x=_pos-_w/double(2);
                switch (axis_align_ & CA_ALIGNMENT){
                    case CA_TOP:
                        _y=m1-_h/3;
                        break;
                    case CA_BOTTOM:
                        _y=m2+_h;
                        break;
                    case CA_CENTER:
                        _y=l+_h/3;
                        {
                            Fl_Color _color=fl_color();
                            fl_color(color());
                            ca_rectf(_x-_h/6,l-_h/2,_w+_h/3,_h);
                            fl_color(_color);
                        }
                        break;
                    default:
                        _y=0;
                        break;
                }
                ca_text(label,_x,_y);
            }
        }
		fl_line_style(0,0);
        fl_pop_clip();
    }
};


void Ca_X_Axis::current(){
    canvas_->current_x(this);
};

void Ca_X_Axis::draw_grid(){
    if(!valid_)return;
    int tick_index=-1;
    double tick_value;
    int tick_order;
    double _interval=0;
    int l1,l2;
	double _pos;

    l1=canvas_->y()+Fl::box_dy(canvas_->box());
    l2=canvas_->y()+canvas_->h()+Fl::box_dy(canvas_->box())-Fl::box_dh(canvas_->box());
	int tcl;
	if(!(tcl=tick_length_))
		tcl=label_font_size_;
    while(next_tick(tick_index, tick_value, tick_order, _interval)){
		_pos=position(tick_value);
		if(scale_&CA_REV){
			if(_pos<max_pos_-canvas_->border()) break;
			if(_pos>min_pos_+canvas_->border()) continue;
		}else{
			if(_pos<min_pos_-canvas_->border()) continue;
			if(_pos>max_pos_+canvas_->border()) break;
		}
		int grt;
        if((grt=grid_visible_ & CA_LABEL_GRID) && !(tick_index % label_step_)){
			fl_color(label_grid_color_);
            fl_line_style(label_grid_style_,label_grid_width_);
			if(grt==CA_LABEL_GRID){
				fl_begin_loop();
				fl_vertex(_pos,l1);
				fl_vertex(_pos,l2);
				fl_end_loop();

			}else{
				if(grt&CA_LEFT_LABEL_TICK){
					fl_begin_loop();
					fl_vertex(_pos,l1);
					fl_vertex(_pos,l1+tcl);
					fl_end_loop();
				}
				if(grt&CA_RIGHT_LABEL_TICK){
					fl_begin_loop();
					fl_vertex(_pos,l2-tcl);
					fl_vertex(_pos,l2);
					fl_end_loop();
				}
			}
        }else if((grt=grid_visible_ & CA_MAJOR_GRID) && !(tick_index % major_step_)){
			fl_color(major_grid_color_);
            fl_line_style(major_grid_style_,major_grid_width_);
			if(grt==CA_MAJOR_GRID){
				fl_begin_loop();
				fl_vertex(_pos,l1);
				fl_vertex(_pos,l2);
				fl_end_loop();

			}else{
				if(grt&CA_LEFT_MAJOR_TICK){
					fl_begin_loop();
					fl_vertex(_pos,l1);
					fl_vertex(_pos,l1+tcl);
					fl_end_loop();
				}
				if(grt&CA_RIGHT_MAJOR_TICK){
					fl_begin_loop();
					fl_vertex(_pos,l2-tcl);
					fl_vertex(_pos,l2);
					fl_end_loop();
				}
			}
        }else if((grt=grid_visible_&CA_MINOR_GRID)){
			fl_color(minor_grid_color_);
            fl_line_style(minor_grid_style_,minor_grid_width_);
			if(grt==CA_MINOR_GRID){
				fl_begin_loop();
				fl_vertex(_pos,l1);
				fl_vertex(_pos,l2);
				fl_end_loop();

			}else{
				if(grt&CA_LEFT_MINOR_TICK){
					fl_begin_loop();
					fl_vertex(_pos,l1);
					fl_vertex(_pos,l1+tcl/2);
					fl_end_loop();
				}
				if(grt&CA_RIGHT_MINOR_TICK){
					fl_begin_loop();
					fl_vertex(_pos,l2-tcl/2);
					fl_vertex(_pos,l2);
					fl_end_loop();
				}
			}
        }
    }
	fl_line_style(0,0);
    fl_color(FL_BLACK);
};



Ca_X_Axis::Ca_X_Axis(int x, int y, int w, int h, const char *label):Ca_Axis_(x, y, w,  h,  label){
    if(!(canvas_->current_x()))
        current();
};


Ca_X_Axis::~Ca_X_Axis(){
	if(canvas_){
		Ca_ObjectChain *ochain=canvas_->first_object_;
		Ca_ObjectChain *next;
		Ca_ObjectChain *previous=0;
		while (ochain){
			next=ochain->next;
			if(ochain->object->x_axis_==this){
	            delete ochain->object;
				if(previous)
					previous->next=next;
				else
					canvas_->first_object_=next;
				delete ochain;
			}
			ochain=next;
		}
	}
}
   


////////////////////////////   Ca_Y_Axis  //////////////////////////////////////////////////////    

int Ca_Y_Axis::min_pos(){
	if (scale_&CA_REV)
		return canvas_->y()+canvas_->border()+Fl::box_dy(canvas_->box());
	else
		return canvas_->y()+canvas_->h()-canvas_->border()+Fl::box_dy(canvas_->box())-Fl::box_dh(canvas_->box());
		
};

int Ca_Y_Axis::max_pos(){
	if (scale_&CA_REV)
		return canvas_->y()+canvas_->h()-canvas_->border()+Fl::box_dy(canvas_->box())-Fl::box_dh(canvas_->box());
	else
		return canvas_->y()+canvas_->border()+Fl::box_dy(canvas_->box());
		
		
};

void Ca_Y_Axis::draw(){
    int tick_index=-1;
    double tick_value;
    int tick_order;//,tick_number;
    double _interval=0;
    const char * label_format=label_format_;
//    if(damage()|FL_DAMAGE_ALL)
//        draw_label();
    if (damage()&(FL_DAMAGE_ALL|CA_DAMAGE_ALL)){
        update();
        if (box()==FL_NO_BOX){
            fl_color(parent()->color());
            fl_rectf(x(),y(),w(),h());
        }else
            draw_box();
        if(!valid_) return;
        fl_font(label_font_face_,label_font_size_);
        int a, b, l1, l2, m1, m2, l,_x,_w,_h; //temporary coordinates for ticks
        double _pos,_y;
        fl_clip(x()+Fl::box_dx(box()),y()+Fl::box_dy(box()),w()-Fl::box_dw(box()),h()-Fl::box_dh(box()));
		fl_color(axis_color_);
        a=x()+Fl::box_dx(box())+border_;
        b=a+w()-Fl::box_dw(box())-2*border_;
        switch(axis_align_ & CA_ALIGNMENT){
            case CA_RIGHT:
                l=l1=m1=a;
				if(axis_align_&CA_NO_TICS)
					m2=m1;
				else
					if (tick_length_)
						m2=m1+tick_length_;
					else
						m2=m1+label_font_size_;
                l2=(l1+m2)/2;
                break;
            case CA_LEFT:
                l=l2=m2=b-1;
				if(axis_align_&CA_NO_TICS)
					m1=m2;
				else
					if (tick_length_)
						m1=m2-tick_length_;
					else
						m1=m2-label_font_size_;
                l1=(m1+m2)/2;
                break;
            case CA_CENTER:
                m1=a;
                m2=b;
                l=(a+b)/2;
                l1=(a+l)/2;
                l2=(l+b)/2;
                break;
            default:
                m1=0;
                m2=0;
                l=0;
                l1=0;
                l2=0;
                break;
        }
		fl_line_style(FL_SOLID|FL_CAP_FLAT,tick_width_);
//		double minp,maxp;
		
        if(axis_align_ & CA_LINE){
			fl_begin_line();
			fl_vertex(l,canvas_->y()+Fl::box_dy(canvas_->box()));
			fl_vertex(l,canvas_->y()+canvas_->h()+Fl::box_dy(canvas_->box())-Fl::box_dh(canvas_->box()));
			//fl_vertex(x()+border_,l);
			//fl_vertex(x()+w()-border_,l);
			fl_end_line();
        }
        while(next_tick(tick_index, tick_value, tick_order, _interval)){
			_pos=position(tick_value);
			if(scale_&CA_REV){
				if(_pos<min_pos_-canvas_->border()) continue;
				if(_pos>max_pos_+canvas_->border()) break;
			}else{
				if(_pos<max_pos_-canvas_->border()) break;
				if(_pos>min_pos_+canvas_->border()) continue;
			}
			if(!(axis_align_&CA_NO_TICS)){
				fl_begin_loop();
				if(tick_index % major_step_){
					fl_vertex(l1,_pos);
					fl_vertex(l2,_pos);
				}else{
					fl_vertex(m1,_pos);
					fl_vertex(m2,_pos);
				}
				fl_end_loop();
			}
            if(!((tick_index % label_step_)|(axis_align_&CA_NO_LABELS))){
                char label[MAX_LABEL_LENGTH];
                char _label_format[MAX_LABEL_FORMAT];
                if(!label_format){
                    int _tick_order;
                    if (tick_order>=0)
                        _tick_order=0;
                    else
                        _tick_order=-tick_order;
                    sprintf(_label_format,"%s.%if","%",_tick_order);
                }
                else
                    strcpy(_label_format,label_format);
                sprintf(label, _label_format,tick_value);
                fl_measure(label,_w,_h);
                _y=_pos+_h/3;
                switch (axis_align_ & CA_ALIGNMENT){
                    case CA_LEFT:
                        _x=m1-_h/3-_w;
                        break;
                    case CA_RIGHT:
                        _x=m2+_h/3;
                        break;
                    case CA_CENTER:
                        _x=(m1+m2)/2-_w/2;
                        {
                            Fl_Color _color=fl_color();
                            fl_color(color());
                            ca_rectf(_x-_h/6,_pos-_h/2,_w+_h/3,_h);
                            fl_color(_color);
                        }
                        break;
                    default:
                        _x=0;
                        break;
                }
                ca_text(label,_x,_y);
            }
        }
		fl_line_style(0);
        fl_pop_clip();
    }
};


void Ca_Y_Axis::current(){
    canvas_->current_y(this);
};

    

void Ca_Y_Axis::draw_grid(){
    if(!valid_)return;
	int tick_index=-1;
    double tick_value;
    int tick_order;
    double _interval=0;
    int l1,l2;
	l1=canvas_->x()+Fl::box_dx(canvas_->box());
	l2=canvas_->x()+canvas_->w()+Fl::box_dx(canvas_->box())-Fl::box_dw(canvas_->box());
	int tcl;
	if(!(tcl=tick_length_))
		tcl=label_font_size_;
    while(next_tick(tick_index, tick_value, tick_order,_interval)){
		double _pos=position(tick_value);
		if(scale_&CA_REV){
			if(_pos<min_pos_-canvas_->border()) continue;
			if(_pos>max_pos_+canvas_->border()) break;
		}else{
			if(_pos<max_pos_-canvas_->border()) break;
			if(_pos>min_pos_+canvas_->border()) continue;
		}
		int grt;
        if((grt=grid_visible_&CA_LABEL_GRID) && !(tick_index % label_step_)){
			fl_color(label_grid_color_);
            fl_line_style(label_grid_style_,label_grid_width_);
			if(grt==CA_LABEL_GRID){
				fl_begin_loop();
				fl_vertex(l1,_pos);
				fl_vertex(l2,_pos);
				fl_end_loop();

			}else{
				if(grt&CA_LEFT_LABEL_TICK){
					fl_begin_loop();
					fl_vertex(l1,_pos);
					fl_vertex(l1+tcl,_pos);
					fl_end_loop();
				}
				if(grt&CA_RIGHT_LABEL_TICK){
					fl_begin_loop();
					fl_vertex(l2-tcl,_pos);
					fl_vertex(l2,_pos);
					fl_end_loop();
				}
			}
        }else if((grt=grid_visible_&CA_MAJOR_GRID) && !(tick_index % major_step_)){
			fl_color(major_grid_color_);
            fl_line_style(major_grid_style_,major_grid_width_);
			if(grt==CA_MAJOR_GRID){
				fl_begin_loop();
				fl_vertex(l1,_pos);
				fl_vertex(l2,_pos);
				fl_end_loop();

			}else{
				if(grt&CA_LEFT_MAJOR_TICK){
					fl_begin_loop();
					fl_vertex(l1,_pos);
					fl_vertex(l1+tcl,_pos);
					fl_end_loop();
				}
				if(grt&CA_RIGHT_MAJOR_TICK){
					fl_begin_loop();
					fl_vertex(l2-tcl,_pos);
					fl_vertex(l2,_pos);
					fl_end_loop();
				}
			}
        }else if((grt=(grid_visible_&CA_MINOR_GRID))){
			fl_color(minor_grid_color_);
            fl_line_style(minor_grid_style_,minor_grid_width_);
			if(grt==CA_MINOR_GRID){
				fl_begin_loop();
				fl_vertex(l1,_pos);
				fl_vertex(l2,_pos);
				fl_end_loop();

			}else{
				if(grt&CA_LEFT_MINOR_TICK){
					fl_begin_loop();
					fl_vertex(l1,_pos);
					fl_vertex(l1+tcl/2,_pos);
					fl_end_loop();
				}
				if(grt&CA_RIGHT_MINOR_TICK){
					fl_begin_loop();
					fl_vertex(l2-tcl/2,_pos);
					fl_vertex(l2,_pos);
					fl_end_loop();
				}
			}
        }
    }

	fl_line_style(0,0);
	fl_color(FL_BLACK);
};

    
Ca_Y_Axis::Ca_Y_Axis(int x, int y, int w, int h, const char * label):Ca_Axis_(x, y, w,  h,  label){
    if(!(canvas_->current_y()))
        current();
    axis_align(CA_LEFT|CA_LINE);
};

Ca_Y_Axis::~Ca_Y_Axis(){
	if(canvas_){
	Ca_ObjectChain *ochain=canvas_->first_object_;
    Ca_ObjectChain *next;
	Ca_ObjectChain *previous=0;
    while (ochain){
        next=ochain->next;
        if(ochain->object->y_axis_==this){
            delete ochain->object;
			if(previous)
				previous->next=next;
			else
				canvas_->first_object_=next;
			delete ochain;
        }
		ochain=next;
    }
	}
}

/////////////////////////////// Ca_Canvas  ////////////////////////////////////////////

Ca_Canvas *Ca_Canvas::current_=0;

void Ca_Canvas::draw(){
    
    uchar damage_= damage();
   // int _b=border_/2;
   // int _x=x()+_b;
   // int _y=y()+_b;
   // int _w=w()-2*_b;
   // int _h=h()-2*_b;
    int replot=0;

    Ca_Axis_ *axis = last_axis_;

/// something similar will go in the future into the lauout layer...
    while (axis){
        replot |= axis->update();
        axis=axis->previous_axis_;
    }
///
	if(damage_!=CA_DAMAGE_ADD)
		draw_box();
	fl_clip(x()+Fl::box_dx(box()),y()+Fl::box_dy(box()),w()-Fl::box_dw(box()),h()-Fl::box_dh(box()));
    if((damage_!=CA_DAMAGE_ADD)||replot){
        last_plotted_=0;
        axis=last_axis_;
		while(axis){
			if(!(axis->grid_visible()&CA_FRONT)&&(axis->visible()||(axis->grid_visible()&CA_ALWAYS_VISIBLE)))
				axis->draw_grid();
			axis=axis->previous_axis_;
        }
    }
            
    if (last_plotted_)
        last_plotted_=last_plotted_->next;
    else
        last_plotted_=first_object_;
    while(last_plotted_){
        last_plotted_->object->draw();
        last_plotted_=last_plotted_->next;
    }
    last_plotted_=last_object_; 
	axis=last_axis_;        
    while(axis){
		if((axis->grid_visible()&CA_FRONT)&&(axis->visible()||(axis->grid_visible()&CA_ALWAYS_VISIBLE)))
			axis->draw_grid();
		axis=axis->previous_axis_;
    }

    fl_pop_clip();
    if (damage_&FL_DAMAGE_ALL)
        draw_label();
};
    


void Ca_Canvas::add_object(Ca_Object_ * object){
    last_object_=last_object_->next=new Ca_ObjectChain();
    last_object_->object=object;
};

void Ca_Canvas::clear(){
    while(first_object_)
        delete first_object_->object;
    damage(CA_DAMAGE_ALL);
};

Ca_Canvas::Ca_Canvas(int x, int y, int w, int h, const char *label)
	:Fl_Box(x,y,w,h,label),
	last_axis_(0),border_(CANVAS_BORDER), current_x_(0), current_y_(0),
    first_object_(0),last_object_(0),last_plotted_(0)
{
	
    current(this);
};

void Ca_Canvas::border(int border){
    border_=border;
    damage(CA_DAMAGE_ALL);
    Ca_Axis_ *axis=last_axis_;
    while(axis){
        axis->damage(CA_DAMAGE_ALL);
        axis=axis->previous_axis_;
    }
};

Ca_Canvas::~Ca_Canvas(){
    clear();
    Ca_Axis_ *axis=last_axis_;
    while(axis){
        last_axis_=axis->previous_axis_;
        axis->canvas_=0;
        axis = last_axis_;
    }
};
        




////////////////////////  Ca_Object //////////////////////

Ca_Object_::Ca_Object_(Ca_Canvas * canvas)
	:canvas_(canvas)
{
    if(!canvas_)
        canvas_=Ca_Canvas::current();
    Ca_ObjectChain *objectchain=new(Ca_ObjectChain);
    objectchain->object=this;
    objectchain->next=0;
    if(canvas_->last_object_)
        canvas_->last_object_=canvas_->last_object_->next=objectchain;
    else
        canvas_->last_object_=canvas_->first_object_=objectchain;
    x_axis_=canvas_->current_x();
    y_axis_=canvas_->current_y();
    canvas_->damage(CA_DAMAGE_ADD);
};


Ca_Object_::~Ca_Object_(){
    Ca_ObjectChain * objectchain=canvas_->first_object_;
    Ca_ObjectChain * previouschain=0;
    while(objectchain->object!=this){
        previouschain=objectchain;
        objectchain=objectchain->next;
    }
    if(previouschain)
        previouschain->next=objectchain->next;
    else
        canvas_->first_object_=objectchain->next;
    if(canvas_->last_object_==objectchain)
        canvas_->last_object_=previouschain;
    delete objectchain;
    canvas_->last_plotted_=0;
    canvas_->damage(CA_DAMAGE_ALL);
};



/////////////////////////   Ca_Point    //////////////////////////////////////////////////////

void Ca_Point::draw(){
    fl_color(color);
    double s,t;
    double _x=x_axis_->position(x);
    double _y=y_axis_->position(y);
    switch(style & CA_POINT_STYLE){
	case CA_NO_POINT:
		break;
    case CA_SIMPLE:
        ca_point(_x,_y);
        break;
    case CA_ROUND:
		ca_filled_circle(_x,_y,size);
        //ca_pie(_x-size,_y-size,2*size,2*size,0,360);
        if (style & CA_BORDER){
			fl_color(border_color);
            fl_line_style(0,border_width);
			fl_begin_loop();
            fl_circle(_x,_y,size);
			fl_end_loop();
            fl_line_style(0,0);
        }
        break;
    case CA_SQUARE:
        ca_rectf(_x-size,_y-size,2*size,2*size);
        if (style & CA_BORDER){
            fl_color(border_color);
            fl_line_style(0,border_width);
            ca_rect(_x-size,_y-size,2*size,2*size);
			fl_line_style(0,0);
        }
        break;
    case CA_UP_TRIANGLE:
        s=(int)(1.12*size+0.5);
        t=_y+size/3;
        ca_polygon(_x,t-2*size,_x-s,t,_x+s,t);
        if (style & CA_BORDER){
            fl_color(border_color);
            fl_line_style(0,border_width);
            ca_loop(_x,t-2*size,_x-s,t,_x+s,t);
			fl_line_style(0,0);
        }
        break;
    case CA_DOWN_TRIANGLE:
        s=(int)(1.12*size+0.5);
        t=_y-size/3;
        ca_polygon(_x,t+2*size,_x-s,t,_x+s,t);
        if (style & CA_BORDER){
            fl_color(border_color);
            fl_line_style(0,border_width);
            ca_loop(_x,t+2*size,_x-s,t,_x+s,t);
			fl_line_style(0,0);
        }
        break;
    case CA_DIAMOND:
        s=(int)(1.3*size+0.5);
        ca_polygon(_x,_y-s,_x-s,_y,_x,_y+s,_x+s,_y);
        if (style & CA_BORDER){
            fl_color(border_color);
            fl_line_style(0,border_width);
            ca_loop(_x,_y-s,_x-s,_y,_x,_y+s,_x+s,_y);
			fl_line_style(0,0);
        }
        break;
    }
};

Ca_Point::Ca_Point(double _x, double _y, Fl_Color _color, int _style, int _size, Fl_Color _border_color, int _border_width)
	:Ca_Object_(0),
	x(_x),
    y(_y),
    style(_style),
	size(_size),
    color(_color),
    border_color(_border_color),
	border_width(_border_width)

{
};

////////////////////////////  Ca_LinePoint  ////////////////////////////////////////////////////////

void Ca_LinePoint::draw(){
    Ca_Point::draw();
    if(previous){
        fl_color(color);
        fl_line_style(0,line_width);
		fl_begin_line();
        fl_vertex(previous->x_axis_->position(previous->x),previous->y_axis_->position(previous->y));
		fl_vertex(x_axis_->position(x),y_axis_->position(y));
		fl_end_line();
		fl_line_style(0,0);
    }
    
};

Ca_LinePoint::Ca_LinePoint( Ca_LinePoint *_previous, double _x, double _y,int _line_width, Fl_Color color, int style, int size, Fl_Color border_color, int _border_width):Ca_Point(_x, _y, color, style, size, border_color, _border_width){
    previous=_previous;
	line_width=_line_width;
};

Ca_LinePoint::Ca_LinePoint(Ca_LinePoint *_previous, double _x, double _y)
	:Ca_Point(_x, _y, previous->color, previous->style, previous->size,previous->border_color, previous->border_width),
	previous(_previous)
{
	if(_previous)
		line_width=_previous->line_width;
	else
		line_width=0;
};


////////////////////////////  Ca_PolyLine  ////////////////////////////////////////////////////////


Ca_PolyLine::Ca_PolyLine(Ca_PolyLine *_previous, double _x, double _y,int _line_style, int _line_width, Fl_Color color, int style, int size, Fl_Color border_color,int _border_width)
	:Ca_LinePoint(_previous, _x, _y, _line_width, color,  style, size, border_color, _border_width),
	line_style(_line_style)	
{
	next=0;
	if(_previous) _previous->next=this;
	canvas_->damage(CA_DAMAGE_ALL);
};

Ca_PolyLine::Ca_PolyLine(Ca_PolyLine *_previous, double _x, double _y):Ca_LinePoint(_previous,x,y){
	next=0;
	if(_previous){
		line_style=_previous->line_style;
		_previous->next=this;
	}
	canvas_->damage(CA_DAMAGE_ALL);
};

void Ca_PolyLine::draw(){
	Ca_Point::draw();
	if(next) return;
	Ca_PolyLine * temp;
	int c=color;
	int style=line_style;
	int size=line_width;
	fl_color(c);
	fl_line_style(style,size);
	fl_begin_line();
	fl_vertex(x_axis_->position(x),y_axis_->position(y));
	temp=(Ca_PolyLine *)previous;
	while(temp){
		fl_vertex(temp->x_axis_->position(temp->x),temp->y_axis_->position(temp->y));
		if((temp->line_style != style)||(temp->color!=c)||(temp->line_width!=size)){
			fl_end_line();
			c=temp->color;
			style=temp->line_style;
			size=temp->line_width;
			fl_color(c);
			fl_line_style(style,size);
			fl_begin_line();
			fl_vertex(temp->x_axis_->position(x),temp->y_axis_->position(y));
		}
		temp=(Ca_PolyLine *)(temp->previous);
	}
	fl_end_line();
	fl_line_style(0,0);
};

Ca_Line::Ca_Line(int _n, double *_data, double *_data_2, int _line_style, int _line_width, Fl_Color color,  int style, int size, Fl_Color border_color, int border_width)
	:Ca_Point(0, 0, color, style, size, border_color, border_width),
	line_style(_line_style),
	line_width(_line_width),
	n(_n),
	data(_data),
	data_2(_data_2)
{
};

Ca_Line::Ca_Line(int _n, double *_data, int _line_style, int _line_width, Fl_Color color, int style, int size, Fl_Color border_color, int border_width)
	:Ca_Point(0, 0, color, style, size, border_color, border_width),
	line_style(_line_style),
	line_width(_line_width),
	n(_n),
	data(_data),
	data_2(0)
{
};

void Ca_Line::draw(){
	fl_color(color);
	fl_line_style(line_style,line_width);
	fl_begin_line();
	int i;
	if(data_2){
		for(i=0;i<n;i++)
			fl_vertex(x_axis_->position(data[i]),y_axis_->position(data_2[i]));
		fl_end_line();
		fl_line_style(0,0);
		for(i=0;i<n;i++){
			x=data[i];
			y=data_2[i];
			Ca_Point::draw();
		}
	}else{
		for(i=0;i<n;i++)
			fl_vertex(x_axis_->position(data[2*i]),y_axis_->position(data[2*i+1]));
		fl_end_line();
		for(i=0;i<n;i++){
			x=data[2*i];
			y=data[2*i+1];
			Ca_Point::draw();
		}
		fl_line_style(0,0);
	}
	
};

void Ca_Text::draw(){
	uchar align_=align;
	double X,Y,W,H;
	double X1,Y1;

	X = x_axis_->position(x1);
	X1 = x_axis_->position(x2);
	if(X1>X)
		W=X1-X;
	else{
		W=X-X1;
		X=X1;
	}
	Y = y_axis_->position(y1);
	Y1 = y_axis_->position(y2);
	if(Y1>Y)
		H = Y1-Y;
	else{
		H = Y-Y1;
		Y = Y1;
	}
	fl_color(label_color);
	fl_font(label_font,label_size);
	ca_text(label,X,Y,W,H,(Fl_Align)align_);
};


Ca_Text::Ca_Text(double _x1, double _x2, double _y1, double _y2, char *_label, uchar _align, Fl_Font _label_font, int _label_size, Fl_Color _label_color)
	:Ca_Object_(0),
	x1(_x1), x2(_x2), y1(_y1), y2(_y2),
	label(_label),
	align(_align),
	label_color(_label_color),
	label_font(_label_font),
	label_size(_label_size)
{};

Ca_Text::Ca_Text(double x, double y,char *_label, uchar _align, Fl_Font _label_font, int _label_size, Fl_Color _label_color)
	:Ca_Object_(0),
	x1(x), x2(x), y1(y), y2(y),
	label(_label),
	align(_align),
	label_color(_label_color),
	label_font(_label_font),
	label_size(_label_size)
{};




void Ca_Bar::draw(){
	uchar align_=align;
	double X,Y,W,H;
	double X1,Y1;

	X = x_axis_->position(x1);
	X1 = x_axis_->position(x2);
	if(X1>X)
		W=X1-X;
	else{
		W=X-X1;
		X=X1;
	}
	Y = y_axis_->position(y1);
	Y1 = y_axis_->position(y2);
	if(Y1>Y)
		H = Y1-Y;
	else{
		H = Y-Y1;
		Y = Y1;
	}

	fl_color(color);
	ca_rectf(X,Y,W,H);
	if(border_width>=0){
		fl_color(border_color);
		fl_line_style(FL_SOLID|FL_CAP_SQUARE, border_width);
		ca_rect(X,Y,W,H);
	}
	fl_line_style(0,0);

	if(!(align_&15)||(align_&FL_ALIGN_INSIDE)){
		X += border_width/2;
		W -= border_width;
		Y += border_width/2;
		H -= border_width;
	}else{
		X -= border_width/2;
		W += border_width;
		Y -= border_width/2;
		H += border_width;
		if (align_ & FL_ALIGN_TOP){
			align_ ^= (FL_ALIGN_BOTTOM|FL_ALIGN_TOP);
			H=Y;
			Y=canvas_->y()+Fl::box_dy(canvas_->box());
			H-=Y;
		}else if(align_ & FL_ALIGN_BOTTOM){
			align_^=(FL_ALIGN_BOTTOM|FL_ALIGN_TOP);
			Y=Y+H;
			H=canvas_->y()+canvas_->h()+Fl::box_dy(canvas_->box())-Fl::box_dh(canvas_->box())-Y;
		}else if(align_ & FL_ALIGN_LEFT){
			align_^=(FL_ALIGN_LEFT|FL_ALIGN_RIGHT);
			W=X;
			X=canvas_->x()+Fl::box_dx(canvas_->box());
			W=W-X-3;
		}else if(align_ & FL_ALIGN_RIGHT){
			align_ ^=(FL_ALIGN_LEFT|FL_ALIGN_RIGHT);
			X=X+W+3;
			W=canvas_->x()+Fl::box_dx(canvas_->box())+canvas_->w()-Fl::box_dw(canvas_->box())-X;
		}
	}
	fl_color(label_color);
	fl_font(label_font,label_size);
	ca_text(label,X,Y,W,H,(Fl_Align)align_);
};







Ca_Bar::Ca_Bar(double _x1, double _x2, double _y1, double _y2, Fl_Color _color,  Fl_Color _border_color, int _border_width,  char *_label, uchar _align, Fl_Font _label_font, int _label_size, Fl_Color _label_color)
		:Ca_Text(_x1, _x2, _y1, _y2, _label, _align, _label_font, _label_size, _label_color),
		color(_color),
		border_color(_border_color),
		border_width(_border_width)
		{
};
