fltk_test_11

// Cartesian.H,v 1.0

//

// Copyright 2000-2005 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.

#ifndef _Cartesian_h_

#define _Cartesian_h_

#include <FL/Fl_Box.H>

#define DEFAULT_POINT_SIZE 4

#define CA_DEFAULT_LABEL_SIZE 12

enum Ca_Damage {CA_DAMAGE_ALL=1, CA_DAMAGE_ADD=2};

enum Ca_When{CA_WHEN_MIN=1,CA_WHEN_MAX=2};

enum Ca_AxisAlign{CA_LEFT=0, CA_RIGHT=1, CA_BOTTOM=CA_LEFT, CA_TOP=CA_RIGHT,

CA_CENTER=3,CA_ALIGNMENT=3,CA_NO_LABELS=32, CA_NO_TICS=64, CA_LINE=128};

enum Ca_PointStyle{CA_SIMPLE=0,CA_ROUND=1,CA_SQUARE=2,CA_UP_TRIANGLE=3, CA_DOWN_TRIANGLE=4,

CA_DIAMOND=5,CA_NO_POINT=6, CA_POINT_STYLE=127, CA_BORDER=128};

enum Ca_GridVisible{

CA_MINOR_GRID=0xF,CA_LEFT_MINOR_TICK=0x1, CA_BOTTOM_MINOR_TICK=CA_LEFT_MINOR_TICK,CA_RIGHT_MINOR_TICK=0x2, CA_TOP_MINOR_TICK=CA_RIGHT_MINOR_TICK, CA_MINOR_TICK=0x3,

CA_MAJOR_GRID=0xF0, CA_LEFT_MAJOR_TICK=0x10, CA_BOTTOM_MAJOR_TICK=CA_LEFT_MAJOR_TICK,CA_RIGHT_MAJOR_TICK=0x20, CA_TOP_MAJOR_TICK=CA_RIGHT_MAJOR_TICK,CA_MAJOR_TICK=0x30,

CA_LABEL_GRID=0xF00,CA_LEFT_LABEL_TICK=0x100,CA_BOTTOM_LABEL_TICK=CA_LEFT_LABEL_TICK,CA_RIGHT_LABEL_TICK=0x200, CA_TOP_LABEL_TICK=CA_RIGHT_LABEL_TICK, CA_LABEL_TICK=0x300,

CA_ALWAYS_VISIBLE=0x1000,CA_FRONT=0x2000};

enum Ca_AxisType{CA_LIN=0, CA_LOG=1, CA_REV=2};

class Ca_Canvas;

class Ca_Axis_;

class Ca_Object_;

class Ca_ObjectChain;

//class Cartesian;

///////////////////////////////////////////////////////////////////////////

class Ca_ObjectChain{

public:

    Ca_Object_ *object;

    Ca_ObjectChain *next;

};

///////////////////////////////////////////////////////////////////////////

class Ca_Canvas:public Fl_Box{

    friend class Ca_Axis_;

    friend class Ca_X_Axis;

    friend class Ca_Y_Axis;

    friend class Ca_Object_;

    static Ca_Canvas *current_;

    int replot_;

    Ca_Axis_ * last_axis_;

    int border_;

    Ca_Axis_ *current_x_;

    Ca_Axis_ *current_y_;

    Ca_ObjectChain * first_object_;

    Ca_ObjectChain * last_object_;

    Ca_ObjectChain * last_plotted_;

    void add_object(Ca_Object_ *object);

    

    /* No function body - prevents copy construction/assignment */

    Ca_Canvas(const Ca_Canvas &);    

    const Ca_Canvas & operator=(const Ca_Canvas &); 

    int dx_, dy_, dw_, dh_;

protected:

    void draw();

public:

    static Ca_Canvas * current(){return current_;};

    static void current(Ca_Canvas * _current){current_=_current;}

    void current_x(Ca_Axis_ * axis){current_x_=axis;};

    void current_y(Ca_Axis_ * axis){current_y_=axis;};

    Ca_Axis_ * current_x(){return current_x_;};

    Ca_Axis_ * current_y(){return current_y_;};

