/*
 * Grace - GRaphing, Advanced Computation and Exploration of data
 * 
 * Home page: http://plasma-gate.weizmann.ac.il/Grace/
 * 
 * Copyright (c) 1991-1995 Paul J Turner, Portland, OR
 * Copyright (c) 1996-2007 Grace Development Team
 * 
 * Maintained by Evgeny Stambulchik
 * 
 * 
 *                           All Rights Reserved
 * 
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 * 
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 * 
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* 
 * Draw axis bars, axis labels, ticks and tick labels
 */

/* with SCALE_DEGREES, only the display is in degrees,
 * the internal data remains in radians
 */

#include <config.h>
#include <cmath.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "globals.h"
#include "utils.h"
#include "graphs.h"
#include "graphutils.h"
#include "draw.h"
#include "parser.h"
#include "protos.h"
#include "ng_objects.h"

static void polar_draw_arc (VPoint vc ,VPoint v1 ,VPoint v2 ,int invert);
static void calc_tick (int inout ,int t_op ,double x ,double dx ,double *x1 ,double *x2);
static void draw_tick (double vtpos ,double x1 ,double x2 ,VVector ort_para ,VVector ort_perp);
static void draw_phi_axis_tick (int inout ,int place ,double phi ,double rho1 ,double rho2 ,double dr);
static void draw_rho_axis_tick (int inout ,int place ,double rho ,double phi1 ,double phi2 ,double dx);


int is_xaxis(int axis)
{
    return ((axis % 2 == 0));
}

int is_yaxis(int axis)
{
    return ((axis % 2 == 1));
}

static int is_phi_axis (int axis) /* = xaxis */
{
    return ((axis % 2 == 0));
}

static int is_rho_axis (int axis) /* = yaxis */
{
    return ((axis % 2 == 1));
}

int is_log_axis(int gno, int axis)
{
    if ((is_xaxis(axis) && islogx(gno)) ||
        (is_yaxis(axis) && islogy(gno))) {
        return TRUE;
    } else {
        return FALSE;
    }
}

int is_logit_axis(int gno, int axis)
{
    if ((is_xaxis(axis) && islogitx(gno)) ||
        (is_yaxis(axis) && islogity(gno))) {
        return TRUE;
    } else {
        return FALSE;
    }
}

void drawgrid (int gno ,int layer)
{
  int caxis ,scale;
    tickmarks *t;
    tickprops tprops;
    int ttype;
    world w;
    view v;
    double wtpos;
    WPoint wp_grid_start = {0.0, 0.0}, wp_grid_stop = {0.0, 0.0};
    VPoint vp_grid_start, vp_grid_stop;
    VPoint vpc;
    double wc_start, wc_stop; /* world coordinates */
    int ittype_loop, itick;
        
    setclipping(TRUE);
    
    /* TODO: add Pen to ticks and remove the following */
    setpattern(1);
    
    get_graph_viewport(gno, &v);
    get_graph_world(gno, &w);
    
    /* graph center; for polar plots */
    vpc.x = (v.xv1 + v.xv2)/2.0;
    vpc.y = (v.yv1 + v.yv2)/2.0;
    
    for (caxis = 0; caxis < MAXAXES; caxis++) {
      if (obj_tid_is_visible_on_layer (Q_Axis ,caxis ,Q_Graph ,gno ,layer)) {
	t = get_graph_tickmarks(gno, caxis);
	if (!t || t->active != TRUE) {
            continue;
        }
	if (is_xaxis(caxis)) { /* an X-axis */
	  scale = get_graph_xscale(gno);
	    wc_start = w.xg1;
	    wc_stop = w.xg2;
            wp_grid_start.y = w.yg1;
            wp_grid_stop.y = w.yg2;
	} else {              /* a Y-axis */
	  scale = get_graph_yscale(gno);
	    wc_start = w.yg1;
	    wc_stop = w.yg2;
            wp_grid_start.x = w.xg1;
            wp_grid_stop.x = w.xg2;
	}
		
	for (ittype_loop = 0; ittype_loop < 2; ittype_loop++) {
	    if (ittype_loop == 0) { /* minor ticks */
                ttype = TICK_TYPE_MINOR;
                tprops = t->mprops;
	    } else {	  /* major ticks */
                ttype = TICK_TYPE_MAJOR;
                tprops = t->props;
	    }
            if (tprops.gridflag == 0) {
	        continue;
	    }

            setcolor(tprops.color);
	    setlinewidth(tprops.linew);
	    setlinestyle(tprops.lines);
	    
	    for (itick = 0; itick < t->nticks; itick++) {
	    	if (t->tloc[itick].type != ttype) {
	    	    continue;
	    	}
                
                wtpos = t->tloc[itick].wtpos;

	    	if ((wtpos < wc_start) || (wtpos > wc_stop)) {
	    	    continue;
	    	}

	        if (is_xaxis(caxis)) { /* an X-axis or phi axis */
                    wp_grid_start.x = wtpos;
                    wp_grid_stop.x  = wtpos;
	        } else {               /* a Y-axis or rho axis*/
                    wp_grid_start.y = wtpos;
                    wp_grid_stop.y  = wtpos;
	        }
	    	vp_grid_start = Wpoint2Vpoint (wp_grid_start);
	    	vp_grid_stop  = Wpoint2Vpoint (wp_grid_stop);
                if (is_rho_axis (caxis) && get_graph_type(gno) == GRAPH_POLAR) {
	 	    polar_draw_arc (vpc ,vp_grid_start ,vp_grid_stop ,is_graph_xinvert(gno));
                } else {
		    DrawLine(vp_grid_start, vp_grid_stop);
                }
	    }
	}
      }
    }
}

