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