Graphics: fix bug after uid refactoring
[scilab.git] / scilab / modules / graphics / src / c / Format.c
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 1998-2001 - ENPC - Jean-Philippe Chancelier
4  * Copyright (C) 2001 - INRIA - Fran├žois Delebecque
5  * Copyright (C) 2004-2006 - INRIA - Fabrice Leray
6  * Copyright (C) 2006 - INRIA - Jean-Baptiste Silvy
7  * Copyright (C) 2009 - DIGITEO - Pierre Lando
8  * Copyright (C) 2011 - DIGITEO - Manuel Juliachs
9  *
10  * This file must be used under the terms of the CeCILL.
11  * This source file is licensed as described in the file COPYING, which
12  * you should have received as part of this distribution.  The terms
13  * are also available at
14  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
15  *
16  */
17
18 /*------------------------------------------------------------------------
19  *    Graphic library
20  *    Copyright (C) 1998-2001 Enpc/Jean-Philippe Chancelier
21  *    jpc@cermics.enpc.fr
22  --------------------------------------------------------------------------*/
23 /*------------------------------------------------------------------------
24  * Axis drawing for 2d plots (format selection)
25  *
26  * void  ChoixFormatE(fmt, desres, xmin, xmax, xpas) : find a format
27  * void  ChoixFormatE1(fmt, desres, xx, nx)          : find a format
28  * int   C2F(graduate)(xmi,xma,xi,xa,np1,np2,kminr,kmaxr,ar)
29  *                : change [xmi,xmax] for pretty graduation
30  *--------------------------------------------------------------------------*/
31
32 #if defined(__linux__)
33 #define _GNU_SOURCE /* Bug 5673 fix: avoid dependency on GLIBC_2.7 */
34 #endif
35
36 #include <stdio.h>
37 #include <string.h>
38 #include "math_graphics.h"
39 #include "Format.h"
40 #include "MALLOC.h"
41 #include "GetProperty.h"
42 #include "BasicAlgos.h"
43 #include "sciprint.h"
44 #include "localization.h"
45 #include "Scierror.h"
46 #include <machine.h>
47
48 #include "getGraphicObjectProperty.h"
49 #include "graphicObjectProperties.h"
50
51 #define MAX(A,B) ((A<B)?B:A)
52
53 static double spans[18] = {10, 12, 14, 15, 16, 18, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100};
54 static int ticks[18] = {11, 7, 8, 4, 9, 10, 11, 6, 7, 8, 9, 10, 11, 7, 8, 9, 10, 11};
55 static double width[18] = {1, 2, 2, 5, 2, 2, 2, 5, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10};
56
57 /** Maximum of ticks for log mode */
58 #define MAX_LOG_TICKS 15
59
60 /* end here */
61
62 extern double C2F(dlamch)  (char *CMACH, unsigned long int);
63
64 static void FormatPrec (char *fmt, int *desres, double xmin, double xmax,
65                         double xpas);
66 static void FormatPrec1 (char *fmt, int *desres, double *xx, int nx);
67 static int Fsepare (char *fmt, int dec, int *l, double xmin, double xmax,
68                     double xpas);
69 static int Fsepare1 (char *fmt, int dec, int *l, double *xx, int nx);
70 static void graduate1 (double *xmi, double* xma, double* xi, double* xa,
71                        int * np1, int * np2, int * kminr, int * kmaxr, int * ar, int count);
72
73 static void gradua (double *xmi, double *xma, int * kminr, int *kmaxr, int *ar, int *npr, int *b);
74 static void decompSup (double x, int * xk, int *  xa, int   b);
75 static void decompInf (double x, int * xk, int *  xa, int   b);
76
77 static void removeIndex(double* changedArray, int size, int ind);
78 static void removeBadTicks(double* ticks, BOOL * removedTicks, int * nbTicks);
79 static void GradFixedlog(double minVal, double maxVal, double* ticks, int nbGrads);
80 static int GradLog(double _min, double _max, double *_grads, int *n_grads, int compNgrads);
81
82 /**
83  * ChoixFormatE returns a format ("%.*f" or "%.*e")
84  * in fmt given xmin,xmax,pas.
85  *   fmt : character string
86  * fmt gives a format which can be used to display
87  * number in range xmin:step:xmax
88  * Exemple : ChoixFormatE(format,min,max,step);
89  *           fprintf(format,min+k*step);
90  * The format is searched so as to give distinct values
91  * for the numeric values xmin + k*xpas in [xmin,xmax]
92  * and give enough precision.
93  */
94 static void ChoixFormatE(char *fmt, double xmin, double xmax, double xpas)
95 {
96     char c = 0;
97     int des = 0, len = 0;
98     /* format f minimal  */
99     for (des = 0 ; des < 5 ; des++)
100     {
101         if (Fsepare("%.*f", des, &len, xmin, xmax, xpas))
102         {
103             break;
104         }
105     }
106     if (des < 5 && len <= 6)
107     {
108         c = 'f';
109         strcpy(fmt, "%.*f");
110     }
111     else
112     {
113         for (des = 0 ; des < 5 ; des++)
114         {
115             if (Fsepare("%.*e", des, &len, xmin, xmax, xpas))
116             {
117                 break;
118             }
119         }
120         c = 'e';
121         strcpy(fmt, "%.*e");
122     }
123     FormatPrec(fmt, &des, xmin, xmax, xpas);
124     sprintf(fmt, "%%.%d%c", des, c);
125 }
126
127 /*
128  *  checks if given format gives enough precision
129  *  if not increase it (i.e increase desres)
130  */
131
132 static void FormatPrec(char *fmt, int *desres, double xmin, double xmax, double xpas)
133 {
134     char buf1[100], buf2[100];
135     int i = 0;
136     while (xmin + ((double)i)*xpas < xmax && *desres  < 10)
137     {
138         double x1, x2, yy1;
139         yy1 = xmin + ((double) i) * xpas;
140         sprintf(buf1, fmt, *desres, yy1);
141         sprintf(buf2, fmt, *desres, yy1 + xpas);
142         sscanf(buf1, "%lf", &x1);
143         sscanf(buf2, "%lf", &x2);
144         if (  Abs((x2 - x1 - xpas) / xpas) >= 0.1)
145         {
146             *desres += 1;
147         }
148         if (  Abs((x1 - yy1) / xpas) >= 0.01)
149         {
150             *desres += 1;
151         }
152         i++;
153     }
154 }
155
156 /*
157  *  checks if format fmt gives different values for numbers
158  *  from xmin to xmax with step xpas. It also returns in variable l
159  *  the string length that will result in using the format
160  */
161
162 static int Fsepare(char *fmt, int dec, int *l, double xmin, double xmax, double xpas)
163 {
164     double x = xmin;
165     char buf1[100], buf2[100];
166     *l = 0;
167     /**  Take care of : sprintf(buf1,"%.*f",0,1.d230) which overflow in buf1 **/
168     /**  we don't use %.*f format if numbers are two big **/
169     if (strcmp("%.*f", fmt) == 0 && (Abs(xmax) > 1.e+10 || Abs(xmin) > 1.e+10))
170     {
171         return (0);
172     }
173     sprintf(buf1, fmt, dec, xmin);
174     while (x < xmax)
175     {
176         x += xpas;
177         strcpy(buf2, buf1);
178         sprintf(buf1, fmt, dec, x);
179         *l = (((int)strlen(buf1) >= *l) ? (int) strlen(buf1) : *l);
180         if (strcmp(buf1, buf2) == 0)
181         {
182             return (0);
183         }
184     }
185     return (1);
186 }
187
188 void ChoixFormatE1(char *fmt, double *xx, int nx)
189 {
190     char c = 0;
191     int des = 0, len = 0;
192     /* format f minimal  */
193     for (des = 0 ; des < 5 ; des++)
194     {
195         if (Fsepare1("%.*f", des, &len, xx, nx))
196         {
197             break;
198         }
199     }
200     if (des < 5 && len <= 6)
201     {
202         c = 'f';
203         strcpy(fmt, "%.*f");
204     }
205     else
206     {
207         for (des = 0 ; des < 5 ; des++)
208         {
209             if (Fsepare1("%.*e", des, &len, xx, nx))
210             {
211                 break;
212             }
213         }
214         c = 'e';
215         strcpy(fmt, "%.*e");
216     }
217     FormatPrec1(fmt, &des, xx, nx);
218     sprintf(fmt, "%%.%d%c", des, c);
219 }
220
221
222 /*----------------------------------------------------------
223  *  checks if format fmt gives different values for numbers
224  *  from xmin to xmax with step xpas. It also returns in variable l
225  *  the string length that will result in using the format
226  *------------------------------------------------------*/
227
228 static void FormatPrec1(char *fmt, int *desres, double *xx, int nx)
229 {
230     char buf1[100], buf2[100];
231     double xpas = 0.;
232     int i = 0;
233     while (i < nx - 1 && *desres  < 10)
234     {
235         double x1 = 0., x2 = 0.;
236         sprintf(buf1, fmt, *desres, xx[i]);
237         sprintf(buf2, fmt, *desres, xx[i + 1]);
238         sscanf(buf1, "%lf", &x1);
239         sscanf(buf2, "%lf", &x2);
240         xpas = xx[i + 1] - xx[i];
241         if (xpas != 0.0)
242         {
243             if (Abs((x2 - x1 - xpas) / xpas) >= 0.1)
244             {
245                 *desres += 1;
246             }
247             if (Abs((x1 - xx[i]) / xpas) >= 0.1)
248             {
249                 *desres += 1;
250             }
251         }
252         i++;
253     }
254 }
255
256 static int Fsepare1(char *fmt, int dec, int *l, double *xx, int nx)
257 {
258     char buf1[100], buf2[100];
259     int i = 0;
260     *l = 0;
261     /**  Take care of : sprintf(buf1,"%.*f",0,1.d230) which overflow in buf1 **/
262     /**  we don't use %.*f format if numbers are two big **/
263     if (strcmp("%.*f", fmt) == 0 && (Abs(xx[nx - 1]) > 1.e+10 || Abs(xx[0]) > 1.e+10))
264     {
265         return (0);
266     }
267     sprintf(buf1, fmt, dec, xx[0]);
268     for (i = 1 ; i < nx ; i++)
269     {
270         strcpy(buf2, buf1);
271         sprintf(buf1, fmt, dec, xx[i]);
272         *l = (((int)strlen(buf1) >= *l) ? (int) strlen(buf1) : *l);
273         if (strcmp(buf1, buf2) == 0)
274         {
275             return (0);
276         }
277     }
278     return (1);
279 }
280
281 /*----------------------------------------------------
282  * int C2F(graduate)(xmi,xma,xi,xa,np1,np2,kminr,kmaxr,ar)
283  * (xgraduate at Scilab level)
284  * Rescale an interval so as to find a pretty graduation
285  * for [xmi,xma] given seeks (xi,xa,np1,np2)
286  * such that  xi <= xmi <= xmax <= xa
287  * with xi et xa  numbers of type  kminr 10^ar and kmaxr 10^ar.
288  * then the interval [xi,xa] can be splited in *np2 sub-intervals
289  *  (kminr-kmaxr can be divided by *np2)
290  *  x_i= (kminr + i*(kmaxr-kminr)/ (*np2))*10^ar;
291  * i=0:(*np2)
292  * ecah of the  np2 intervals can in turn be splited in np1 ungraduated
293  * subintervals
294  * [np1,np2] follow the nax parameters of plot2d.
295  *
296  *  We also want to keep np2 small (*np2 <=10)
297  *  and we want [xi,xa] to be as close as possible to the interval
298  *  [xmi,xma]
299  *---------------------------------------------------- */
300
301 int C2F(graduate)(double *xmi, double *xma, double *xi, double *xa, int *np1, int *np2, int *kminr, int *kmaxr, int *ar)
302 {
303     if (*xmi > *xma)
304     {
305         double xma1 = *xmi, xmi1 = *xma;
306         graduate1(&xmi1, &xma1, xi, xa, np1, np2, kminr, kmaxr, ar, 0);
307     }
308     else
309     {
310         graduate1(xmi, xma, xi, xa, np1, np2, kminr, kmaxr, ar, 0);
311     }
312     return (0);
313 }
314
315 static void graduate1(double *xmi, double *xma, double *xi, double *xa, int *np1, int *np2, int *kminr, int *kmaxr, int *ar, int count)
316 {
317     int npr = 0, b = 0, i = 0, dx = 0, dxmi = 0, dxma = 0;
318     /* fprintf(stderr,"[%20.10f,%20.10f]\n",*xmi,*xma); */
319     /*
320      *
321      */
322     dx   = ((*xma) != (*xmi)) ? (int) ceil(log10(Abs((*xma) - (*xmi)))) : 0;
323     dxmi = (*xmi != 0) ? (int) ceil(log10(Abs((*xmi)))) : 0;
324     dxma = (*xma != 0) ? (int) ceil(log10(Abs((*xma)))) : 0;
325     dx = Max(dx - dxmi, dx - dxma);
326     /* il faut limiter b de sorte que dans la decomposition */
327     /* avec b les nombres entiers manipules ne deviennent pas trop grands */
328     /* on choisit donc b < 10 en considerant que le plus grand entier est */
329     /* 0x7FFFFFFF */
330     /* on prends aussi un b minimum de 3 : pour avoir des intervalles */
331     /* plus serr'es  : ce nombre est 'eventuellement a affiner      */
332     b = Max(-dx + 2, 3);
333     /* fprintf(stderr,"choix de b=%d",b); */
334     if (b >= 10)
335     {
336         double xmi1 = 0., xma1 = 0.;
337         int iexp = 0;
338         /* fprintf(stderr,"je ne peux decomposer les 2 nombres sont identiques\n"); */
339         /*
340         a la precision donnee les deux nombre ne peuvent etre decomposer
341         kmin,kmax devrait sinon depasser maxint
342         on les ecarte de ce qu'il faut pour pouvoir
343         les separer.
344         Attention : il faut faire attention de bien choisir iexp
345         pour ne pas boucler la dedans
346         */
347         iexp = 9 - dxmi - 1;
348         xmi1 = *xmi - exp10((double) - iexp);
349         xma1 = *xmi + exp10((double) - iexp);
350         if (count > 1)
351         {
352             sciprint(_("Internal Error: Loop in graduate1\n"));
353             sciprint(_("Please send a Bug report to dev@lists.scilab.org\n"));
354         }
355         graduate1(&xmi1, &xma1, xi, xa, np1, np2, kminr, kmaxr, ar, count + 1);
356         return;
357     }
358     while (b >= 1)
359     {
360         /* fprintf(stderr,"\tAppel avec b=%d\n",b); */
361         gradua(xmi, xma, kminr, kmaxr, ar, &npr, &b);
362         *xi = (*kminr) * exp10((double) * ar);
363         *xa = (*kmaxr) * exp10((double) * ar);
364         /** fprintf(stderr,"\tRes=[%20.10f,%20.10f]-->[%d,%d,10^%d,%d]\n",*xi,*xa
365             ,*kminr,*kmaxr,*ar,npr); */
366         *np2 = npr;
367         if (*np2 <= 20)
368         {
369             break;
370         }
371         else
372         {
373             b--;
374         }
375     }
376     /*
377       on veut essayer de ne pas depasser 10 intervalles (*np2 <= 10)
378       pour les intervalle ou on ecrit un nombre,
379       or on peut en avoir jusqu'a 20. On regarde si le nombre d'intervalle
380       est divisible. on aura alors une graduation en np2 pour l'ecriture
381       des nombres et une sous graduation np1 juste avec des tirets.
382       */
383     *np1 = 2 ;
384     if ( *np2 <= 10 )
385     {
386         return ;
387     }
388     /* le nombre est > 10 : s'il est impair on rajoute 1
389        pour diviser par deux */
390     if (*np2 % 2 == 1)
391     {
392         int step;
393         step = (*kmaxr - *kminr) / (*np2);
394         (*np2)++;
395         *kmaxr += step;
396         *xa =  (*kmaxr) * exp10((double) * ar);
397     }
398     /* On recherche des diviseurs a nouveaux pour diminuer le nombre
399        d'intervalles */
400     for (i = 2 ; i <= 10 ; i++)
401     {
402         if (*np2 % i == 0)
403         {
404             *np1 = i, *np2 /= i;
405             return;
406         }
407     }
408     *np1 = *np2;
409     *np2 = 1;
410 }
411
412 /*
413  *  renvoit kminr,kmaxr et ar tels que
414  *  [kminr*10^ar,kmaxr*10^ar] contient [xmi,xma]
415  *  b est un parametre de decompSup,decompInf
416  *  on doit avoir a l'appel xmi < xma.
417  *  le choix se fait entre deux intervalles possibles
418  *  on choisit celui qui est le plus proche de [xmi,xma]
419  *  a condition que (kmaxr-kminr) <= 20
420  *  pour b=1 on sait que (kmaxr-kminr) <= 20
421  *  20 intervalles au plus (que l'on obtient si xmin et xmax sont
422  *  de signe opposes sinon c'est 10)
423  */
424
425 /* np2, and np1 must be smaller than maxint */
426
427 #define DMAX 0xFFFFFFF
428
429 static void gradua(double *xmi, double *xma, int *kminr, int *kmaxr, int *ar, int *npr, int *b)
430 {
431     double x0 = *xmi, x1 = *xma, loc = 0.;
432     int x0k = 0, x0a = 0;
433     int x1k = 0, x1a = 0;
434     int kmin1 = 0, kmax1 = 0, a1 = 0, np1 = 0, kmin2 = 0, kmax2 = 0, a2 = 0, np2 = 0, kmin = 0, kmax = 0, a = 0, np = 0;
435     decompInf(x0, &x0k, &x0a, *b);
436     decompSup(x1, &x1k, &x1a, *b);
437     /** special cases **/
438     if (x1 == 0.0)
439     {
440         x1a = x0a;
441     }
442     if (x0 == 0.0)
443     {
444         x0a = x1a;
445     }
446     loc = Min(floor(x0 * exp10((double) - x1a)), ((double)DMAX));
447     if (loc < 0)
448     {
449         loc = Max(loc, -((double) DMAX));
450     }
451     kmin1 = (int) loc;
452     kmax1 = x1k;
453     np1 = Abs(kmax1 - kmin1);
454     np1 = (np1 < 0) ? DMAX : np1;
455     if (np1 > 10)
456     {
457         if  ((np1 % 2) == 0)
458         {
459             np1 /= 2;
460         }
461         else
462         {
463             np1++;
464             np1 /= 2;
465             kmax1++;
466         }
467     }
468     a1 = x1a;
469     /* fprintf(stderr,"\t\tsols : [%d,%d].10^%d,n=%d\t",kmin1,kmax1,a1,np1);  */
470     kmin2 = x0k;
471     loc = Min(ceil(x1 * exp10((double) - x0a)), ((double)DMAX));
472     kmax2 = (int) loc;
473     np2 = Abs(kmax2 - kmin2);
474     np2 = (np2 < 0) ? DMAX : np2;
475     if (np2 > 10)
476     {
477         if (np2 % 2 == 0)
478         {
479             np2 /= 2;
480         }
481         else
482         {
483             np2++;
484             kmin2--;
485         }
486     }
487     a2 = x0a;
488     /* fprintf(stderr,"[%d,%d].10^%d=%d\n",kmin2,kmax2,a2,np2);  */
489     if (np1 * exp10((double)a1) < np2 * exp10((double) a2))
490     {
491         if (np1 <= 20)
492         {
493             kmin = kmin1;
494             kmax = kmax1;
495             np = np1;
496             a = a1;
497         }
498         else
499         {
500             kmin = kmin2;
501             kmax = kmax2;
502             np = np2;
503             a = a2;
504         }
505     }
506     else
507     {
508         if (np2 <= 20)
509         {
510             kmin = kmin2;
511             kmax = kmax2;
512             np = np2;
513             a = a2;
514         }
515         else
516         {
517             kmin = kmin1;
518             kmax = kmax1;
519             np = np1;
520             a = a1;
521         }
522     }
523     *kminr = kmin;
524     *kmaxr = kmax;
525     *ar = a;
526     *npr = np;
527     if (kmin == kmax)
528     {
529         /*
530          * a la precision demandee les deux [xi,xa] est reduit a un point
531          * on elargit l'intervalle
532          */
533         /* fprintf(stderr,"Arg : kmin=kmax=%d",kmin) ; */
534         /* fprintf(stderr," a=%d, x0=%f,x1=%f\n",a,x0,x1); */
535         (*kminr)--;
536         (*kmaxr)++;
537         *npr = 2;
538     };
539 }
540
541 /*
542  * soit x > 0 reel fixe et b entier fixe : alors il existe un unique couple
543  * (k,a) dans NxZ avec k dans [10^(b-1)+1,10^b] tel que
544  * (k-1)*10^a < x <= k 10^a
545  * donne par  a = ceil(log10(x))-b et k=ceil(x/10^a)
546  * decompSup renvoit xk=k et xa=a
547  * si x < 0 alors decompSup(x,xk,xa,b)
548  *    s'obtient par decompInf(-x,xk,xa,b) et xk=-xk
549  * Remarque : la taille de l'entier k obtenu est controle par b
550  * il faut choisir b < 10 pour ne pas depasser dans k l'entier maximum
551  */
552
553 static void decompSup(double x, int *xk, int *xa, int b)
554 {
555     if (x == 0.0)
556     {
557         *xk = 0;
558         *xa = 1; /* jpc */
559     }
560     else
561     {
562         if (x > 0)
563         {
564             double xd;
565             static double epsilon;
566             static int first = 0;
567             if (first == 0)
568             {
569                 epsilon = 10.0 * F2C(dlamch)("e", 1L);
570                 first++;
571             }
572             /* if x is very near (k+1)10^a (epsilon machine)
573              * we increment xk
574              */
575             *xa = (int) ceil(log10(x)) - b;
576             *xk = (int) ceil(x / exp10((double) * xa));
577             xd = (*xk - 1) * exp10((double) * xa);
578             if (Abs((x - xd) / x) < epsilon)
579             {
580                 *xk -= 1;
581             }
582         }
583         else
584         {
585             decompInf(-x, xk, xa, b);
586             *xk = -(*xk);
587         }
588     }
589 }
590
591
592 /*
593  * soit x > 0 alors il existe un unique couple
594  * (k,a) dans NxZ avec k in [10^(b-1),10^b-1] tel que
595  * (k)*10^a <= x < (k+1) 10^a
596  * donne par
597  * a = floor(log10(x))-b+1 et k = floor(x/10^a)
598  * decompInf renvoit xk=k et xa=a
599  * si x < 0 alors decompInf(x,xk,xa,b)
600  *    s'obtient par decompSup(-x,xk,xa,b) et xk=-xk
601  */
602
603 static void decompInf(double x, int *xk, int *xa, int b)
604 {
605     if (x == 0.0)
606     {
607         *xk = 0;
608         *xa = 1; /* jpc */
609     }
610     else
611     {
612         if (x > 0)
613         {
614             double xup;
615             static double epsilon;
616             static int first = 0;
617             if (first == 0)
618             {
619                 epsilon = 10.0 * F2C(dlamch)("e", 1L);
620                 first++;
621             }
622             *xa = (int) floor(log10(x)) - b + 1;
623             *xk = (int) floor(x / exp10((double) * xa));
624             /* if x is very near (k+1)10^a (epsilon machine)
625              * we increment xk
626              */
627             xup = (*xk + 1) * exp10((double) * xa);
628             if (Abs((x - xup) / x) < epsilon)
629             {
630                 *xk += 1;
631             }
632         }
633         else
634         {
635             decompSup(-x, xk, xa, b);
636             *xk = -(*xk);
637         }
638     }
639 }
640
641 /*--------------------------------------------------------------------------*/
642 /* remove an element in the array from translating the next
643 elements on step backward */
644 static void removeIndex(double* changedArray, int size, int ind)
645 {
646     int i  = 0;
647     for (i = ind + 1 ; i < size ; i++)
648     {
649         changedArray[i - 1] = changedArray[i];
650     }
651 }
652 /*--------------------------------------------------------------------------*/
653 /* remove in the ticks array the indices i such as removedTicks[i] */
654 /* is true. The value nbtics is an in-out variable */
655 static void removeBadTicks(double* curTicks, BOOL * removedTicks, int * nbTicks)
656 {
657     int i  = 0;
658     for (i = *nbTicks - 1 ; i >= 0 ; i--)
659     {
660         if (removedTicks[i])
661         {
662             removeIndex(curTicks, *nbTicks, i);
663             *nbTicks = *nbTicks - 1;
664         }
665     }
666 }
667 /*--------------------------------------------------------------------------*/
668 /* compute the graduation of the segment [minVal,maxVal] knowing the number of ticks */
669 static void GradFixedlog(double minVal, double maxVal, double* outTicks, int nbGrads)
670 {
671     int initSize  = 0;
672     int i = 0;
673
674     /* intialize the array as usual */
675     double tempTicks[20];
676     GradLog(minVal, maxVal, tempTicks, &initSize, FALSE);
677
678     if (initSize > nbGrads)
679     {
680         /* we create a smaller vector from a bigger one */
681         int nbRemove = initSize - nbGrads;
682
683         BOOL * removedTicks = NULL;
684         if ((removedTicks = MALLOC(initSize * sizeof(BOOL))) == NULL)
685         {
686             return;
687         }
688
689         for (i = 0 ; i < initSize ; i++)
690         {
691             removedTicks[i] = FALSE;
692         }
693
694         /* we now remove the nbremove indexes : round((0.5 + i) * size / nbRemove) */
695         /* i=0..nbReg-1 should do the thing */
696         for (i = 0 ; i < nbRemove ; i++)
697         {
698             int remIndex = 1 + (int) round( i  * ((double) initSize - 2) / ((double) nbRemove));
699             removedTicks[remIndex] = TRUE;
700         }
701
702         removeBadTicks(tempTicks, removedTicks, &initSize);
703
704         FREE(removedTicks);
705
706     }
707     doubleArrayCopy(outTicks, tempTicks, nbGrads);
708
709 }
710
711
712 /* compute the automatic graduation of the segment [_min,_max] and store it in _grads */
713 /* the number of graduation may be fixed if compNgrads is TRUE or automatically computed */
714 /* otherwise. */
715 static int GradLog(double   _min   ,
716                    double   _max   ,
717                    double* _grads ,
718                    int    * n_grads,
719                    int      compNgrads)
720 {
721     int i = 0;
722     int log_min = 0, log_max = 0;
723     int size = 0;
724
725     if (compNgrads)
726     {
727         GradFixedlog(_min, _max, _grads, *n_grads);
728         return 0;
729     }
730
731     log_max =  (int) ceil(_max);
732     log_min =  (int) floor(_min);
733
734     /* If _min == _max, enlarge the interval*/
735     if (log_max == log_min)
736     {
737         log_max++;
738         log_min--;
739     }
740
741     size = log_max - log_min + 1;
742
743     *n_grads = 0;
744
745     if (size <= MAX_LOG_TICKS)
746     {
747         for (i = 0; i < size; i++)
748         {
749             _grads[i] = log_min + i;
750             *n_grads = (*n_grads) + 1;
751         }
752     }
753     else
754     {
755         int pas = 0, old_pas = 0, j;
756         int val = size, passed = 0;
757
758         /* Try to reduce number of ticks, by finding the greatest divider */
759         for (j = val - 1; j > 1; j--)
760             if (val % j == 0)
761             {
762                 old_pas = pas;
763                 pas = j;
764                 passed = 1;
765
766                 if ((MAX_LOG_TICKS * pas) <= val)
767                 {
768                     if (old_pas != 0)
769                     {
770                         pas = old_pas;
771                     }
772                     break;
773                 }
774             }
775
776         /* If we haven't found a divider or if the number of ticks will be to large */
777         /* Use only towo ticks */
778         if (passed != 1 || (size / pas) >  MAX_LOG_TICKS)
779         {
780             pas = size;
781         }
782
783         if (pas == size)
784         {
785             _grads[0] = log_min;
786             _grads[1] = log_max;
787             *n_grads = 2;
788         }
789         else
790         {
791             for (i = 0; i <= (int)(size / pas); i++)
792             {
793                 _grads[i] = log_min + (i * pas);
794
795                 *n_grads = (*n_grads) + 1;
796             }
797         }
798     }
799
800     return 0;
801 }
802
803 /**
804 * get the exponent used for log axis from given data bounds
805 * @return 0 if OK, -1 if negative bounds.
806 */
807 int sciGetLogExponent(double minBound, double maxBound, double* expMin, double* expMax)
808 {
809     if (minBound > 0)
810     {
811         *expMin = floor(log10(minBound));
812         *expMax = ceil( log10(maxBound));
813         return 0;
814     }
815     *expMax = 1.0;
816     *expMin = 0.0;
817     return -1;
818 }
819 /*--------------------------------------------------------------------------*/
820 /*
821  * This function has been adapted to the MVC framework (property get calls)
822  * in order to be able to provide a valid format string when computing
823  * default labels for the Axis object. The algorithm is left untouched.
824  * Its code ought to be put within the Java part of the Model.
825  */
826 int ComputeC_format(int iObjUID, char * c_format)
827 {
828     int i = 0, j = 0;
829     int pos = 0;
830     int* piPos = &pos;
831     int xy_type = 0;
832     int* piXy_type = &xy_type;
833     int nx = 0;
834     int* piNx = &nx;
835     int ny = 0;
836     int* piNy = &ny;
837     double *x = NULL;
838     double *y = NULL;
839     double* tmpx = NULL;
840     double* tmpy = NULL;
841     int iType = -1;
842     int *piType = &iType;
843     int  xpassed = 0, ypassed = 0, Nx = 0, Ny = 0, x3, y3;
844     int parentAxesID;
845     int * piParentAxesID = &parentAxesID;
846     int logFlag = 0;
847     int* piLogFlag = &logFlag;
848
849     getGraphicObjectProperty(iObjUID, __GO_TYPE__, jni_int, (void **)&piType);
850
851     if (iType != __GO_AXIS__)
852     {
853         Scierror(999, _("Error: ComputeFormat must be used with AXIS objects\n"));
854         return -1;
855     }
856
857     getGraphicObjectProperty(iObjUID, __GO_PARENT_AXES__, jni_int, (void **)&piParentAxesID);
858
859     getGraphicObjectProperty(iObjUID, __GO_TICKS_DIRECTION__, jni_int, (void **)&piPos);
860     getGraphicObjectProperty(iObjUID, __GO_TICKS_STYLE__, jni_int, (void **)&piXy_type);
861
862     getGraphicObjectProperty(iObjUID, __GO_X_NUMBER_TICKS__, jni_int, (void **)&piNx);
863     getGraphicObjectProperty(iObjUID, __GO_Y_NUMBER_TICKS__, jni_int, (void **)&piNy);
864
865     /* Allocating space before re-copying values to not pollute the good values
866     that will be used inside Axes.c */
867     if ((x = MALLOC(nx * sizeof(double))) == NULL)
868     {
869         Scierror(999, _("%s: No more memory.\n"), "ComputeC_format");
870         return -1;
871     }
872
873     if ((y = MALLOC(ny * sizeof(double))) == NULL)
874     {
875         Scierror(999, _("%s: No more memory.\n"), "ComputeC_format");
876         FREE(x);
877         return -1;
878     }
879
880     getGraphicObjectProperty(iObjUID, __GO_X_TICKS_COORDS__, jni_double_vector, (void **)&tmpx);
881     getGraphicObjectProperty(iObjUID, __GO_Y_TICKS_COORDS__, jni_double_vector, (void **)&tmpy);
882
883     for (i = 0; i < nx; i++)
884     {
885         x[i] = tmpx[i];
886     }
887
888     for (i = 0; i < ny; i++)
889     {
890         y[i] = tmpy[i];
891     }
892
893     /* Algo. here */
894     if (xy_type == 2)
895     {
896         if (pos == 0 || pos == 1)
897         {
898             getGraphicObjectProperty(iObjUID, __GO_X_AXIS_LOG_FLAG__, jni_int, (void **)&piLogFlag);
899
900             if (logFlag == 0)
901             {
902                 while (x[3] > 10)
903                 {
904                     x[3] = floor(x[3] / 2);
905                 }
906             }
907             else
908             {
909                 if (x[3] > 12)
910                 {
911                     /* F.Leray arbitrary value=12 for the moment */
912                     x3 = (int)x[3];   /* if x[3]>12 algo is triggered to search a divisor */
913                     for (j = x3 - 1; j > 1; j--)
914                     {
915                         if (x3 % j == 0)
916                         {
917                             x[3] = j;
918                             xpassed = 1;
919                         }
920                     }
921                     if (xpassed != 1)
922                     {
923                         x[3] = 1;
924                     }
925                 }
926             }
927         }
928         else if (pos == 2 || pos == 3)
929         {
930             getGraphicObjectProperty(iObjUID, __GO_Y_AXIS_LOG_FLAG__, jni_int, (void **)&piLogFlag);
931
932             if (logFlag == 0)
933             {
934                 while (y[3] > 10)
935                 {
936                     y[3] = floor(y[3] / 2);
937                 }
938             }
939             else
940             {
941                 if (y[3] > 12)
942                 {
943                     y3 = (int)y[3];
944                     for (j = y3 - 1; j > 1; j--)
945                     {
946                         if (y3 % j == 0)
947                         {
948                             y[3] = j;
949                             ypassed = 1;
950                         }
951                     }
952                     if (ypassed != 1)
953                     {
954                         y[3] = 1;
955                     }
956                 }
957             }
958         }
959     }
960
961
962     /** Real to Pixel values **/
963     if (xy_type == 0)
964     {
965         Nx = nx;
966         Ny = ny;
967     }
968     else if (xy_type == 1)
969     {
970         if (pos == 0 || pos == 1)
971         {
972             Nx = (int) x[2] + 1;
973         }
974         else if (pos == 2 || pos == 3)
975         {
976             Ny = (int) y[2] + 1;
977         }
978     }
979     else if (xy_type == 2)
980     {
981         if (pos == 0 || pos == 1)
982         {
983             Nx = (int) x[3] + 1;
984         }
985         else if (pos == 2 || pos == 3)
986         {
987             Ny = (int) y[3] + 1;
988         }
989     }
990     else
991     {
992         Scierror(999, _("%s: Wrong type argument %s.\n"), "Sci_Axis", "xy_type");
993         FREE(x);
994         x = NULL;
995         FREE(y);
996         y = NULL;
997         return -1;
998     }
999
1000     if (pos == 0 || pos == 1)
1001     {
1002         /** Horizontal axes **/
1003         /** compute a format **/
1004         if (xy_type == 0)
1005         {
1006             ChoixFormatE1(c_format, x, Nx);
1007         }
1008         else if (xy_type == 1)
1009         {
1010             ChoixFormatE (c_format, x[0], x[1], (x[1] - x[0]) / x[2]);
1011         }
1012         else if (xy_type == 2)
1013         {
1014             ChoixFormatE (c_format,
1015                           (x[0] * exp10(x[2])),
1016                           (x[1] * exp10(x[2])),
1017                           ((x[1] * exp10(x[2])) - (x[0] * exp10(x[2]))) / x[3]);
1018             /* Adding F.Leray 06.05.04 */
1019         }
1020         /** the horizontal segment **/
1021     }
1022     else if (pos == 2 || pos == 3)
1023     {
1024         /** Vertical axes **/
1025         if (xy_type == 0)
1026         {
1027             ChoixFormatE1(c_format, y, Ny);
1028         }
1029         else if (xy_type == 1)
1030         {
1031             ChoixFormatE(c_format, y[0], y[1], (y[1] - y[0]) / y[2]);
1032         }
1033         else if (xy_type == 2)
1034         {
1035             ChoixFormatE (c_format,
1036                           (y[0] * exp10(y[2])),
1037                           (y[1] * exp10(y[2])),
1038                           ((y[1] * exp10(y[2])) - (y[0] * exp10(y[2]))) / y[3]);
1039             /* Adding F.Leray 06.05.04 */
1040         }
1041         /** the vertical segment **/
1042     }
1043
1044     /* c_format should be filled now */
1045
1046     FREE(x);
1047     x = NULL;
1048     FREE(y);
1049     y = NULL;
1050
1051     return 0;
1052 }
1053 /*--------------------------------------------------------------------------*/
1054 /*
1055  * This function has been updated for the MVC (property get calls).
1056  * Its code ought to be put within the Java part of the Model.
1057  */
1058 int ComputeXIntervals(int iObjUID, char xy_type, double ** vector, int * N, int checkdim)
1059 {
1060     int i = 0;
1061     double* val = NULL; /* represents the x or y ticks coordinates */
1062     int nval = 0;
1063
1064     int n = 0;
1065     int nx = 0;
1066     int* piNx = &nx;
1067     int ny = 0;
1068     int* piNy = &ny;
1069     BOOL ishoriz = FALSE;
1070
1071     getGraphicObjectProperty(iObjUID, __GO_X_NUMBER_TICKS__, jni_int, (void **)&piNx);
1072     getGraphicObjectProperty(iObjUID, __GO_Y_NUMBER_TICKS__, jni_int, (void **)&piNy);
1073
1074     /* draw an horizontal axis : YES (horizontal axis) or NO (vertical axis) */
1075     ishoriz = (nx > ny) ? TRUE : FALSE;
1076
1077     if (ishoriz == TRUE)
1078     {
1079         getGraphicObjectProperty(iObjUID, __GO_X_TICKS_COORDS__, jni_double_vector, (void **)&val);
1080         nval = nx;
1081     }
1082     else
1083     {
1084         getGraphicObjectProperty(iObjUID, __GO_Y_TICKS_COORDS__, jni_double_vector, (void **)&val);
1085         nval = ny;
1086     }
1087
1088     if (xy_type == 'v')
1089     {
1090         *N = n = nval;
1091
1092         if ((*vector = (double *) MALLOC(n * sizeof(double))) == NULL)
1093         {
1094             Scierror(999, _("%s: No more memory.\n"), "ComputeXIntervals");
1095             return -1;
1096         }
1097
1098         for (i = 0; i < n; i++)
1099         {
1100             (*vector)[i] = val[i];
1101         }
1102     }
1103     else if (xy_type == 'r')
1104     {
1105         double step = 0;
1106
1107         *N = n = (int)val[2] + 1; /* intervals number is given by  ppaxes->x or ppaxes->y */
1108
1109         if (checkdim)
1110         {
1111             if (nval != 3)
1112             {
1113                 sciprint(_("Warning: %s must be changed, %s is '%s' and %s dimension is not %d.\n"), "tics_coord", "xy_type", "r", "tics_coord", 3);
1114             }
1115
1116             if (nval < 3)
1117             {
1118                 Scierror(999, _("%s must be changed FIRST, %s is '%s' and %s dimension < %d.\n"), "tics_coord", "xy_type", "r", "tics_coord", 3);
1119                 *vector = (double *) NULL;
1120                 return -1;
1121             }
1122         }
1123
1124         if ((*vector = (double *) MALLOC(n * sizeof(double))) == NULL)
1125         {
1126             Scierror(999, _("%s: No more memory.\n"), "ComputeXIntervals");
1127             return -1;
1128         }
1129
1130         step = (val[1] - val[0]) / (n - 1);
1131
1132         for (i = 0; i < n - 1; i++)
1133         {
1134             (*vector)[i] = val[0] + i * step;
1135         }
1136
1137         (*vector)[n - 1] = val[1]; /* xmax */
1138
1139     }
1140     else if (xy_type == 'i')
1141     {
1142         double step = 0;
1143
1144         *N = n = (int)val[3] + 1;
1145
1146         if (checkdim)
1147         {
1148             if (nval != 4)
1149             {
1150                 sciprint(_("Warning: %s must be changed, %s is '%s' and %s dimension is not %d.\n"), "tics_coord", "xy_type", "i", "tics_coord", 4);
1151             }
1152
1153             if (nval < 4)
1154             {
1155                 Scierror(999, _("%s must be changed FIRST, %s is '%s' and %s dimension < %d.\n"), "tics_coord", "xy_type", "i", "tics_coord", 4);
1156                 *vector = (double *) NULL;
1157                 return -1;
1158             }
1159         }
1160
1161         if ((*vector = (double *)  MALLOC(n * sizeof(double))) == NULL)
1162         {
1163             Scierror(999, _("%s: No more memory.\n"), "ComputeXIntervals");
1164             return -1;
1165         }
1166
1167         step = (val[1] * exp10(val[2]) - val[0] * exp10(val[2])) / val[3];
1168
1169
1170         for (i = 0; i < n - 1; i++)
1171         {
1172             (*vector)[i] = val[0] * exp10(val[2]) + i * step;
1173         }
1174
1175         (*vector)[n - 1] = val[1] * exp10(val[2]); /* xmax */
1176
1177     }
1178
1179     return 0;
1180 }
1181 /*--------------------------------------------------------------------------*/
1182 /**
1183  * Compute the default labels of an axis from the positions of the ticks.
1184  * @param[in/out] pobjUID the axis object UID
1185  * @return a string matrix containing the labels.
1186  *         Actually it is a row vector.
1187  */
1188 StringMatrix * computeDefaultTicsLabels(int iObjUID)
1189 {
1190     StringMatrix * ticsLabels = NULL  ;
1191     int            nbTics     = 0     ;
1192     char           tempFormat[5]      ;
1193     char         * c_format   = NULL  ;
1194     double       * vector     = NULL   ; /* position of labels */
1195     char           curLabelBuffer[257];
1196     int            i = 0;
1197
1198     int tmp = 0;
1199     int* piTmp = &tmp;
1200     char ticksStyle = 'v';
1201
1202     getGraphicObjectProperty(iObjUID, __GO_FORMATN__, jni_string, (void **)&c_format);
1203
1204     /*
1205      * If different from the empty string, the format is already specified,
1206      * if equal, it needs to be computed.
1207      */
1208     if (strcmp(c_format, "") == 0)
1209     {
1210         ComputeC_format(iObjUID, tempFormat);
1211         c_format = tempFormat;
1212     }
1213
1214     getGraphicObjectProperty(iObjUID, __GO_TICKS_STYLE__, jni_int, (void **)&piTmp);
1215
1216     if (tmp == 0)
1217     {
1218         ticksStyle = 'v';
1219     }
1220     else if (tmp == 1)
1221     {
1222         ticksStyle = 'r';
1223     }
1224     else if (tmp == 2)
1225     {
1226         ticksStyle = 'i';
1227     }
1228
1229     /* vector is allocated here */
1230     if (ComputeXIntervals(iObjUID, ticksStyle, &vector, &nbTics, 1) != 0)
1231     {
1232         Scierror(999, _("Bad size in %s: you must first increase the size of the %s.\n"), "tics_coord", "tics_coord");
1233         return 0;
1234     }
1235
1236     /* create a vector of strings */
1237     ticsLabels = newMatrix(1, nbTics);
1238
1239     if (curLabelBuffer == NULL)
1240     {
1241         Scierror(999, _("%s: No more memory.\n"), "computeDefaultTicsLabels");
1242         return NULL;
1243     }
1244
1245     for (i = 0 ; i < nbTics ; i++)
1246     {
1247         sprintf(curLabelBuffer, c_format, vector[i]) ; /* we can't know for sure the size of the label */
1248         /* That's why it is first stored in a big array */
1249         copyStrMatElement(ticsLabels, 0, i, curLabelBuffer);
1250     }
1251
1252     FREE(vector);
1253     vector = NULL;
1254
1255     return ticsLabels;
1256
1257 }
1258 /*--------------------------------------------------------------------------*/
1259 /**
1260  * Create a new string which is the result the conversion of a double value
1261  * using a certain format
1262  * @param bufferSize size of the buffer used to store the store before the copying
1263  *                   it to the result. It must greater than the length of the returning string.
1264  *                   and ideally the same length.
1265  * @return the newly created strings, or NULL if an error occurred.
1266  */
1267 static char * copyFormatedValue(double value, const char format[5], int bufferSize)
1268 {
1269     char * buffer = (char*)MALLOC(bufferSize * sizeof(char));
1270     char * res = NULL;
1271     int resLength = 0;
1272
1273     if (buffer == NULL)
1274     {
1275         return NULL;
1276     }
1277
1278     sprintf(buffer , format, value);
1279
1280     resLength =  (int)strlen(buffer) + 1 ; /* + 1 <=> 0 terminating char */
1281
1282     res = (char*)MALLOC(resLength * sizeof(char));
1283
1284     if (res == NULL)
1285     {
1286         FREE(buffer);
1287         return NULL;
1288     }
1289
1290     strncpy(res, buffer, resLength);
1291
1292     FREE(buffer);
1293
1294     return res;
1295 }
1296 /*--------------------------------------------------------------------------*/
1297 char ** copyFormatedArray(const double values[], int nbStrings, const char format[5], int bufferSize)
1298 {
1299     int i = 0;
1300     char ** res = MALLOC(nbStrings * sizeof(char *));
1301
1302     if (res == NULL)
1303     {
1304         return NULL;
1305     }
1306
1307     for (i = 0 ; i < nbStrings ; i++)
1308     {
1309         res[i] = copyFormatedValue(values[i], format, bufferSize);
1310     }
1311
1312     return res;
1313
1314 }
1315 /*--------------------------------------------------------------------------*/
1316 /**************************************************
1317 * Global values which are set at this level and
1318 * not redirected to each driver
1319 **************************************************/
1320
1321 static char FPF[32] = {'\0'};
1322
1323 char * getFPF(void)
1324 {
1325     return (FPF);
1326 }
1327 /*--------------------------------------------------------------------------*/