Initial release of Maemo 5 port of gnuplot
[gnuplot] / src / color.c
diff --git a/src/color.c b/src/color.c
new file mode 100644 (file)
index 0000000..f8b78b4
--- /dev/null
@@ -0,0 +1,680 @@
+#ifndef lint
+static char *RCSid() { return RCSid("$Id: color.c,v 1.70.2.9 2008/06/23 17:51:35 mikulik Exp $"); }
+#endif
+
+/* GNUPLOT - color.c */
+
+/*[
+ *
+ * Petr Mikulik, since December 1998
+ * Copyright: open source as much as possible
+ *
+ * What is here:
+ *   - Global variables declared in .h are initialized here
+ *   - Palette routines
+ *   - Colour box drawing
+ *
+]*/
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "color.h"
+#include "getcolor.h"
+
+#include "axis.h"
+#include "gadgets.h"
+#include "graphics.h"
+#include "plot.h"
+#include "graph3d.h"
+#include "pm3d.h"
+#include "graphics.h"
+#include "term_api.h"
+#include "util3d.h"
+#include "alloc.h"
+
+/* COLOUR MODES - GLOBAL VARIABLES */
+
+t_sm_palette sm_palette;  /* initialized in init_color() */
+
+/* Copy of palette previously in use.
+ * Exported so that change_term() can invalidate contents
+ * FIXME: better naming 
+ */
+static t_sm_palette prev_palette = {
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       (rgb_color *) 0, -1
+    };
+
+
+#ifdef EXTENDED_COLOR_SPECS
+int supply_extended_color_specs = 0;
+#endif
+
+/* Corners of the colour box. */
+static int cb_x_from, cb_x_to, cb_y_from, cb_y_to;
+
+/* Internal prototype declarations: */
+
+static void draw_inside_color_smooth_box_postscript __PROTO((FILE * out));
+static void draw_inside_color_smooth_box_bitmap __PROTO((FILE * out));
+void cbtick_callback __PROTO((AXIS_INDEX axis, double place, char *text, struct lp_style_type grid));
+
+
+
+/* *******************************************************************
+  ROUTINES
+ */
+
+
+void
+init_color()
+{
+  /* initialize global palette */
+  sm_palette.colorFormulae = 37;  /* const */
+  sm_palette.formulaR = 7;
+  sm_palette.formulaG = 5;
+  sm_palette.formulaB = 15;
+  sm_palette.positive = SMPAL_POSITIVE;
+  sm_palette.use_maxcolors = 0;
+  sm_palette.colors = 0;
+  sm_palette.color = NULL;
+  sm_palette.ps_allcF = 0;
+  sm_palette.gradient_num = 0;
+  sm_palette.gradient = NULL;
+  sm_palette.cmodel = C_MODEL_RGB;
+  sm_palette.Afunc.at = sm_palette.Bfunc.at = sm_palette.Cfunc.at = NULL;
+  sm_palette.gamma = 1.5;
+
+  /* initialisation of smooth color box */
+  color_box = default_color_box;
+}
+
+
+/*
+   Make the colour palette. Return 0 on success
+   Put number of allocated colours into sm_palette.colors
+ */
+int
+make_palette()
+{
+    int i;
+    double gray;
+
+    if (!term->make_palette) {
+       fprintf(stderr, "Error: terminal \"%s\" does not support continuous colors.\n",term->name);
+       return 1;
+    }
+
+    /* ask for suitable number of colours in the palette */
+    i = term->make_palette(NULL);
+    if (i == 0) {
+       /* terminal with its own mapping (PostScript, for instance)
+          It will not change palette passed below, but non-NULL has to be
+          passed there to create the header or force its initialization
+        */
+       if (memcmp(&prev_palette, &sm_palette, sizeof(t_sm_palette))) {
+           term->make_palette(&sm_palette);
+           prev_palette = sm_palette;
+           FPRINTF((stderr,"make_palette: calling term->make_palette for term with ncolors == 0\n"));
+       } else
+           FPRINTF((stderr,"make_palette: skipping duplicate palette for term with ncolors == 0\n"));
+       return 0;
+    }
+
+    /* set the number of colours to be used (allocated) */
+    sm_palette.colors = i;
+    if (sm_palette.use_maxcolors > 0 && i > sm_palette.use_maxcolors)
+       sm_palette.colors = sm_palette.use_maxcolors;
+
+    if (prev_palette.colorFormulae < 0
+       || sm_palette.colorFormulae != prev_palette.colorFormulae
+       || sm_palette.colorMode != prev_palette.colorMode
+       || sm_palette.formulaR != prev_palette.formulaR
+       || sm_palette.formulaG != prev_palette.formulaG
+       || sm_palette.formulaB != prev_palette.formulaB
+       || sm_palette.positive != prev_palette.positive
+       || sm_palette.colors != prev_palette.colors) {
+       /* print the message only if colors have changed */
+       if (interactive)
+       fprintf(stderr, "smooth palette in %s: available %i color positions; using %i of them\n", term->name, i, sm_palette.colors);
+    }
+
+    prev_palette = sm_palette;
+
+    if (sm_palette.color != NULL) {
+       free(sm_palette.color);
+       sm_palette.color = NULL;
+    }
+    sm_palette.color = gp_alloc( sm_palette.colors * sizeof(rgb_color),
+                                "pm3d palette color");
+
+    /*  fill sm_palette.color[]  */
+    for (i = 0; i < sm_palette.colors; i++) {
+       gray = (double) i / (sm_palette.colors - 1);    /* rescale to [0;1] */
+       rgb1_from_gray( gray, &(sm_palette.color[i]) );
+    }
+
+    /* let the terminal make the palette from the supplied RGB triplets */
+    term->make_palette(&sm_palette);
+
+    return 0;
+}
+
+/*
+ * Force a mismatch between the current palette and whatever is sent next,
+ * so that the new one will always be loaded 
+ */
+void
+invalidate_palette()
+{
+    prev_palette.colors = -1;
+}
+
+/*
+   Set the colour on the terminal
+   Currently, each terminal takes care of remembering the current colour,
+   so there is not much to do here.
+ */
+void
+set_color(double gray)
+{
+    t_colorspec color;
+    if (!(term->set_color))
+       return;
+    color.type = TC_FRAC;
+    color.value = gray;
+    term->set_color(&color);
+}
+
+void
+set_rgbcolor(int rgblt)
+{
+    t_colorspec color;
+    if (!(term->set_color))
+       return;
+    color.type = TC_RGB;
+    color.lt = rgblt;
+    term->set_color(&color);
+}
+
+void ifilled_quadrangle(gpiPoint* icorners)
+{
+    icorners->style = FS_OPAQUE;
+    term->filled_polygon(4, icorners);
+
+    if (pm3d.hidden3d_tag) {
+
+       int i;
+
+       /* Colour has changed, so we must apply line properties again.
+        * FIXME: It would be cleaner to apply the general line properties
+        * outside this loop, and limit ourselves to apply_pm3dcolor().
+        */
+       static struct lp_style_type lp = DEFAULT_LP_STYLE_TYPE;
+       lp_use_properties(&lp, pm3d.hidden3d_tag, 0);
+       term_apply_lp_properties(&lp);
+
+       term->move(icorners[0].x, icorners[0].y);
+       for (i = 3; i >= 0; i--) {
+           term->vector(icorners[i].x, icorners[i].y);
+       }
+    }
+}
+
+
+/* The routine above for 4 points explicitly.
+ * This is the only routine which supportes extended
+ * color specs currently.
+ */
+#ifdef EXTENDED_COLOR_SPECS
+void
+filled_quadrangle(gpdPoint * corners, gpiPoint * icorners)
+#else
+void
+filled_quadrangle(gpdPoint * corners)
+#endif
+{
+    int i;
+    double x, y;
+#ifndef EXTENDED_COLOR_SPECS
+    gpiPoint icorners[4];
+#endif
+    for (i = 0; i < 4; i++) {
+       map3d_xy_double(corners[i].x, corners[i].y, corners[i].z, &x, &y);
+       icorners[i].x = x;
+       icorners[i].y = y;
+    }
+
+    ifilled_quadrangle(icorners);
+}
+
+
+/*
+   Makes mapping from real 3D coordinates, passed as coords array,
+   to 2D terminal coordinates, then draws filled polygon
+ */
+void
+filled_polygon_3dcoords(int points, struct coordinate GPHUGE * coords)
+{
+    int i;
+    double x, y;
+    gpiPoint *icorners;
+    icorners = gp_alloc(points * sizeof(gpiPoint), "filled_polygon3d corners");
+    for (i = 0; i < points; i++) {
+       map3d_xy_double(coords[i].x, coords[i].y, coords[i].z, &x, &y);
+       icorners[i].x = x;
+       icorners[i].y = y;
+    }
+#ifdef EXTENDED_COLOR_SPECS
+    if (supply_extended_color_specs) {
+       icorners[0].spec.gray = -1;     /* force solid color */
+    }
+#endif
+    icorners->style = FS_OPAQUE;
+    term->filled_polygon(points, icorners);
+    free(icorners);
+}
+
+
+/*
+   Makes mapping from real 3D coordinates, passed as coords array, but at z coordinate
+   fixed (base_z, for instance) to 2D terminal coordinates, then draws filled polygon
+ */
+void
+filled_polygon_3dcoords_zfixed(int points, struct coordinate GPHUGE * coords, double z)
+{
+    int i;
+    double x, y;
+    gpiPoint *icorners;
+    icorners = gp_alloc(points * sizeof(gpiPoint), "filled_polygon_zfix corners");
+    for (i = 0; i < points; i++) {
+       map3d_xy_double(coords[i].x, coords[i].y, z, &x, &y);
+       icorners[i].x = x;
+       icorners[i].y = y;
+    }
+#ifdef EXTENDED_COLOR_SPECS
+    if (supply_extended_color_specs) {
+       icorners[0].spec.gray = -1;     /* force solid color */
+    }
+#endif
+    icorners->style = FS_OPAQUE;
+    term->filled_polygon(points, icorners);
+    free(icorners);
+}
+
+
+/*
+   Draw colour smooth box
+
+   Firstly two helper routines for plotting inside of the box
+   for postscript and for other terminals, finally the main routine
+ */
+
+
+/* plot the colour smooth box for from terminal's integer coordinates
+   [cb_x_from,cb_y_from] to [cb_x_to,cb_y_to].
+   This routine is for postscript files --- actually, it writes a small
+   PS routine.
+ */
+static void
+draw_inside_color_smooth_box_postscript(FILE * out)
+{
+    int scale_x = (cb_x_to - cb_x_from), scale_y = (cb_y_to - cb_y_from);
+    fputs("stroke gsave\t%% draw gray scale smooth box\n"
+         "maxcolors 0 gt {/imax maxcolors def} {/imax 1024 def} ifelse\n", out);
+
+    /* nb. of discrete steps (counted in the loop) */
+    fprintf(out, "%i %i translate %i %i scale 0 setlinewidth\n", cb_x_from, cb_y_from, scale_x, scale_y);
+    /* define left bottom corner and scale of the box so that all coordinates
+       of the box are from [0,0] up to [1,1]. Further, this normalization
+       makes it possible to pass y from [0,1] as parameter to setgray */
+    fprintf(out, "/ystep 1 imax div def /y0 0 def /ii 0 def\n");
+    /* local variables; y-step, current y position and counter ii;  */
+    if (sm_palette.positive == SMPAL_NEGATIVE) /* inverted gray for negative figure */
+       fputs("{ 0.99999 y0 sub g ", out); /* 1 > x > 1-1.0/1024 */
+    else
+       fputs("{ y0 g ", out);
+    if (color_box.rotation == 'v')
+       fputs("0 y0 N 1 0 V 0 ystep V -1 0 f\n", out);
+    else
+       fputs("y0 0 N 0 1 V ystep 0 V 0 -1 f\n", out);
+    fputs("/y0 y0 ystep add def /ii ii 1 add def\n"
+         "ii imax ge {exit} if } loop\n"
+         "grestore 0 setgray\n", out);
+}
+
+
+
+/* plot the colour smooth box for from terminal's integer coordinates
+   [x_from,y_from] to [x_to,y_to].
+   This routine is for non-postscript files, as it does explicitly the loop
+   over all thin rectangles
+ */
+static void
+draw_inside_color_smooth_box_bitmap(FILE * out)
+{
+    int steps = 128; /* I think that nobody can distinguish more colours drawn in the palette */
+    int i, xy, xy2, xy_from, xy_to;
+    double xy_step, gray;
+    gpiPoint corners[4];
+
+    (void) out;                        /* to avoid "unused parameter" warning */
+    if (color_box.rotation == 'v') {
+       corners[0].x = corners[3].x = cb_x_from;
+       corners[1].x = corners[2].x = cb_x_to;
+       xy_from = cb_y_from;
+       xy_to = cb_y_to;
+    } else {
+       corners[0].y = corners[1].y = cb_y_from;
+       corners[2].y = corners[3].y = cb_y_to;
+       xy_from = cb_x_from;
+       xy_to = cb_x_to;
+    }
+    xy_step = (color_box.rotation == 'h' ? cb_x_to - cb_x_from : cb_y_to - cb_y_from) / (double) steps;
+
+    for (i = 0; i < steps; i++) {
+       gray = (double) i / steps;      /* colours equidistantly from [0,1] */
+       if (sm_palette.positive == SMPAL_NEGATIVE)
+           gray = 1 - gray;
+       /* Set the colour (also for terminals which support extended specs). */
+       set_color(gray);
+       xy = xy_from + (int) (xy_step * i);
+       xy2 = xy_from + (int) (xy_step * (i + 1));
+       if (color_box.rotation == 'v') {
+           corners[0].y = corners[1].y = xy;
+           corners[2].y = corners[3].y = (i == steps - 1) ? xy_to : xy2;
+       } else {
+           corners[0].x = corners[3].x = xy;
+           corners[1].x = corners[2].x = (i == steps - 1) ? xy_to : xy2;
+       }
+#ifdef EXTENDED_COLOR_SPECS
+       if (supply_extended_color_specs) {
+           corners[0].spec.gray = -1;  /* force solid color */
+       }
+#endif
+       /* print the rectangle with the given colour */
+       corners->style = FS_OPAQUE;
+       term->filled_polygon(4, corners);
+    }
+}
+
+/* Notice HBB 20010720: would be static, but HP-UX gcc bug forbids
+ * this, for now */
+void
+cbtick_callback(
+    AXIS_INDEX axis,
+    double place,
+    char *text,
+    struct lp_style_type grid) /* linetype or -2 for no grid */
+{
+    int len = (text ? CB_AXIS.ticscale : CB_AXIS.miniticscale)
+       * (CB_AXIS.tic_in ? -1 : 1) * (term->h_tic);
+    double cb_place = (place - CB_AXIS.min) / (CB_AXIS.max - CB_AXIS.min);
+       /* relative z position along the colorbox axis */
+    unsigned int x1, y1, x2, y2;
+
+#if 0
+    printf("cbtick_callback:  place=%g\ttext=\"%s\"\tgrid.l_type=%i\n",place,text,grid.l_type);
+#endif
+    (void) axis;               /* to avoid 'unused' warning */
+
+    /* calculate tic position */
+    if (color_box.rotation == 'h') {
+       x1 = x2 = cb_x_from + cb_place * (cb_x_to - cb_x_from);
+       y1 = cb_y_from;
+       y2 = cb_y_from - len;
+    } else {
+       x1 = cb_x_to;
+       x2 = cb_x_to + len;
+       y1 = y2 = cb_y_from + cb_place * (cb_y_to - cb_y_from);
+    }
+
+    /* draw grid line */
+    if (grid.l_type > LT_NODRAW) {
+       term_apply_lp_properties(&grid);        /* grid linetype */
+       if (color_box.rotation == 'h') {
+           (*term->move) (x1, cb_y_from);
+           (*term->vector) (x1, cb_y_to);
+       } else {
+           (*term->move) (cb_x_from, y1);
+           (*term->vector) (cb_x_to, y1);
+       }
+       term_apply_lp_properties(&border_lp);   /* border linetype */
+    }
+
+    /* draw tic */
+    (*term->move) (x1, y1);
+    (*term->vector) (x2, y2);
+
+    /* draw label */
+    if (text) {
+       /* get offset */
+       int offsetx, offsety;
+       map3d_position_r(&(axis_array[axis].ticdef.offset),
+                        &offsetx, &offsety, "cbtics");
+       /* User-specified different color for the tics text */
+       if (axis_array[axis].ticdef.textcolor.type != TC_DEFAULT)
+           apply_pm3dcolor(&(axis_array[axis].ticdef.textcolor), term);
+       if (color_box.rotation == 'h') {
+           int y3 = cb_y_from - (term->v_char);
+           int hrotate = 0;
+
+           if (axis_array[axis].tic_rotate
+               && (*term->text_angle)(axis_array[axis].tic_rotate))
+                   hrotate = axis_array[axis].tic_rotate;
+           if (len > 0) y3 -= len; /* add outer tics len */
+           if (y3<0) y3 = 0;
+           write_multiline(x2+offsetx, y3+offsety, text,
+                           (hrotate ? LEFT : CENTRE), CENTRE, hrotate,
+                           axis_array[axis].ticdef.font);
+           if (hrotate)
+               (*term->text_angle)(0);
+       } else {
+           unsigned int x3 = cb_x_to + (term->h_char);
+           if (len > 0) x3 += len; /* add outer tics len */
+           write_multiline(x3+offsetx, y2+offsety, text,
+                           LEFT, CENTRE, 0.0,
+                           axis_array[axis].ticdef.font);
+       }
+       term_apply_lp_properties(&border_lp);   /* border linetype */
+    }
+
+    /* draw tic on the mirror side */
+    if (CB_AXIS.ticmode & TICS_MIRROR) {
+       if (color_box.rotation == 'h') {
+           y1 = cb_y_to;
+           y2 = cb_y_to + len;
+       } else {
+           x1 = cb_x_from;
+           x2 = cb_x_from - len;
+       }
+       (*term->move) (x1, y1);
+       (*term->vector) (x2, y2);
+    }
+}
+
+/*
+   Finally the main colour smooth box drawing routine
+ */
+void
+draw_color_smooth_box(int plot_mode)
+{
+    double tmp;
+    FILE *out = gppsfile;      /* either gpoutfile or PSLATEX_auxfile */
+
+    if (color_box.where == SMCOLOR_BOX_NO)
+       return;
+    if (!term->filled_polygon)
+       return;
+
+    /*
+       firstly, choose some good position of the color box
+
+       user's position like that (?):
+       else {
+       x_from = color_box.xlow;
+       x_to   = color_box.xhigh;
+       }
+     */
+    if (color_box.where == SMCOLOR_BOX_USER) {
+       if (!is_3d_plot) {
+           double xtemp, ytemp;
+           map_position(&color_box.origin, &cb_x_from, &cb_y_from, "cbox");
+           map_position_r(&color_box.size, &xtemp, &ytemp, "cbox");
+           cb_x_to = xtemp;
+           cb_y_to = ytemp;
+       } else if (splot_map && is_3d_plot) {
+           /* In map view mode we allow any coordinate system for placement */
+           double xtemp, ytemp;
+           map3d_position_double(&color_box.origin, &xtemp, &ytemp, "cbox");
+           cb_x_from = xtemp;
+           cb_y_from = ytemp;
+           map3d_position_r(&color_box.size, &cb_x_to, &cb_y_to, "cbox");
+       } else {
+           /* But in full 3D mode we only allow screen coordinates */
+           cb_x_from = color_box.origin.x * (term->xmax) + 0.5;
+           cb_y_from = color_box.origin.y * (term->ymax) + 0.5;
+           cb_x_to = color_box.size.x * (term->xmax) + 0.5;
+           cb_y_to = color_box.size.y * (term->ymax) + 0.5;
+       }
+       cb_x_to += cb_x_from;
+       cb_y_to += cb_y_from;
+
+    } else { /* color_box.where == SMCOLOR_BOX_DEFAULT */
+       if (plot_mode == MODE_SPLOT && !splot_map) {
+           /* HBB 20031215: new code.  Constants fixed to what the result
+            * of the old code in default view (set view 60,30,1,1)
+            * happened to be. Somebody fix them if they're not right! */
+           cb_x_from = xmiddle + 0.709 * xscaler;
+           cb_x_to   = xmiddle + 0.778 * xscaler;
+           cb_y_from = ymiddle - 0.147 * yscaler;
+           cb_y_to   = ymiddle + 0.497 * yscaler;
+
+       } else if (is_3d_plot) {
+           /* MWS 09-Dec-05, make color box full size for splot maps. */
+           double dx = (X_AXIS.max - X_AXIS.min);
+           map3d_xy(X_AXIS.max + dx * 0.025, Y_AXIS.min, base_z, &cb_x_from, &cb_y_from);
+           map3d_xy(X_AXIS.max + dx * 0.075, Y_AXIS.max, ceiling_z, &cb_x_to, &cb_y_to);
+       } else { /* 2D plot */
+           struct position default_origin = {graph,graph,graph, 1.025, 0, 0};
+           struct position default_size = {graph,graph,graph, 0.05, 1.0, 0};
+           double xtemp, ytemp;
+           map_position(&default_origin, &cb_x_from, &cb_y_from, "cbox");
+           map_position_r(&default_size, &xtemp, &ytemp, "cbox");
+           cb_x_to = xtemp + cb_x_from;
+           cb_y_to = ytemp + cb_y_from;
+       }
+
+       /* now corrections for outer tics */
+       if (color_box.rotation == 'v') {
+           int cblen = (CB_AXIS.tic_in ? -1 : 1) * CB_AXIS.ticscale * 
+               (term->h_tic); /* positive for outer tics */
+           int ylen = (Y_AXIS.tic_in ? -1 : 1) * Y_AXIS.ticscale * 
+               (term->h_tic); /* positive for outer tics */
+           if ((cblen > 0) && (CB_AXIS.ticmode & TICS_MIRROR)) {
+               cb_x_from += cblen;
+               cb_x_to += cblen;
+           }
+           if ((ylen > 0) && 
+               (axis_array[FIRST_Y_AXIS].ticmode & TICS_MIRROR)) {
+               cb_x_from += ylen;
+               cb_x_to += ylen;
+           }
+       }
+    }
+
+    if (cb_y_from > cb_y_to) { /* switch them */
+       tmp = cb_y_to;
+       cb_y_to = cb_y_from;
+       cb_y_from = tmp;
+    }
+
+    /* Optimized version of the smooth colour box in postscript. Advantage:
+       only few lines of code is written into the output file.
+     */
+    if (gppsfile)
+       draw_inside_color_smooth_box_postscript(out);
+    else
+       draw_inside_color_smooth_box_bitmap(out);
+
+    if (color_box.border) {
+       /* now make boundary around the colour box */
+       if (color_box.border_lt_tag >= 0) {
+           /* user specified line type */
+           struct lp_style_type lp = border_lp;
+           lp_use_properties(&lp, color_box.border_lt_tag, 1);
+           term_apply_lp_properties(&lp);
+       } else {
+           /* black solid colour should be chosen, so it's border linetype */
+           term_apply_lp_properties(&border_lp);
+       }
+       newpath();
+       (term->move) (cb_x_from, cb_y_from);
+       (term->vector) (cb_x_to, cb_y_from);
+       (term->vector) (cb_x_to, cb_y_to);
+       (term->vector) (cb_x_from, cb_y_to);
+       (term->vector) (cb_x_from, cb_y_from);
+       closepath();
+
+       /* Set line properties to some value, this also draws lines in postscript terminals. */
+           term_apply_lp_properties(&border_lp);
+       }
+
+    /* draw tics */
+    if (axis_array[COLOR_AXIS].ticmode) {
+       term_apply_lp_properties(&border_lp); /* border linetype */
+       gen_tics(COLOR_AXIS, cbtick_callback );
+    }
+
+    /* write the colour box label */
+    if (CB_AXIS.label.text) {
+       int x, y;
+       apply_pm3dcolor(&(CB_AXIS.label.textcolor),term);
+       if (color_box.rotation == 'h') {
+           int len = CB_AXIS.ticscale * (CB_AXIS.tic_in ? 1 : -1) * 
+               (term->v_tic);
+
+           map3d_position_r(&(CB_AXIS.label.offset), &x, &y, "smooth_box");
+           x += (cb_x_from + cb_x_to) / 2;
+
+#define DEFAULT_Y_DISTANCE 1.0
+           y += cb_y_from + (- DEFAULT_Y_DISTANCE - 1.7) * term->v_char;
+#undef DEFAULT_Y_DISTANCE
+           if (len < 0) y += len;
+           if (x<0) x = 0;
+           if (y<0) y = 0;
+           write_multiline(x, y, CB_AXIS.label.text, CENTRE, JUST_CENTRE, 0,
+                           CB_AXIS.label.font);
+       } else {
+           int len = CB_AXIS.ticscale * (CB_AXIS.tic_in ? -1 : 1) *
+               (term->h_tic);
+           /* calculate max length of cb-tics labels */
+           widest_tic_strlen = 0;
+           if (CB_AXIS.ticmode & TICS_ON_BORDER) {
+               widest_tic_strlen = 0; /* reset the global variable */
+               gen_tics(COLOR_AXIS, /* 0, */ widest_tic_callback);
+           }
+           map3d_position_r(&(CB_AXIS.label.offset), &x, &y, "smooth_box");
+#define DEFAULT_X_DISTANCE 1.0
+           x += cb_x_to + (widest_tic_strlen + DEFAULT_X_DISTANCE + 1.5) * term->h_char;
+#undef DEFAULT_X_DISTANCE
+           if (len > 0) x += len;
+           y += (cb_y_from + cb_y_to) / 2;
+           if (x<0) x = 0;
+           if (y<0) y = 0;
+           if ((*term->text_angle)(CB_AXIS.label.rotate)) {
+               write_multiline(x, y, CB_AXIS.label.text, CENTRE, JUST_TOP,
+                               CB_AXIS.label.rotate, CB_AXIS.label.font);
+               (*term->text_angle)(0);
+           } else {
+               write_multiline(x, y, CB_AXIS.label.text, LEFT, JUST_TOP, 0, CB_AXIS.label.font);
+           }
+       }
+       reset_textcolor(&(CB_AXIS.label.textcolor),term);
+    }
+
+}
+