void drawaxes (int gno ,int layer)
{
  int caxis ,graph_type ,phi_axis;
  int place;             /* 0:in  1:out */
    tickmarks *t;
    tickprops tprops;
    world w;
    view v, bb;
    double vbase1, vbase2 ,vbase ,vstart ,vstop;
    double vbase_tlabel, vbase_tlabel1, vbase_tlabel2;
    double tsize, tlsize, wtpos, vtpos;
    double tl_offset, tl_trans;
    WPoint wp1_start, wp1_stop, wp2_start, wp2_stop;
    VPoint vp1_start, vp1_stop, vp2_start, vp2_stop;
    VPoint vp_tlabel, vp_label, vp_label_offset1, vp_label_offset2;
    VPoint vpc, vp1, vp2;
    VVector ort_para, ort_perp;
    double wc_start, wc_stop, wc_start_labels, wc_stop_labels; /* world
                                                                coordinates */
    int ittype_loop, itick, itcur;
    int ttype;
    char tlabel[MAX_STRING_LENGTH];
    int tlabel1_just, tlabel2_just, label1_just, label2_just;
    int langle;

    WPoint wp;
    int nticks;
    double offset_rho;

    int tick_dir_sign ,t_op;
    
    double (*coord_conv) ();
    
    
    setclipping(FALSE);

    /* TODO: add Pen to ticks and remove the following */
    setpattern(1);
    
    get_graph_viewport(gno, &v);
    get_graph_world(gno, &w);
    graph_type = get_graph_type (gno);

    /* graph center; for polar plots */
    vpc.x = (v.xv1 + v.xv2)/2.0;
    vpc.y = (v.yv1 + v.yv2)/2.0;
    
       
    for (caxis = 0; caxis < MAXAXES; caxis++) {
      if (obj_tid_is_visible_on_layer (Q_Axis ,caxis ,Q_Graph ,gno ,layer) == FALSE) continue;
	t = get_graph_tickmarks(gno, caxis);
	if (!t || t->active != TRUE) {
            continue;
        }
        t_op = t->t_op;
        if (t->zero == FALSE) {
            tick_dir_sign = +1;
        } else {
            tick_dir_sign = -1;
        }
	offset_rho = 0.02 * t->props.size;
	phi_axis   = is_xaxis(caxis) && graph_type == GRAPH_POLAR;
        
        if (is_xaxis(caxis)) { /* an X-axis or phi-axis */
	    ort_para.x = 1.0;
	    ort_para.y = 0.0;
	    ort_perp.x = 0.0;
	    ort_perp.y = 1.0;
            
	    coord_conv = xy_xconv;
	    
	    wc_start = w.xg1;
	    wc_stop = w.xg2;

            wp1_start.x = w.xg1;
	    wp1_stop.x  = w.xg2;
	    wp2_start.x = w.xg1;
	    wp2_stop.x  = w.xg2;
	    if (t->zero == TRUE) {
                if (w.yg1 <= 0.0 && w.yg2 >= 0.0) {
	            wp1_start.y = 0.0;
	            wp1_stop.y  = 0.0;
	            wp2_start.y = 0.0;
	            wp2_stop.y  = 0.0;
                } else {
                    continue;
                }
            } else {
	        wp1_start.y = w.yg1;
	        wp1_stop.y  = w.yg1;
	        wp2_start.y = w.yg2;
	        wp2_stop.y  = w.yg2;
            }
            
            vp1_start = Wpoint2Vpoint(wp1_start);
            vp1_stop  = Wpoint2Vpoint(wp1_stop);
            vp2_start = Wpoint2Vpoint(wp2_start);
            vp2_stop  = Wpoint2Vpoint(wp2_stop);
            
            if (is_graph_yinvert(gno) == TRUE) {
                vpswap(&vp1_start, &vp2_start);
                vpswap(&vp1_stop, &vp2_stop);
            }

            /* TODO axis offset for polar plots */
            if (graph_type != GRAPH_POLAR) {
                 vp1_start.y -= t->offsx;
                 vp1_stop.y  -= t->offsx;
                 vp2_start.y += t->offsy;
                 vp2_stop.y  += t->offsy;
	    }
	    
	    vbase1 = vp1_start.y;
	    vbase2 = vp2_start.y;
	    
	    tlabel1_just = JUST_CENTER|JUST_TOP;
	    tlabel2_just = JUST_CENTER|JUST_BOTTOM;
	    
	    switch (t->label_layout) {
	    case LAYOUT_PARALLEL:
	    	langle =  0;
	    	break;
	    case LAYOUT_PERPENDICULAR:
	    	langle = 90;
	    	break;
	    default:
	    	errmsg("Internal error in drawaxes()");
	    	return;
	    }
	} else {              /* a Y-axis or rho-axis */
	    ort_para.x = 0.0;
	    ort_para.y = 1.0;
	    ort_perp.x = 1.0;
	    ort_perp.y = 0.0;
	    
	    coord_conv = xy_yconv;
	    
	    wc_start = w.yg1;
	    wc_stop = w.yg2;

	    wp1_start.y = w.yg1;
	    wp1_stop.y  = w.yg2;
	    wp2_start.y = w.yg1;
	    wp2_stop.y  = w.yg2;

	    if (t->zero == TRUE) {
                if (w.xg1 <= 0.0 && w.xg2 >= 0.0) {
	            wp1_start.x = 0.0;
	            wp1_stop.x  = 0.0;
	            wp2_start.x = 0.0;
	            wp2_stop.x  = 0.0;
                } else {
                    continue;
                }
            } else {
	        wp1_start.x = w.xg1;
	        wp1_stop.x  = w.xg1;
	        wp2_start.x = w.xg2;
	        wp2_stop.x  = w.xg2;
            }
            
            vp1_start = Wpoint2Vpoint(wp1_start);
            vp1_stop  = Wpoint2Vpoint(wp1_stop);
            vp2_start = Wpoint2Vpoint(wp2_start);
            vp2_stop  = Wpoint2Vpoint(wp2_stop);

            if (is_graph_xinvert(gno) == TRUE) {
                vpswap(&vp1_start, &vp2_start);
                vpswap(&vp1_stop, &vp2_stop);
            }

            if (get_graph_type(gno) != GRAPH_POLAR) {
                vp1_start.x -= t->offsx;
                vp1_stop.x  -= t->offsx;
                vp2_start.x += t->offsy;
                vp2_stop.x  += t->offsy;
            }
	    
	    vbase1 = vp1_start.x;
	    vbase2 = vp2_start.x;
	    if (get_graph_type(gno) == GRAPH_POLAR) {
	      tlabel1_just = JUST_CENTER|JUST_MIDDLE;
	    } else {
	      tlabel1_just = JUST_RIGHT|JUST_MIDDLE;
	    }
	    tlabel2_just = JUST_LEFT|JUST_MIDDLE;
	
	    switch (t->label_layout) {
	    case LAYOUT_PARALLEL:
	    	langle = 90;
	    	break;
	    case LAYOUT_PERPENDICULAR:
	    	langle =  0;
	    	break;
	    default:
	    	errmsg("Internal error in drawaxes()");
	    	return;
	    }
	}
	
	/* Begin axis bar stuff for all cases (GRAPH_POLAR and others) */
	if (t->t_drawbar) {
	    setcolor(t->t_drawbarcolor);
	    setlinewidth(t->t_drawbarlinew);
	    setlinestyle(t->t_drawbarlines);
	    if (t_op == PLACEMENT_NORMAL || t_op == PLACEMENT_BOTH) {
                if (phi_axis) {
		    polar_draw_arc (vpc ,vp1_start ,vp1_stop ,is_graph_xinvert(gno));
                } else {
	    	    DrawLine(vp1_start, vp1_stop);
                }
	    }
	    if (t_op == PLACEMENT_OPPOSITE || t_op == PLACEMENT_BOTH) {
                if (phi_axis) {
		    polar_draw_arc (vpc ,vp2_start ,vp2_stop ,is_graph_xinvert(gno));
                } else {
	    	    DrawLine(vp2_start, vp2_stop);
                }
	    }
	}
	/* End axis bar stuff*/


        activate_bbox(BBOX_TYPE_TEMP, TRUE);
        reset_bbox(BBOX_TYPE_TEMP);        
        if (graph_type == GRAPH_POLAR) {
	  /* Begin tickmarks stuff for GRAPH_POLAR */
	  if (t->t_flag) {
	    for (ittype_loop = 0; ittype_loop < 2; ittype_loop++) { /* minor/major */
	      if (ittype_loop == 0) {
		ttype = TICK_TYPE_MINOR;
		tprops = t->mprops;
	      } else {
		ttype = TICK_TYPE_MAJOR;
		tprops = t->props;
	      }
	      setcolor	    (tprops.color);
	      setlinewidth  (tprops.linew);
	      setlinestyle  (tprops.lines);
	      tsize = 0.02 * tprops.size;
	      for (place = 0; place < 2; place++) {
		if ((place == 0 && (t_op == PLACEMENT_NORMAL   || t_op == PLACEMENT_BOTH)) ||
		    (place == 1 && (t_op == PLACEMENT_OPPOSITE || t_op == PLACEMENT_BOTH)) ) {
		  itcur = 0;
		  for (itick = 0; itick < t->nticks; itick++) {
		    if (t->tloc[itick].type != ttype) continue;
		    wtpos = t->tloc[itick].wtpos;
		    if ((wtpos < wc_start) || (wtpos > wc_stop)) continue;
		    if (phi_axis) {
		      draw_phi_axis_tick (t->t_inout ,place ,wtpos ,w.yg1 ,w.yg2 ,tsize);
		    } else {
		      draw_rho_axis_tick (t->t_inout ,place ,wtpos ,w.xg1 ,w.xg2 ,tsize);
		    }
		  }
		}
	      }
	    }
	  }

	  /* Begin tick label stuff for GRAPH_POLAR */
	  if (t->tl_flag) {
	    tprops      = t->props;
	    setfont     (t->tl_font);
	    setcharsize (t->tl_charsize);
	    tlsize      = 0.02 * t->tl_charsize;
	    tsize 	= t->t_inout ? 0.02 * tprops.size : 0.0;
	    nticks      = t->nticks;
	    if (t->tloc[0].wtpos == t->tloc[nticks].wtpos ||
		(phi_axis && (t->tloc[nticks].wtpos - t->tloc[0].wtpos) > 1.95 * M_PI) )
	      nticks--;
	    for (itick = 0; itick < nticks; itick++) {
	      if (t->tloc[itick].type != TICK_TYPE_MAJOR) continue;
	      wtpos = t->tloc[itick].wtpos;
	      if ((wtpos < wc_start) || (wtpos > wc_stop) ) continue;
	      if (phi_axis) {
		wp.x = wtpos;
		wp.y = w.yg2;
	      } else {
		wp.x = w.xg1;
		wp.y = wtpos;
	      }
	      vp_tlabel = Wpoint2Vpoint (wp);
	      if (is_phi_axis (caxis)) {
		vp_tlabel.x += (offset_rho + tsize + t->tl_gap.x) * cos(wtpos);
		vp_tlabel.y += (offset_rho + tsize + t->tl_gap.x) * sin(wtpos);
	      } else {
		vp_tlabel.x += t->tl_gap.y * cos(wp.x) + (tsize + t->tl_gap.x) * sin(wp.x);
		vp_tlabel.y += t->tl_gap.y * sin(wp.x) - (tsize + t->tl_gap.x) * cos(wp.x);
	      }
	      if (t->tl_prestr[0]) {
		strcpy(tlabel, t->tl_prestr);
	      } else {
		tlabel[0] = '\0';
	      }
	      if (t->tloc[itick].label != NULL) {
		strcat(tlabel, t->tloc[itick].label);
	      }
	      if (t->tl_appstr[0]) {
		strcat(tlabel, t->tl_appstr);
	      }
	      setcolor(t->tl_color);
	      WriteString (vp_tlabel, t->tl_angle, tlabel1_just, tlabel);
	    }
	  }
	  continue;		       	  /* end of GRAPH_POLAR  stuff */
        }

	/* Begin axis tick stuff */
	if (t->t_flag) {
	    for (ittype_loop = 0; ittype_loop < 2; ittype_loop++) {
	        if (ittype_loop == 0) { /* minor ticks */
                    ttype = TICK_TYPE_MINOR;
                    tprops = t->mprops;
	        } else {      /* major ticks */
		  ttype = TICK_TYPE_MAJOR;
		  tprops = t->props;
	        }
                setcolor(tprops.color);
                setlinewidth(tprops.linew);
	        setlinestyle(tprops.lines);
	        tsize = 0.02 * tprops.size;
		for (place = 0; place < 2; place++) {   /* 0:in  1:out  */
		  if ((place == 0 && (t_op == PLACEMENT_NORMAL   || t_op == PLACEMENT_BOTH)) ||
		      (place == 1 && (t_op == PLACEMENT_OPPOSITE || t_op == PLACEMENT_BOTH)) ) {
		    vbase =  (place == 1) ? vbase2 : vbase1;
		    calc_tick (t->t_inout ,place ,vbase ,tsize ,&vstart ,&vstop);
		    itcur = 0;
		    for (itick = 0; itick < t->nticks; itick++) {
		      if (t->tloc[itick].type != ttype) continue;
		      wtpos = t->tloc[itick].wtpos;
		      if ((wtpos < wc_start) || (wtpos > wc_stop)) continue;
		      vtpos = coord_conv(wtpos);
		      draw_tick (vtpos ,vstart ,vstop ,ort_para ,ort_perp);
		    }
		    itcur++;
		  }
		}
	    }
	}
	/* End axis ticks stuff */
        
        /* Make sure we don't end up with an empty BBox if no ticks have
           been drawn */
        vp1.x = v.xv1;
        vp1.y = v.yv1;
        vp2.x = v.xv2;
        vp2.y = v.yv2;
        update_bbox(BBOX_TYPE_TEMP, vp1);
        update_bbox(BBOX_TYPE_TEMP, vp2);


	/* Begin tick label stuff */

	if(t->tl_gaptype==TYPE_AUTO) {
	  /* hard coded offsets for autoplacement of tick labels */
	  tl_trans=0.0;     /* parallel */
	  tl_offset=0.01;  /* perpendicular */
	} else{
	  tl_trans  = t->tl_gap.x;
	  tl_offset = t->tl_gap.y;
	}

	if (t->tl_flag) {
	    if (t->tl_starttype == TYPE_SPEC) {
	        wc_start_labels = t->tl_start;
            } else {
	        wc_start_labels = wc_start;
	    }
	
	    if (t->tl_stoptype == TYPE_SPEC) {
	        wc_stop_labels = t->tl_stop;
            } else {
	        wc_stop_labels = wc_stop;
	    }
	
	    tlsize = 0.02 * t->tl_charsize;

	    tsize = 0.02 * t->props.size;
 
	    switch (t->t_inout) {
	    case TICKS_IN:
	        vbase_tlabel1 = vbase1 - (1 - tick_dir_sign)/2*tsize - tl_offset;
	        vbase_tlabel2 = vbase2 + (1 - tick_dir_sign)/2*tsize + tl_offset;
	        break;
	    case TICKS_OUT:
	        vbase_tlabel1 = vbase1 - (1 + tick_dir_sign)/2*tsize - tl_offset;
	        vbase_tlabel2 = vbase2 + (1 + tick_dir_sign)/2*tsize + tl_offset;
	        break;
	    case TICKS_BOTH:
	        vbase_tlabel1 = vbase1 - tsize - tl_offset;
	        vbase_tlabel2 = vbase2 + tsize + tl_offset;
	        break;
	    default:
	        errmsg("Internal error in drawaxes()");
	        return;
	    }

	    setfont(t->tl_font);
	    setcharsize(t->tl_charsize);
	
	    itcur = 0;
            for (itick = 0; itick < t->nticks; itick++) {
	        if (t->tloc[itick].type != TICK_TYPE_MAJOR) {
	            continue;
	        }
 
                wtpos = t->tloc[itick].wtpos;

	        if ((wtpos < wc_start_labels) || (wtpos > wc_stop_labels)) {
	            continue;
	        }
	
	        if (t->tl_prestr[0]) {
	            strcpy(tlabel, t->tl_prestr);
	        } else {
                    tlabel[0] = '\0';
                }
	        if (t->tloc[itick].label != NULL) {
	            strcat(tlabel, t->tloc[itick].label);
	        }
	        if (t->tl_appstr[0]) {
	            strcat(tlabel, t->tl_appstr);
	        }
	
	        vtpos = coord_conv(wtpos);

	        if (itcur % (t->tl_skip + 1) == 0) {
                    /* Set color before each tick label, since pre/app
                       strings may change it */
	            setcolor(t->tl_color);
		    
                    /* Tick labels on normal side */
	            if (t->tl_op == PLACEMENT_NORMAL ||
	                t->tl_op == PLACEMENT_BOTH) {
	                vbase_tlabel = vbase_tlabel1 - (tl_offset + tlsize)*
	                                        (itcur % (t->tl_staggered + 1));
	                vp_tlabel.x = (vtpos + tl_trans)*ort_para.x +
                                                       vbase_tlabel*ort_perp.x;
	                vp_tlabel.y = (vtpos + tl_trans)*ort_para.y +
                                                       vbase_tlabel*ort_perp.y;
	                WriteString(vp_tlabel, t->tl_angle, tlabel1_just, tlabel);
	            }
		    /* Tick labels on opposite side */
	            if (t->tl_op == PLACEMENT_OPPOSITE ||
	                t->tl_op == PLACEMENT_BOTH) {
                        vbase_tlabel = vbase_tlabel2 + (tl_offset + tlsize)*
	                                        (itcur % (t->tl_staggered + 1));
	                vp_tlabel.x = (vtpos + tl_trans)*ort_para.x +
                                                       vbase_tlabel*ort_perp.x;
	                vp_tlabel.y = (vtpos + tl_trans)*ort_para.y +
                                                       vbase_tlabel*ort_perp.y;
	                WriteString(vp_tlabel, t->tl_angle, tlabel2_just, tlabel);
	            }
	        }
                itcur++;
	    }
	}

        /* End tick label stuff */

        bb = get_bbox(BBOX_TYPE_TEMP);

	/* Begin axis label stuff */
	
	if (t->label_place == TYPE_SPEC) {
	    vp_label_offset1.x = t->label.x1;
	    vp_label_offset1.y = t->label.y1;
	    vp_label_offset2.x = t->label.x1;
	    vp_label_offset2.y = t->label.y1;
            
            /* These settings are for backward compatibility */
            label1_just = JUST_CENTER|JUST_MIDDLE;
            label2_just = JUST_CENTER|JUST_MIDDLE;
	} else {
            /* parallel is trivial ;-) */
	    vp_label_offset1.x = 0.00;
	    vp_label_offset2.x = 0.00;
	    
            /* perpendicular */
            if (is_xaxis(caxis)) {
                vp_label_offset1.y = vbase1 - bb.yv1;
                vp_label_offset2.y = bb.yv2 - vbase2;
            } else {
                vp_label_offset1.y = vbase1 - bb.xv1;
                vp_label_offset2.y = bb.xv2 - vbase2;
            }
            
            vp_label_offset1.y += tl_offset;
            vp_label_offset2.y += tl_offset;

            label1_just = tlabel1_just;
            label2_just = tlabel2_just;
	}

	if (t->label.s && t->label.s[0]) {
	    
	    setcharsize(t->label.charsize);
	    setfont(t->label.font);
	    setcolor(t->label.color);

	    /* Axis label on normal side */
	    if (t->label_op == PLACEMENT_NORMAL ||
		t->label_op == PLACEMENT_BOTH) {

	        vp_label.x = (vp1_start.x + vp1_stop.x)/2
                    + vp_label_offset1.x*ort_para.x
                    - vp_label_offset1.y*ort_perp.x;
	        vp_label.y = (vp1_start.y + vp1_stop.y)/2
                    + vp_label_offset1.x*ort_para.y
                    - vp_label_offset1.y*ort_perp.y;

	        WriteString(vp_label, langle, label1_just, t->label.s);
	    }

	    /* Axis label on opposite side */
	    if (t->label_op == PLACEMENT_OPPOSITE ||
		t->label_op == PLACEMENT_BOTH) {

	        vp_label.x = (vp2_start.x + vp2_stop.x)/2
                    + vp_label_offset2.x*ort_para.x
		    + vp_label_offset2.y*ort_perp.x ;
	        vp_label.y = (vp2_start.y + vp2_stop.y)/2
                    + vp_label_offset2.x*ort_para.y
                    + vp_label_offset2.y*ort_perp.y ;

	        WriteString(vp_label, langle, label2_just, t->label.s);
	    }
	}
        
	/* End axis label stuff */
    }
}
/**
 *  Called by plotone before drawgrid 
 *             (except for GRAPH_PIE)
 */