    void clear();

    int border(){return border_;};

    void border(int border);

    void clip_border(int dx, int dy, int dw, int dh){ dx_ = dx; dy_ = dy; dw_ = dw; dh_ = dh;}

    void clip_border(int * dx, int * dy, int *dw, int *dh){ *dx = dx_; *dy = dy_; *dw = dw_; *dh = dh_;}

    

    Ca_ObjectChain * objects(){return first_object_;};

    Ca_Canvas(int x, int y, int w, int h, const char *label=0);

    ~Ca_Canvas();

    

};

///////////////////////////////////////////////////////////////////////////

class Ca_Axis_:public Fl_Box{

    friend class Ca_Canvas;

    Ca_Axis_ *previous_axis_;



protected:

    int scale_;

    int next_tick(int &tick_index, double &tick_value, int &tick_order, double &interval_);

    bool valid_;

    double k_;

    double q_;

    const char *label_format_;

    Fl_Color minor_grid_color_;

    Fl_Color major_grid_color_;

    Fl_Color label_grid_color_;

    int minor_grid_style_;

    int major_grid_style_;

    int label_grid_style_;

    int minor_grid_width_;

    int major_grid_width_;

    int label_grid_width_;

    char * minor_grid_dashes_;

    char * major_grid_dashes_;

    char * label_grid_dashes_;

    int grid_visible_;

    double tick_interval_;

    int tick_separation_;

    int tick_length_;

    int tick_width_;

    int major_step_;

    int label_step_;

    Ca_Canvas * canvas_;

    unsigned char  axis_align_;

    Fl_Font label_font_face_;

    int label_font_size_;

    double min_;

    double max_;

    int min_pos_;

    int max_pos_;

    int border_;

    Fl_Color axis_color_;

    

    virtual int min_pos()=0;

    virtual int max_pos()=0;

    int update();

    virtual void draw_grid()=0;

    

public:

    virtual void current()=0;

    double position(double);

    double value(double);

    Ca_Canvas * canvas(){return canvas_;};

    int border(){return border_;};

    void border(int border){border_=border;damage(CA_DAMAGE_ALL);}

    double minimum(){return min_;}; 

    double maximum(){return max_;};

    void minimum(double x);

    void maximum(double x);

    void clear(){valid_=0;};

    int cleared(){return !valid_;};

    

    void rescale(int when, double x);

    void rescale_move(int when, double  x);

    void tick_interval(double interval) {tick_interval_=interval; damage(CA_DAMAGE_ALL);};

    double tick_interval(){return tick_interval_;};

    void tick_separation(int separation) {tick_separation_=separation; damage(CA_DAMAGE_ALL);};

    int tick_separation(){return tick_separation_;};

    void tick_length(int length){tick_length_=length; damage(CA_DAMAGE_ALL);};

    int tick_length(){return tick_length_;};

    void tick_width(int width){tick_width_=width; damage(CA_DAMAGE_ALL);};

    int tick_width(){return tick_width_;};

    void major_step(int step){major_step_=step;damage(CA_DAMAGE_ALL);};

    int major_step(){return major_step_;};

    void label_step(int step){label_step_=step;damage(CA_DAMAGE_ALL);};

    int label_step(){return label_step_;};

    void label_format(const char *format){label_format_=format; damage(CA_DAMAGE_ALL);};

    const char* label_format(){return label_format_;};

    void label_font(Fl_Font face){label_font_face_=face; damage(CA_DAMAGE_ALL);};

    Fl_Font label_font(){return label_font_face_;};

    void label_size(int size){label_font_size_=size; damage(CA_DAMAGE_ALL);};

    int label_size(){return label_font_size_;};

    void axis_color(Fl_Color _axis_color){axis_color_=_axis_color; damage(CA_DAMAGE_ALL);};

    Fl_Color axis_color(){ return axis_color_;};

    void minor_grid_color(Fl_Color color){minor_grid_color_=color; canvas_->damage(CA_DAMAGE_ALL);};

    Fl_Color minor_grid_color(){return minor_grid_color_;};

