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