void calculate_tickgrid(int gno)
{
    int caxis ,graph_type ,phi_axis;
    int itick, imtick, itmaj;
    int nmajor;
    double swc_start, swc_stop, stmajor;
    int scale;
    double wtmaj ,wtpos;
    world w;
    tickmarks *t;
    int res, len;
    grarr *tvar;
    double *tt;
    scale = SCALE_NORMAL;    
reenter:
    get_graph_world(gno, &w);
    graph_type = get_graph_type (gno);
    
    for (caxis = 0; caxis < MAXAXES; caxis++) {
	t = get_graph_tickmarks(gno, caxis);

	if (!t || t->active != TRUE || t->t_spec == TICKS_SPEC_BOTH) continue;

	phi_axis = is_xaxis(caxis) && graph_type == GRAPH_POLAR;

	if (t->t_spec == TICKS_SPEC_NONE) {
            if (is_xaxis(caxis)) {
                scale = get_graph_xscale(gno);
                if (scale == SCALE_LOG) {
                    swc_start = fscale(w.xg1, scale);
                    swc_stop  = fscale(w.xg2, scale);
                } else {
                    swc_start = w.xg1;
                    swc_stop  = w.xg2;
                }
            } else {
                scale = get_graph_yscale(gno);
                if (scale == SCALE_LOG) {
                    swc_start = fscale(w.yg1, scale);
                    swc_stop  = fscale(w.yg2, scale);
                } else {
                    swc_start = w.yg1;
                    swc_stop  = w.yg2;
                }
            }
            if (scale == SCALE_LOG) {
                stmajor = fscale(t->tmajor, scale);
            } else {
                stmajor = t->tmajor;
            }

            if (stmajor <= 0.0) {
	        errmsg("Invalid major tick spacing, autoticking");
	        autotick_axis(gno, caxis);
                goto reenter;
	    }

            if (t->t_round == TRUE && scale != SCALE_DEGREES) {
                swc_start = floor(swc_start/stmajor)*stmajor;
            }

            nmajor = (int) ceil((swc_stop - swc_start) / stmajor + 1);
            t->nticks = (nmajor - 1)*(t->nminor + 1) + 1;

            if (t->nticks > MAX_TICKS) {
	        errmsg("Too many ticks ( > MAX_TICKS ), autoticking");
	        autotick_axis(gno, caxis);
                goto reenter;
	    }

    /*
     *         if (t->nticks > MAX_TICKS) {
     *             t->nticks = MAX_TICKS;
     *         }
     */

            itick = 0;
            itmaj = 0;
            while (itick < t->nticks) {
                if (scale == SCALE_LOG) {
                    wtmaj = ifscale(swc_start + itmaj*stmajor, scale);
                } else {
                    wtmaj = swc_start + itmaj*stmajor;
                    if (t->t_round == TRUE && fabs(wtmaj) < 1.0e-6*stmajor) {
                        wtmaj = 0.0;
                    }
                }
                t->tloc[itick].wtpos = wtmaj;
                t->tloc[itick].type = TICK_TYPE_MAJOR;

                itick++;
                for (imtick = 0; imtick < t->nminor && itick < t->nticks; imtick++) {
                    if (scale == SCALE_LOG) {
                        t->tloc[itick].wtpos = wtmaj * (imtick + 2);
                    } else {
                        t->tloc[itick].wtpos = wtmaj + (imtick + 1)*stmajor/(t->nminor + 1);
                    }
                    t->tloc[itick].type = TICK_TYPE_MINOR;
	            XCFREE(t->tloc[itick].label);
                    itick++;
                }
                itmaj++;
            }
        }

        if (t->t_spec != TICKS_SPEC_BOTH) {
	    nmajor = 0;
            for (itick = 0; itick < t->nticks; itick++) {
                if (t->tloc[itick].type == TICK_TYPE_MAJOR) {
                    nmajor++;
                }
            }
	    if (t->tl_formula && t->tl_formula[0] != '\0') {

                tvar = get_parser_arr_by_name("$t");
                if (tvar == NULL) {
		  tvar = define_parser_arr("$t" ,VAR_INTRINSIC);
                    if (tvar == NULL) {
	                errmsg("Internal error");
                        return;
                    }
                }

                if (tvar->length != 0) {
                    xfree(tvar->data);
                    tvar->length = 0;
                }
                tvar->data = xmalloc(nmajor*SIZEOF_DOUBLE);
                if (tvar->data == NULL) {
                    return;
                }
                tvar->length = nmajor;
                
                itmaj = 0;
                for (itick = 0; itick < t->nticks; itick++) {
                    if (t->tloc[itick].type == TICK_TYPE_MAJOR) {
                        tvar->data[itmaj] = t->tloc[itick].wtpos;
                        itmaj++;
                    }
                }

                res = v_scanner(t->tl_formula, &len, &tt);
                XCFREE(tvar->data);
                tvar->length = 0;
                if (res != RETURN_SUCCESS || len != nmajor) {
                    errmsg("Error in tick transformation formula");
                    return;
                }

                itmaj = 0;
                for (itick = 0; itick < t->nticks; itick++) {
                    if (t->tloc[itick].type == TICK_TYPE_MAJOR) {
	                t->tloc[itick].label = copy_string(t->tloc[itick].label, 
                            create_fstring(t->tl_format, t->tl_prec,
                                tt[itmaj], LFORMAT_TYPE_EXTENDED));
                        itmaj++;
                    }
                }
                xfree(tt);
	    } else {
                for (itick = 0; itick < t->nticks; itick++) {
                    if (t->tloc[itick].type == TICK_TYPE_MAJOR) {
		      wtpos = t->tloc[itick].wtpos;
		      if (phi_axis && scale == SCALE_DEGREES) wtpos *= 180.0 / M_PI;
	                t->tloc[itick].label = copy_string(t->tloc[itick].label, 
                            create_fstring(t->tl_format, t->tl_prec,
                                wtpos, LFORMAT_TYPE_EXTENDED));
                    }
                }
            }
	}
    }
}


