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