    void minor_grid_style(int style, int width=0, char * dashes=0){minor_grid_style_=style;minor_grid_width_=width;minor_grid_dashes_=dashes; canvas_->damage(CA_DAMAGE_ALL);};

    void minor_grid_style(int * style, int * width=0, char ** dashes=0){

if (style) *style=minor_grid_style_;

   if (width) *width=minor_grid_width_;

   if (dashes) *dashes=minor_grid_dashes_;

};

    void major_grid_color(Fl_Color color){major_grid_color_=color;  canvas_->damage(CA_DAMAGE_ALL);};

    Fl_Color major_grid_color(){return major_grid_color_;};

    void major_grid_style(int style, int width=0, char * dashes=0){major_grid_style_=style; major_grid_width_=width; major_grid_dashes_=dashes; canvas_->damage(CA_DAMAGE_ALL);};

    void major_grid_style(int * style, int * width=0, char ** dashes=0){

        if (style) *style=major_grid_style_;

        if (width) *width=major_grid_width_;

        if (dashes) *dashes=major_grid_dashes_;

    };

    void label_grid_color(Fl_Color color){label_grid_color_=color;  canvas_->damage(CA_DAMAGE_ALL);};

    Fl_Color label_grid_color(){return label_grid_color_;};

    void label_grid_style(int style, int width=0, char * dashes=0){label_grid_style_=style; label_grid_width_=width; label_grid_dashes_=dashes; canvas_->damage(CA_DAMAGE_ALL);};

    void label_grid_style(int * style, int * width=0, char ** dashes=0){

        if (style) *style=label_grid_style_;

        if (width) *width=label_grid_width_;

        if (dashes) *dashes=label_grid_dashes_;

    };

    void grid_visible(int visible){grid_visible_=visible;canvas_->damage(CA_DAMAGE_ALL);};

    int grid_visible(){return grid_visible_;};

    void axis_align(unsigned char align){axis_align_=align; damage(CA_DAMAGE_ALL);};

    unsigned char axis_align(){return axis_align_;};

    void scale(int s){if(s!=scale_){redraw(); canvas_->redraw();} scale_=s;};

    int scale(){return scale_;};

    

    Ca_Axis_(int x, int y, int w, int h, const char * label=0);

    ~Ca_Axis_();

};

//////////////////////////////////////////////////////////////////////

class Ca_X_Axis: public Ca_Axis_{

protected:

    int min_pos();

    int max_pos();

    void draw();

    void draw_grid();

public:

    void current();

    Ca_X_Axis(int x, int y, int w, int h, const char * label=0);

    ~Ca_X_Axis();

};

///////////////////////////////////////////////////////////////////////////

class Ca_Y_Axis: public Ca_Axis_{

    int min_pos();

    int max_pos();

    void draw();

    void draw_grid();

public:

    void current();

    Ca_Y_Axis(int x, int y, int w, int h, const char * label=0);

    ~Ca_Y_Axis();

    

};

///////////////////////////////////////////////////////////////////////////

class Ca_Object_{

    

    friend class Ca_Canvas;

    friend class Ca_Axis_;

    friend class Ca_Y_Axis;

    friend class Ca_X_Axis;

    // No function body - prevents copy construction/assignment 

    Ca_Object_(const Ca_Object_ &);    

    const Ca_Object_ & operator=(const Ca_Object_ &);

    

protected:

    Ca_Canvas *canvas_;

    Ca_Axis_ *x_axis_;

    Ca_Axis_ *y_axis_;

    virtual void draw()=0;

        

public:

    Ca_Object_(Ca_Canvas * canvas=0);

    virtual ~Ca_Object_();

};

///////////////////////////////////////////////////////////////////////////

class Ca_Point:public Ca_Object_{

protected:

    void draw();

public:

    double x;

    double y;

    int style;

    int size;

    Fl_Color color;

    Fl_Color border_color;

    int border_width;

    Ca_Point(double _x, double _y, Fl_Color color=FL_BLACK, int style=CA_SIMPLE, int size=DEFAULT_POINT_SIZE, Fl_Color border_color=FL_BLACK, int border_width=0);

};

