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