/**
 *  vc : center , v1 and v2 ends
 */
static void polar_draw_arc (VPoint vc ,VPoint v1 ,VPoint v2 ,int invert)
{
  double phi1 ,rho1 ,phi2 ,rho2;
  xy2polar (v1.x - vc.x, v1.y - vc.y, &phi1, &rho1);
  xy2polar (v2.x - vc.x, v2.y - vc.y, &phi2, &rho2);
  /* compute the bounding rectangle */
  v1.x = vc.x - rho1;
  v1.y = vc.y + rho1;
  v2.x = vc.x + rho2;
  v2.y = vc.y - rho2;
  if (invert) fswap (&phi1 ,&phi2);
  while (phi1 < -M_PI) phi1 += 2*M_PI;
  while (phi2 <= phi1 + 0.001) phi2 += 2*M_PI;
  phi1 = rint(180.0 / M_PI * phi1);
  phi2 = rint(180.0 / M_PI * phi2);
  DrawArc (v1, v2, (int) phi1, (int) phi2);
}

/**
 * 
 */
static void calc_tick (int inout ,int opposite ,double x ,double dx ,double *x1 ,double *x2)
{
  if (opposite == PLACEMENT_OPPOSITE) dx = - dx;
  *x1 = (inout == TICKS_BOTH || inout == TICKS_OUT) ? x - dx : x;
  *x2 = (inout == TICKS_BOTH || inout == TICKS_IN ) ? x + dx : x;
}

