Scicos blocks: use the new MVC graphic for scopes
[scilab.git] / scilab / modules / scicos_blocks / src / c / cevscpe.c
1 /*
2  *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  *  Copyright (C) 2011 - Scilab Enterprises - ClĂ©ment DAVID
4  *
5  *  This file must be used under the terms of the CeCILL.
6  *  This source file is licensed as described in the file COPYING, which
7  *  you should have received as part of this distribution.  The terms
8  *  are also available at
9  *  http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
10  *
11  */
12
13 #include "dynlib_scicos_blocks.h"
14 #include "scoUtils.h"
15
16 #include "MALLOC.h"
17 #include "elementary_functions.h"
18 #include "string.h"
19
20 #include "getGraphicObjectProperty.h"
21 #include "setGraphicObjectProperty.h"
22 #include "graphicObjectProperties.h"
23 #include "createGraphicObject.h"
24
25 #include "CurrentFigure.h"
26
27 #include "scicos_block4.h"
28 #include "scicos.h"
29
30 #include "localization.h"
31
32 #include "FigureList.h"
33 #include "BuildObjects.h"
34 #include "AxesModel.h"
35
36 /*****************************************************************************
37  * Internal container structure
38  ****************************************************************************/
39
40 #define DEFAULT_MAX_NUMBER_OF_POINTS 8
41
42 /**
43  * Container structure
44  */
45 typedef struct
46 {
47     struct
48     {
49         int *numberOfPoints;
50         int *maxNumberOfPoints;
51         double **data;
52     } internal;
53
54     struct
55     {
56         int periodCounter;
57
58         char *cachedFigureUID;
59         char *cachedAxeUID;
60         char **cachedSegsUIDs;
61     } scope;
62 } sco_data;
63
64 /**
65  * Get (and allocate on demand) the internal data used on this scope
66  * \param block the block
67  * \return the scope data
68  */
69 static sco_data *getScoData(scicos_block * block);
70
71 /**
72  * Release any internal data
73  *
74  * \param block the block
75  */
76 static void freeScoData(scicos_block * block);
77
78 /**
79  * Append the data to the current data
80  *
81  * \param block the block
82  * \param input the input (0-indexed)
83  * \param t the current time
84  */
85 static void appendData(scicos_block * block, int input, double t);
86
87 /**
88  * Push the block data to the segs
89  *
90  * \param block the block
91  * \param input the selected input
92  *
93  */
94 static BOOL pushData(scicos_block * block, int input);
95
96 /*****************************************************************************
97  * Graphics utils
98  ****************************************************************************/
99
100 /**
101  * Get (and allocate on demand) the figure associated with the block
102  * \param block the block
103  * \return a valid figure UID or NULL on error
104  */
105 static char *getFigure(scicos_block * block);
106
107 /**
108  * Get (and allocate on demand) the axe associated with the input
109  *
110  * \param pFigureUID the parent figure UID
111  * \param block the block
112  * \return a valid axe UID or NULL on error
113  */
114 static char *getAxe(char *pFigureUID, scicos_block * block);
115
116 /**
117  * Get (and allocate on demand) the segs associated with the input
118  *
119  * \param pAxeUID the parent axe UID
120  * \param block the block
121  * \param input the current row index (0-indexed)
122  * \return a valid polyline UID or NULL on error
123  */
124 static char *getSegs(char *pAxeUID, scicos_block * block, int input);
125
126 /**
127  * Set the segs buffer size
128  *
129  * \param block the block
130  * \param maxNumberOfPoints the size of the buffer
131  */
132 static BOOL setSegsBuffers(scicos_block * block, int maxNumberOfPoints);
133
134 /**
135  * Set the axes bounds
136  *
137  * \param block the block
138  * \param periodCounter number of past periods since startup
139  */
140 static BOOL setBounds(scicos_block * block, int periodCounter);
141
142 /*****************************************************************************
143  * Simulation function
144  ****************************************************************************/
145
146 /** \fn void cscope(scicos_block * block,int flag)
147     \brief the computational function
148     \param block A pointer to a scicos_block
149     \param flag An int which indicates the state of the block (init, update, ending)
150 */
151 SCICOS_BLOCKS_IMPEXP void cevscpe(scicos_block * block, scicos_flag flag)
152 {
153     char *pFigureUID;
154
155     double t;
156     int i;
157     int mask;
158     int nclk = block->nipar - 6;
159     sco_data *sco;
160
161     BOOL result;
162
163     switch (flag)
164     {
165
166     case Initialization:
167         sco = getScoData(block);
168         if (sco == NULL)
169         {
170             set_block_error(-5);
171         }
172         pFigureUID = getFigure(block);
173         if (pFigureUID == NULL)
174         {
175             // allocation error
176             set_block_error(-5);
177             break;
178         }
179
180         setSegsBuffers(block, DEFAULT_MAX_NUMBER_OF_POINTS);
181         break;
182
183     case StateUpdate:
184         pFigureUID = getFigure(block);
185
186         t = get_scicos_time();
187
188         // select only the masked indexes
189         for (i = 0; i < nclk; i++)
190         {
191             mask = 1 << i;
192             if ((block->nevprt & mask) == mask)
193             {
194                 appendData(block, i, t);
195
196                 result = pushData(block, i);
197                 if (result == FALSE)
198                 {
199                     Coserror("%s: unable to push some data.", "cevscpe");
200                     break;
201                 }
202             }
203         }
204         break;
205
206     case Ending:
207         freeScoData(block);
208         break;
209
210     default:
211         break;
212     }
213 }
214
215 /*-------------------------------------------------------------------------*/
216
217 /*****************************************************************************
218  *
219  * Container management
220  *
221  ****************************************************************************/
222
223 static sco_data *getScoData(scicos_block * block)
224 {
225     sco_data *sco = (sco_data *) * (block->work);
226     int i, j;
227
228     int nclk = block->nipar - 6;
229
230     if (sco == NULL)
231     {
232         /*
233          * Data allocation
234          */
235
236         sco = (sco_data *) MALLOC(sizeof(sco_data));
237         if (sco == NULL)
238             goto error_handler_sco;
239
240         sco->internal.numberOfPoints = (int *)CALLOC(nclk, sizeof(int));
241         if (sco->internal.numberOfPoints == NULL)
242         {
243             goto error_handler_numberOfPoints;
244         }
245         sco->internal.maxNumberOfPoints = (int *)MALLOC(nclk * sizeof(int));
246         if (sco->internal.numberOfPoints == NULL)
247         {
248             goto error_handler_maxNumberOfPoints;
249         }
250         for (i = 0; i < nclk; i++)
251         {
252             sco->internal.maxNumberOfPoints[i] = DEFAULT_MAX_NUMBER_OF_POINTS;
253         }
254
255         sco->internal.data = (double **)CALLOC(2 * nclk, sizeof(double *));
256         if (sco->internal.data == NULL)
257             goto error_handler_data;
258
259         for (i = 0; i < nclk; i++)
260         {
261             /*
262              * Alloc base pointer
263              */
264             sco->internal.data[2 * i + 0] = (double *)CALLOC(3 * DEFAULT_MAX_NUMBER_OF_POINTS, sizeof(double));
265             if (sco->internal.data[2 * i + 0] == NULL)
266                 goto error_handler_data_i;
267
268             /*
269              * Alloc direction pointer
270              */
271             sco->internal.data[2 * i + 1] = (double *)CALLOC(3 * DEFAULT_MAX_NUMBER_OF_POINTS, sizeof(double));
272             if (sco->internal.data[2 * i + 1] == NULL)
273             {
274                 FREE(sco->internal.data[2 * i + 0]);
275                 goto error_handler_data_i;
276             }
277         }
278
279         sco->scope.periodCounter = 0;
280         sco->scope.cachedFigureUID = NULL;
281         sco->scope.cachedAxeUID = NULL;
282         sco->scope.cachedSegsUIDs = (char **)CALLOC(nclk, sizeof(char *));
283         if (sco->scope.cachedSegsUIDs == NULL)
284         {
285             goto error_handler_data_i;
286         }
287
288         *(block->work) = sco;
289     }
290
291     return sco;
292
293     /*
294      * Error management (out of normal flow)
295      */
296
297 error_handler_data_i:
298     for (j = 0; j < i; j++)
299     {
300         FREE(sco->internal.data[2 * j + 0]);
301         FREE(sco->internal.data[2 * j + 1]);
302     }
303     FREE(sco->internal.data);
304 error_handler_data:
305     FREE(sco->internal.maxNumberOfPoints);
306 error_handler_maxNumberOfPoints:
307     FREE(sco->internal.numberOfPoints);
308 error_handler_numberOfPoints:
309     FREE(sco);
310 error_handler_sco:
311     // allocation error
312     set_block_error(-5);
313     return NULL;
314 }
315
316 static void freeScoData(scicos_block * block)
317 {
318     sco_data *sco = getScoData(block);
319     int i;
320
321     int nclk = block->nipar - 6;
322
323     if (sco != NULL)
324     {
325         for (i = 0; i < nclk; i++)
326         {
327             FREE(sco->internal.data[i]);
328         }
329         FREE(sco->internal.data);
330         FREE(sco->internal.maxNumberOfPoints);
331         FREE(sco->internal.numberOfPoints);
332
333 //      Commented due to the C++ allocation
334 //      see http://bugzilla.scilab.org/show_bug.cgi?id=9747
335 //      FREE(sco->scope.cachedFigureUID);
336 //      FREE(sco->scope.cachedAxeUID);
337 //      sco->scope.cachedFigureUID = NULL;
338 //      for (i=0; i<nclk; i++) {
339 //              FREE(sco->scope.cachedPolylinesUIDs[i]);
340 //              sco->scope.cachedPolylinesUIDs[i] = NULL;
341 //      }
342 //      sco->scope.cachedAxeUID = NULL;
343
344         FREE(sco->scope.cachedSegsUIDs);
345
346         FREE(sco);
347     }
348 }
349
350 static sco_data *reallocScoData(scicos_block * block, int input, int numberOfPoints)
351 {
352     sco_data *sco = getScoData(block);
353     double *ptr;
354     int maxNumberOfPoints = sco->internal.maxNumberOfPoints[input];
355     int setLen;
356
357     /*
358      * Realloc base pointer
359      */
360     ptr = (double *)REALLOC(sco->internal.data[2 * input], 3 * numberOfPoints * sizeof(double));
361     if (ptr == NULL)
362         goto error_handler;
363
364     for (setLen = numberOfPoints - maxNumberOfPoints; setLen >= 0; setLen--)
365     {
366         ptr[3 * (maxNumberOfPoints + setLen) + 0] = ptr[3 * (maxNumberOfPoints - 1) + 0];
367         ptr[3 * (maxNumberOfPoints + setLen) + 1] = ptr[3 * (maxNumberOfPoints - 1) + 1];
368         ptr[3 * (maxNumberOfPoints + setLen) + 2] = ptr[3 * (maxNumberOfPoints - 1) + 2];
369     }
370     sco->internal.data[2 * input] = ptr;
371
372     /*
373      * Realloc direction pointer
374      */
375     ptr = (double *)REALLOC(sco->internal.data[2 * input + 1], 3 * numberOfPoints * sizeof(double));
376     if (ptr == NULL)
377         goto error_handler;
378
379     for (setLen = numberOfPoints - maxNumberOfPoints; setLen >= 0; setLen--)
380     {
381         ptr[3 * (maxNumberOfPoints + setLen) + 0] = ptr[3 * (maxNumberOfPoints - 1) + 0];
382         ptr[3 * (maxNumberOfPoints + setLen) + 1] = ptr[3 * (maxNumberOfPoints - 1) + 1];
383         ptr[3 * (maxNumberOfPoints + setLen) + 2] = ptr[3 * (maxNumberOfPoints - 1) + 2];
384     }
385     sco->internal.data[2 * input + 1] = ptr;
386
387     sco->internal.maxNumberOfPoints[input] = numberOfPoints;
388     return sco;
389
390 error_handler:
391     freeScoData(block);
392     // allocation error
393     set_block_error(-5);
394     return NULL;
395 }
396
397 static void appendData(scicos_block * block, int input, double t)
398 {
399     sco_data *sco = getScoData(block);
400     int maxNumberOfPoints = sco->internal.maxNumberOfPoints[input];
401     int numberOfPoints = sco->internal.numberOfPoints[input];
402
403     /*
404      * Handle the case where the t is greater than the data_bounds
405      */
406     if (t > ((sco->scope.periodCounter + 1) * block->rpar[0]))
407     {
408         sco->scope.periodCounter++;
409
410         numberOfPoints = 0;
411         sco->internal.numberOfPoints[input] = 0;
412         if (setBounds(block, sco->scope.periodCounter) == FALSE)
413         {
414             set_block_error(-5);
415             freeScoData(block);
416             sco = NULL;
417         }
418     }
419
420     /*
421      * Handle the case where the scope has more points than maxNumberOfPoints
422      */
423     if (sco != NULL && numberOfPoints >= maxNumberOfPoints)
424     {
425         // on a full scope, re-alloc
426         maxNumberOfPoints = maxNumberOfPoints + DEFAULT_MAX_NUMBER_OF_POINTS;
427         sco = reallocScoData(block, input, maxNumberOfPoints);
428
429         // reconfigure related graphic objects
430         if (setSegsBuffers(block, maxNumberOfPoints) == FALSE)
431         {
432             set_block_error(-5);
433             freeScoData(block);
434             sco = NULL;
435         }
436     }
437
438     /*
439      * Update data
440      */
441     if (sco != NULL)
442     {
443         int setLen;
444
445         /*
446          * Base pointer
447          */
448         for (setLen = maxNumberOfPoints - numberOfPoints; setLen >= 0; setLen--)
449         {
450             sco->internal.data[2 * input][3 * (numberOfPoints + setLen) + 0] = t;   // x
451             sco->internal.data[2 * input][3 * (numberOfPoints + setLen) + 1] = 0;   // y
452             sco->internal.data[2 * input][3 * (numberOfPoints + setLen) + 2] = (double)input;   // z
453         }
454
455         /*
456          * Direction pointer
457          */
458         for (setLen = maxNumberOfPoints - numberOfPoints; setLen >= 0; setLen--)
459         {
460             sco->internal.data[2 * input][3 * (numberOfPoints + setLen) + 0] = t;   // x
461             sco->internal.data[2 * input][3 * (numberOfPoints + setLen) + 1] = 0.8; // y
462             sco->internal.data[2 * input][3 * (numberOfPoints + setLen) + 2] = (double)input;   // z
463         }
464
465         sco->internal.numberOfPoints[input]++;
466     }
467 }
468
469 static BOOL pushData(scicos_block * block, int input)
470 {
471     char *pFigureUID;
472     char *pAxeUID;
473     char *pSegsUID;
474
475     int dataLen;
476     double *base;
477     double *direction;
478     sco_data *sco;
479
480     BOOL result = TRUE;
481
482     pFigureUID = getFigure(block);
483     pAxeUID = getAxe(pFigureUID, block);
484     pSegsUID = getSegs(pAxeUID, block, input);
485
486     sco = getScoData(block);
487     if (sco == NULL)
488         return FALSE;
489
490     // select the right input and row
491     base = sco->internal.data[2 * input];
492     direction = sco->internal.data[2 * input + 1];
493
494     dataLen = 3 * sco->internal.maxNumberOfPoints[input];
495
496     result &= setGraphicObjectProperty(pSegsUID, __GO_BASE__, base, jni_double_vector, dataLen);
497     result &= setGraphicObjectProperty(pSegsUID, __GO_DIRECTION__, direction, jni_double_vector, dataLen);
498
499     return result;
500 }
501
502 /*****************************************************************************
503  *
504  * Graphic utils
505  *
506  ****************************************************************************/
507
508 /**
509  * Set properties on the figure.
510  *
511  * \param pFigureUID the figure uid
512  * \param block the current block
513  */
514 static void setFigureSettings(char *pFigureUID, scicos_block * block)
515 {
516     int nipar = GetNipar(block);
517     int *ipar = GetIparPtrs(block);
518
519     int win_pos[2];
520     int win_dim[2];
521
522     win_pos[0] = ipar[(nipar - 1) - 3];
523     win_pos[1] = ipar[(nipar - 1) - 2];
524     win_dim[0] = ipar[(nipar - 1) - 1];
525     win_dim[1] = ipar[nipar - 1];
526
527     setGraphicObjectProperty(pFigureUID, __GO_POSITION__, &win_pos, jni_int_vector, 2);
528     setGraphicObjectProperty(pFigureUID, __GO_SIZE__, &win_dim, jni_int_vector, 2);
529 };
530
531 /*****************************************************************************
532  *
533  * Graphic
534  *
535  ****************************************************************************/
536
537 static char *getFigure(scicos_block * block)
538 {
539     signed int figNum;
540     char *pFigureUID = NULL;
541     char *pAxe = NULL;
542     static const int i__1 = 1;
543     sco_data *sco = getScoData(block);
544
545     // fast path for an existing object
546     if (sco->scope.cachedFigureUID != NULL)
547     {
548         return sco->scope.cachedFigureUID;
549     }
550
551     figNum = block->ipar[0];
552
553     // with a negative id, use the block number indexed from a constant.
554     if (figNum < 0)
555     {
556         figNum = 20000 + get_block_number();
557     }
558
559     pFigureUID = getFigureFromIndex(figNum);
560     // create on demand
561     if (pFigureUID == NULL)
562     {
563         pFigureUID = createNewFigureWithAxes();
564         setGraphicObjectProperty(pFigureUID, __GO_ID__, &figNum, jni_int, 1);
565
566         // set configured parameters
567         setFigureSettings(pFigureUID, block);
568
569         // allocate the axes through the getter
570         pAxe = getAxe(pFigureUID, block);
571
572         /*
573          * Setup according to block settings
574          */
575         setLabel(pAxe, __GO_X_AXIS_LABEL__, "t");
576         setLabel(pAxe, __GO_Y_AXIS_LABEL__, "y");
577
578         setGraphicObjectProperty(pAxe, __GO_X_AXIS_VISIBLE__, &i__1, jni_bool, 1);
579         setGraphicObjectProperty(pAxe, __GO_Y_AXIS_VISIBLE__, &i__1, jni_bool, 1);
580
581         setBounds(block, 0);
582
583         sco->scope.cachedFigureUID = pFigureUID;
584     }
585
586     if (sco->scope.cachedFigureUID == NULL)
587     {
588         sco->scope.cachedFigureUID = pFigureUID;
589     }
590     return pFigureUID;
591 }
592
593 static char *getAxe(char *pFigureUID, scicos_block * block)
594 {
595     char *pAxe;
596     int i;
597
598     int nclk = block->nipar - 6;
599     sco_data *sco = getScoData(block);
600
601     // fast path for an existing object
602     if (sco->scope.cachedAxeUID != NULL)
603     {
604         return sco->scope.cachedAxeUID;
605     }
606
607     pAxe = findChildWithKindAt(pFigureUID, __GO_AXES__, 0);
608
609     /*
610      * Allocate if necessary
611      */
612     if (pAxe == NULL)
613     {
614         pAxe = cloneGraphicObject(getAxesModel());
615
616         if (pAxe != NULL)
617         {
618             setGraphicObjectRelationship(pFigureUID, pAxe);
619
620             // allocate the segs through the getter
621             for (i = 0; i < nclk; i++)
622             {
623                 getSegs(pAxe, block, i);
624             }
625
626             sco->scope.cachedAxeUID = pAxe;
627         }
628     }
629
630     if (sco->scope.cachedAxeUID == NULL)
631     {
632         sco->scope.cachedAxeUID = pAxe;
633     }
634     return pAxe;
635 }
636
637 static char *getSegs(char *pAxeUID, scicos_block * block, int input)
638 {
639     char *pSegs;
640     static const BOOL b__true = TRUE;
641     static const double d__1 = 1.0;
642
643     int color;
644
645     sco_data *sco = getScoData(block);
646
647     // fast path for an existing object
648     if (sco->scope.cachedSegsUIDs != NULL && sco->scope.cachedSegsUIDs[input] != NULL)
649     {
650         return sco->scope.cachedSegsUIDs[input];
651     }
652
653     pSegs = findChildWithKindAt(pAxeUID, __GO_SEGS__, input);
654
655     /*
656      * Allocate if necessary
657      */
658     if (pSegs == NULL)
659     {
660         pSegs = createGraphicObject(__GO_SEGS__);
661
662         if (pSegs != NULL)
663         {
664             createDataObject(pSegs, __GO_SEGS__);
665             setGraphicObjectRelationship(pAxeUID, pSegs);
666
667             setGraphicObjectProperty(pSegs, __GO_NUMBER_ARROWS__, &sco->internal.maxNumberOfPoints[input], jni_int, 1);
668
669             // Setup properties
670             setGraphicObjectProperty(pSegs, __GO_LINE_THICKNESS__, &d__1, jni_double, 1);
671
672             color = block->ipar[2 + input];
673             if (color > 0)
674             {
675                 setGraphicObjectProperty(pSegs, __GO_LINE_MODE__, &b__true, jni_bool, 1);
676                 setGraphicObjectProperty(pSegs, __GO_SEGS_COLORS__, &color, jni_int_vector, 1);
677             }
678             else
679             {
680                 color = -color;
681                 setGraphicObjectProperty(pSegs, __GO_MARK_MODE__, &b__true, jni_bool, 1);
682                 setGraphicObjectProperty(pSegs, __GO_MARK_STYLE__, &color, jni_int, 1);
683             }
684
685             sco->scope.cachedSegsUIDs[input] = pSegs;
686         }
687     }
688
689     if (sco->scope.cachedSegsUIDs != NULL && sco->scope.cachedSegsUIDs[input] == NULL)
690     {
691         sco->scope.cachedSegsUIDs[input] = pSegs;
692     }
693     return pSegs;
694 }
695
696 static BOOL setSegsBuffers(scicos_block * block, int maxNumberOfPoints)
697 {
698     char *pFigureUID;
699     char *pAxeUID;
700     char *pSegsUID;
701
702     int i;
703     int nclk = block->nipar - 6;
704     BOOL result = TRUE;
705
706     int color;
707
708     pFigureUID = getFigure(block);
709     pAxeUID = getAxe(pFigureUID, block);
710     for (i = 0; i < nclk; i++)
711     {
712         pSegsUID = getSegs(pAxeUID, block, i);
713         result &= setGraphicObjectProperty(pSegsUID, __GO_NUMBER_ARROWS__, &maxNumberOfPoints, jni_int, 1);
714
715         /*
716          * Update color due to bug #9902
717          * http://bugzilla.scilab.org/show_bug.cgi?id=9902
718          */
719         color = block->ipar[2 + i];
720         if (color > 0)
721         {
722             setGraphicObjectProperty(pSegsUID, __GO_SEGS_COLORS__, &color, jni_int_vector, 1);
723         }
724
725     }
726
727     return result;
728 }
729
730 static BOOL setBounds(scicos_block * block, int periodCounter)
731 {
732     char *pFigureUID;
733     char *pAxeUID;
734
735     double dataBounds[6];
736     double period = block->rpar[0];
737
738     dataBounds[0] = periodCounter * period; // xMin
739     dataBounds[1] = (periodCounter + 1) * period;   // xMax
740     dataBounds[2] = 0;          // yMin
741     dataBounds[3] = 1;          // yMax
742     dataBounds[4] = -1.0;       // zMin
743     dataBounds[5] = 1.0;        // zMax
744
745     pFigureUID = getFigure(block);
746     pAxeUID = getAxe(pFigureUID, block);
747     return setGraphicObjectProperty(pAxeUID, __GO_DATA_BOUNDS__, dataBounds, jni_double_vector, 6);
748 }