///////////////////////////////////////////////////////////////////////////

class Ca_LinePoint:public Ca_Point{

protected:

    void draw();

public:

    Ca_LinePoint *previous;

    int line_width;

    Ca_LinePoint(Ca_LinePoint *_previous, double _x, double _y, int line_width, Fl_Color color=FL_BLACK,  int style=CA_SIMPLE, int size=DEFAULT_POINT_SIZE, Fl_Color border_color=FL_BLACK, int border_width=0);

    Ca_LinePoint(Ca_LinePoint *_previous, double _x, double _y);

};

///////////////////////////////////////////////////////////////////////////

class Ca_PolyLine:public Ca_LinePoint{


protected:

    void draw();

public:

    int line_style;

    Ca_PolyLine * next;

    Ca_PolyLine(Ca_PolyLine *_previous, double _x, double _y,int line_style, int line_width=0, Fl_Color color=FL_BLACK, int style=CA_SIMPLE, int size=DEFAULT_POINT_SIZE,  Fl_Color border_color=FL_BLACK, int border_width=0);

    Ca_PolyLine(Ca_PolyLine *_previous, double _x, double _y);

};

///////////////////////////////////////////////////////////////////////////

class Ca_Line:protected Ca_Point{

protected:

    void draw();

public:

    Ca_Point::style; //just making public usefull data

    Ca_Point::size;

    Ca_Point::color;

    Ca_Point::border_color;

    Ca_Point::border_width;

    int line_style;

    int line_width;

    int n;

    double * data;

    double * data_2;

    Ca_Line(int _n, double *_data, double *_data_2, int _line_style, int _line_width=0, Fl_Color color=FL_BLACK, int style=CA_SIMPLE, int size=DEFAULT_POINT_SIZE, Fl_Color border_color=FL_BLACK, int border_width=0);

    Ca_Line(int _n, double *_data, int _line_style, int _line_width=0, Fl_Color _color=FL_BLACK, int style=CA_SIMPLE, int size=DEFAULT_POINT_SIZE, Fl_Color border_color=FL_BLACK, int border_width=0);

};

///////////////////////////////////////////////////////////////////////////

class Ca_Text:public Ca_Object_{

protected:

    void draw();


public:

    double x1,x2,y1,y2;

    char * label;

    uchar align;

    Fl_Color label_color;

    Fl_Font label_font;

    int label_size;

    Ca_Text(double _x1, double _x2, double _y1, double _y2, char *_label, uchar _align=FL_ALIGN_CENTER, Fl_Font _label_font=FL_HELVETICA, int _label_size=CA_DEFAULT_LABEL_SIZE, Fl_Color _label_color=FL_BLACK);

    Ca_Text(double x, double y, char *_label=0, uchar _align=FL_ALIGN_CENTER, Fl_Font _label_font=FL_HELVETICA, int _label_size=CA_DEFAULT_LABEL_SIZE, Fl_Color _label_color=FL_BLACK);

};

///////////////////////////////////////////////////////////////////////////

class Ca_Bar:public Ca_Text{

protected:

    void draw();

public:

    Fl_Color color;

    Fl_Color border_color;

    int border_width;

    Ca_Bar(double _x1, double _x2, double _y1, double _y2, Fl_Color _color=FL_RED,  Fl_Color _border_color=FL_BLACK, int _border_width=0,  char *_label=0, uchar _align=FL_ALIGN_CENTER, Fl_Font _label_font=FL_HELVETICA, int _label_size=CA_DEFAULT_LABEL_SIZE, Fl_Color _label_color=FL_BLACK);

};

#endif

////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////

// Cartesian.cpp,v 1.0

//

// Copyright 2000-2005 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 <math.h>

#include <stdlib.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((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), k_(0), q_(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 l1=0;

int l2=0;

int m1=0;

int m2=0;

int l=0;

int _y=0;

int _w=0;