static void draw_tick (double vtpos ,double x1 ,double x2 ,VVector ort_para ,VVector ort_perp)
{
  VPoint vp1 ,vp2;
  vp1.x = vtpos*ort_para.x + x1*ort_perp.x;
  vp1.y = vtpos*ort_para.y + x1*ort_perp.y;
  vp2.x = vtpos*ort_para.x + x2*ort_perp.x;
  vp2.y = vtpos*ort_para.y + x2*ort_perp.y;
  DrawLine (vp1, vp2);
}

static void draw_phi_axis_tick (int inout ,int place ,double phi ,double rho1 ,double rho2 ,double dr)
{
  double rho ,dr1 ,dr2;
  WPoint wp;
  VPoint vp ,vp1 ,vp2;
  if (place == PLACEMENT_OPPOSITE) {
    rho = rho1;
  } else {
    rho = rho2;
    dr = - dr;
  }
  dr1 = (inout == TICKS_BOTH || inout == TICKS_OUT) ? -dr : 0.0;
  dr2 = (inout == TICKS_BOTH || inout == TICKS_IN ) ?  dr : 0.0;
  wp.x = phi;
  wp.y = rho;
  vp = Wpoint2Vpoint (wp);
  vp1.x = vp.x + dr1*cos(phi);
  vp1.y = vp.y + dr1*sin(phi);
  vp2.x = vp.x + dr2*cos(phi);
  vp2.y = vp.y + dr2*sin(phi);
  DrawLine (vp1 ,vp2);
}

static void draw_rho_axis_tick (int inout ,int place ,double rho ,double phi1 ,double phi2 ,double dx)
{
  double phi ,dx1 ,dx2;
  WPoint wp;
  VPoint vp ,vp1 ,vp2;
  if (place == PLACEMENT_OPPOSITE) {
    phi = phi2;
    dx = - dx;
  } else {
    phi = phi1;
  }
  dx1 = (inout == TICKS_BOTH || inout == TICKS_OUT) ? -dx : 0.0;
  dx2 = (inout == TICKS_BOTH || inout == TICKS_IN ) ?  dx : 0.0;
  wp.x = phi;
  wp.y = rho;
  vp = Wpoint2Vpoint (wp);
  vp1.x = vp.x - dx1*sin(phi);
  vp1.y = vp.y + dx1*cos(phi);
  vp2.x = vp.x - dx2*sin(phi);
  vp2.y = vp.y + dx2*cos(phi);
  DrawLine (vp1 ,vp2);
}
