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