int _h=0; //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_);

        int a=y()+Fl::box_dh(box())+border_;

        int 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;

        }

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;

                }

                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 l1=0; int l2=0; int m1=0; int m2=0; int l=0; int _x=0; int _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_);

        int a=x()+Fl::box_dx(box())+border_;

        int 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;

        }

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;

                }

                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();

    

    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_;

        }

    }

    

    fl_clip(x()+Fl::box_dx(box())+dx_, y()+Fl::box_dy(box())+border_+dy_, w()-Fl::box_dw(box()) - dw_, h()-Fl::box_dh(box()) - dh_);

    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_; 

    fl_pop_clip();

    

    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 (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), dx_(0), dy_(0), dw_(0), dh_(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)

{

};

////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////

// 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 <math.h>

#include <FL/Fl.H>

#include <FL/Fl_Overlay_Window.H>

#include <FL/Fl_Light_Button.H>

#include "Cartesian.H"

#include <FL/fl_draw.H>

#ifdef FL_DEVICE // this is for printing using Fl_Device patch

#include <FL/Fl_Printer.H>

#include <FL/Fl_PostScript.H>

#include <FL/fl_file_chooser.H>

void print(Fl_Widget *, void *w) {

Fl_Widget * g = (Fl_Widget *)w;

char * filename = fl_file_chooser("Print to file...","*.ps","*.ps");

if(!filename) return;

FILE * f = fopen(filename,"w");

if(!f) return;

Fl_Printer * p = new Fl_PostScript(f, 3, Fl_Printer::A4, Fl_Printer::PORTRAIT);

p->page();

p->fit(g, FL_ALIGN_CENTER);

p->draw(g);

delete p;

};

void print2(Fl_Widget *, void *w) {

Fl_Widget * g = (Fl_Widget *)w;

Fl_Printer * p = fl_gdi_printer_chooser();

if(!p) return;

p->page();

p->fit(g, FL_ALIGN_CENTER);

p->draw(g);

delete p;

};

#endif

const double PI=3.1416;

const double MIN_FREQ=90;

const double MAX_FREQ=25000;

const double FREQ_COEF=1.15;

Ca_X_Axis*  frequency;

Ca_Y_Axis* current;

Ca_Y_Axis* power;

Ca_Y_Axis* phase;

Fl_Light_Button *logarithmic;

Fl_Light_Button *reversed;

double R=600;

double L=0.16;

double C=1.6e-7;

double U=1;

Ca_Canvas *canvas;

void type_callback(Fl_Widget *, void *){

power->scale((CA_LOG*logarithmic->value())|CA_REV*reversed->value());

}

void  next_freq(void *){

    static double f=MIN_FREQ;

    static Ca_LinePoint *P_I=0;

    static Ca_PolyLine *P_P=0;

    double XL=2*PI*f*L;

    double XC=1/(2*PI*f*C);

    double I,P,fi;

    I=U/sqrt(R*R+(XL-XC)*(XL-XC));

    P=I*I*R;

    fi=atan((XL-XC)/R);

    phase->current();                                //setting coordinate

    new Ca_Point(f,fi,FL_YELLOW,CA_DIAMOND|CA_BORDER);current->current();                                //setting coordinate

    current->rescale(CA_WHEN_MAX,I*1100);            //different rescalling for max and min just to get some extra gap

    current->rescale(CA_WHEN_MIN,I*1000);            //above maximum

    P_I=new Ca_LinePoint(P_I,f,I*1000,0,FL_BLUE,CA_ROUND|CA_BORDER);

    power->current();                                //setting coordinate

    power->rescale(CA_WHEN_MIN|CA_WHEN_MAX,P*1000);

    P_P=new Ca_PolyLine(P_P,f,P*1000,FL_DASHDOT,2,FL_RED,CA_NO_POINT);

    f =f* FREQ_COEF;

    if(f<=MAX_FREQ)

        Fl::add_timeout(.1,next_freq);

else{

power->current();

//new Ca_Text(1000,1,"Text\ntest!");

}

};

