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