int main(int argc, char ** argv) {

    Fl_Double_Window *w= new Fl_Double_Window(580, 380, "Cartesian graphics example");

    w->size_range(450,250);

Fl_Group *c =new Fl_Group(0, 35, 580, 345 );

    c->box(FL_DOWN_BOX);

    c->align(FL_ALIGN_TOP|FL_ALIGN_INSIDE);

    canvas = new Ca_Canvas(180, 75, 300, 225, "RLC resonance circuit");

    canvas->box(FL_DOWN_BOX);

    canvas->color(7);

    canvas->align(FL_ALIGN_TOP);

    Fl_Group::current()->resizable(canvas);

// w->resizable(canvas);

    canvas->border(15);

    frequency = new Ca_X_Axis(180, 305, 300, 30, "Frequency [Hz]");

    frequency->align(FL_ALIGN_BOTTOM);

frequency->scale(CA_LOG);

    frequency->minimum(MIN_FREQ);

    frequency->maximum(MAX_FREQ);

    frequency->label_format("%g");

    frequency->minor_grid_color(fl_gray_ramp(20));

    frequency->major_grid_color(fl_gray_ramp(15));

    frequency->label_grid_color(fl_gray_ramp(10));

    frequency->grid_visible(CA_MINOR_GRID|CA_MAJOR_GRID|CA_LABEL_GRID);

    frequency->major_step(10);

    frequency->label_step(10);

frequency->axis_color(FL_BLACK);

frequency->axis_align(CA_BOTTOM|CA_LINE);

    current = new Ca_Y_Axis(100, 70, 78, 235, "I [mA]");

    current->align(FL_ALIGN_TOP_RIGHT);

current->minor_grid_style(FL_DASH);

    current->axis_align(CA_LEFT);

current->axis_color(FL_BLACK);

    power = new Ca_Y_Axis(10, 70, 75, 235, "P [mW]");

    power->box(FL_DOWN_BOX);

    power->align(FL_ALIGN_TOP);

power->grid_visible(CA_MINOR_TICK|CA_MAJOR_TICK|CA_LABEL_GRID|CA_ALWAYS_VISIBLE);

//power->minor_grid_style(FL_DOT);

power->minor_grid_color(FL_RED);

power->major_grid_color(FL_RED);

power->label_grid_color(FL_RED);

power->minimum(0.01);                    //setting beggining range

    power->maximum(1);

//power->hide();

    phase = new Ca_Y_Axis(480, 70, 60, 235, "Phase [rad]");

    phase->align(FL_ALIGN_TOP_LEFT);

    phase->minimum(-PI);

    phase->maximum(PI);

    phase->axis_align(CA_RIGHT);

    phase->tick_interval(-PI/4);        //fixed ticks setting

    phase->major_step(2);

    phase->label_step(4);

    phase->label_format("%.2f");

power->current();

//double d1[]={100,300,1000,3000,10000};

//double d2[]={.2,1,0.5,0.7,0.2};

//double d3[]={100,.2,300,1,1000,.5,3000,.7,10000,.2};

//new Ca_Line(5, d3, FL_DOT,0,FL_RED,CA_ROUND|CA_BORDER);

new Ca_Bar(7000, 12000, 0.0011, 0.9, FL_RED,  FL_BLACK, 4,  "Bar", FL_ALIGN_TOP, FL_HELVETICA);

new Ca_Bar(5000, 10000, 0.0011, 0.4, FL_GREEN,  FL_BLACK, 4,  "Sec.\nbar", FL_ALIGN_TOP|FL_ALIGN_INSIDE, FL_HELVETICA);

logarithmic=new Fl_Light_Button(10,310, 75, 25, "Log");

logarithmic->callback(&type_callback);

reversed= new Fl_Light_Button(10,340, 75, 25, "Rev");

reversed->callback(&type_callback);

c->end();

#ifdef FL_DEVICE

Fl_Button *b2 = new Fl_Button(5,5, 90, 25, "Print to file");

b2->callback(print,c);

Fl_Button *b3 = new Fl_Button(105,5, 90, 25, "Print");

b3->callback(print2,c);

#endif

    Fl_Group::current()->resizable(c);

    w->end();

    w->show();

    Fl::add_timeout(0,next_freq);

Fl::run();

return 0;

};