3b99e2e1aa272dc0b8bd3df555f36c3b70fe0b3f
[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     }
343 }
344
345 static sco_data *reallocScoData(scicos_block * block, int numberOfPoints)
346 {
347     sco_data *sco = (sco_data *) * (block->work);
348     int i, j;
349
350     double *ptr;
351     int setLen;
352     int previousNumberOfPoints = sco->internal.maxNumberOfPoints;
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 = numberOfPoints - previousNumberOfPoints - 1; setLen >= 0; setLen--)
363                 ptr[previousNumberOfPoints + setLen] = ptr[previousNumberOfPoints - 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 = numberOfPoints - previousNumberOfPoints - 1; setLen >= 0; setLen--)
373         ptr[previousNumberOfPoints + setLen] = ptr[previousNumberOfPoints - 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
390     sco_data *sco = (sco_data *) * (block->work);
391     int maxNumberOfPoints = sco->internal.maxNumberOfPoints;
392     int numberOfPoints = sco->internal.numberOfPoints;
393
394     /*
395      * Handle the case where the t is greater than the data_bounds
396      */
397     if (t > ((sco->scope.periodCounter + 1) * block->rpar[3]))
398     {
399         sco->scope.periodCounter++;
400
401         numberOfPoints = 0;
402         sco->internal.numberOfPoints = 0;
403         if (setPolylinesBounds(block, input, sco->scope.periodCounter) == FALSE)
404         {
405             set_block_error(-5);
406             freeScoData(block);
407             sco = NULL;
408         }
409     }
410
411     /*
412      * Handle the case where the scope has more points than maxNumberOfPoints
413      */
414     if (sco != NULL && numberOfPoints >= maxNumberOfPoints)
415     {
416         // on a full scope, re-alloc
417         maxNumberOfPoints = maxNumberOfPoints + block->ipar[2];
418         sco = reallocScoData(block, maxNumberOfPoints);
419
420         // reconfigure related graphic objects
421         if (setPolylinesBuffers(block, input, maxNumberOfPoints) == FALSE)
422         {
423             set_block_error(-5);
424             freeScoData(block);
425             sco = NULL;
426         }
427     }
428
429     /*
430      * Update data
431      */
432     if (sco != NULL)
433     {
434         int setLen;
435
436         for (i = 0; i < block->insz[input]; i++)
437         {
438             for (setLen = maxNumberOfPoints - numberOfPoints - 1; setLen >= 0; setLen--)
439                 sco->internal.data[input][i][numberOfPoints + setLen] = data[i];
440         }
441
442         for (setLen = maxNumberOfPoints - numberOfPoints - 1; setLen >= 0; setLen--)
443             sco->internal.time[numberOfPoints + setLen] = t;
444
445         sco->internal.numberOfPoints++;
446     }
447 }
448
449 static BOOL pushData(scicos_block * block, int input, int row)
450 {
451     char const* pFigureUID;
452     char *pAxeUID;
453     char *pPolylineUID;
454
455     double *data;
456     sco_data *sco;
457
458     BOOL result = TRUE;
459
460     pFigureUID = getFigure(block);
461     pAxeUID = getAxe(pFigureUID, block, input);
462     pPolylineUID = getPolyline(pAxeUID, block, row);
463
464     sco = getScoData(block);
465     if (sco == NULL)
466         return FALSE;
467
468     // select the right input and row
469     data = sco->internal.data[input][row];
470
471     result &= setGraphicObjectProperty(pPolylineUID, __GO_DATA_MODEL_X__, sco->internal.time, jni_double_vector, sco->internal.maxNumberOfPoints);
472     result &= setGraphicObjectProperty(pPolylineUID, __GO_DATA_MODEL_Y__, data, jni_double_vector, sco->internal.maxNumberOfPoints);
473
474     return result;
475 }
476
477 /*****************************************************************************
478  *
479  * Graphic utils
480  *
481  ****************************************************************************/
482
483 /**
484  * Set properties on the figure.
485  *
486  * \param pFigureUID the figure uid
487  * \param block the current block
488  */
489 static void setFigureSettings(char const* pFigureUID, scicos_block * block)
490 {
491     int nipar = GetNipar(block);
492     int *ipar = GetIparPtrs(block);
493
494     int win_pos[2];
495     int win_dim[2];
496
497     win_pos[0] = ipar[(nipar - 1) - 3];
498     win_pos[1] = ipar[(nipar - 1) - 2];
499     win_dim[0] = ipar[(nipar - 1) - 1];
500     win_dim[1] = ipar[nipar - 1];
501
502     if (win_pos[0] > 0 && win_pos[1] > 0)
503     {
504         setGraphicObjectProperty(pFigureUID, __GO_POSITION__, &win_pos, jni_int_vector, 2);
505     }
506
507     if (win_dim[0] > 0 && win_dim[1] > 0)
508     {
509         setGraphicObjectProperty(pFigureUID, __GO_SIZE__, &win_dim, jni_int_vector, 2);
510     }
511 };
512
513 /*****************************************************************************
514  *
515  * Graphic
516  *
517  ****************************************************************************/
518
519 static char const* getFigure(scicos_block * block)
520 {
521     signed int figNum;
522     char const* pFigureUID = NULL;
523     char *pAxe = NULL;
524     int i__1 = 1;
525     sco_data *sco = (sco_data *) * (block->work);
526
527     int i;
528
529     // fast path for an existing object
530     if (sco->scope.cachedFigureUID != NULL)
531     {
532         return sco->scope.cachedFigureUID;
533     }
534
535     figNum = block->ipar[0];
536
537     // with a negative id, use the block number indexed from a constant.
538     if (figNum < 0)
539     {
540         figNum = 20000 + get_block_number();
541     }
542
543     pFigureUID = getFigureFromIndex(figNum);
544     // create on demand
545     if (pFigureUID == NULL)
546     {
547         pFigureUID = createNewFigureWithAxes();
548         setGraphicObjectProperty(pFigureUID, __GO_ID__, &figNum, jni_int, 1);
549
550         // the stored uid is a reference to the figure map, not to the current figure
551         pFigureUID = getFigureFromIndex(figNum);
552         sco->scope.cachedFigureUID = pFigureUID;
553
554         // set configured parameters
555         setFigureSettings(pFigureUID, block);
556
557         // allocate the axes through the getter
558         for (i = 0; i < GetNin(block); i++)
559         {
560             pAxe = getAxe(pFigureUID, block, i);
561
562             /*
563              * Setup according to block settings
564              */
565             setLabel(pAxe, __GO_X_AXIS_LABEL__, "t");
566             setLabel(pAxe, __GO_Y_AXIS_LABEL__, "y");
567
568             setGraphicObjectProperty(pAxe, __GO_X_AXIS_VISIBLE__, &i__1, jni_bool, 1);
569             setGraphicObjectProperty(pAxe, __GO_Y_AXIS_VISIBLE__, &i__1, jni_bool, 1);
570
571             setPolylinesBounds(block, i, 0);
572         }
573     }
574
575     if (sco->scope.cachedFigureUID == NULL)
576     {
577         sco->scope.cachedFigureUID = pFigureUID;
578     }
579     return pFigureUID;
580 }
581
582 static char *getAxe(char const* pFigureUID, scicos_block * block, int input)
583 {
584     char *pAxe;
585     int i;
586     sco_data *sco = (sco_data *) * (block->work);
587
588     // fast path for an existing object
589     if (sco->scope.cachedAxeUID != NULL)
590     {
591         return sco->scope.cachedAxeUID;
592     }
593
594     pAxe = findChildWithKindAt(pFigureUID, __GO_AXES__, input);
595
596     /*
597      * Allocate if necessary
598      */
599     if (pAxe == NULL)
600     {
601         cloneAxesModel(pFigureUID);
602         pAxe = findChildWithKindAt(pFigureUID, __GO_AXES__, input);
603     }
604
605     /*
606      * Setup on first access
607      */
608     if (pAxe != NULL)
609     {
610         // allocate the polylines through the getter
611         for (i = 0; i < block->insz[input]; i++)
612         {
613             getPolyline(pAxe, block, i);
614         }
615     }
616
617     /*
618      * then cache with local storage
619      */
620     if (pAxe != NULL && sco->scope.cachedAxeUID == NULL)
621     {
622         sco->scope.cachedAxeUID = strdup(pAxe);
623         releaseGraphicObjectProperty(__GO_PARENT__, pAxe, jni_string, 1);
624     }
625     return sco->scope.cachedAxeUID;
626 }
627
628 static char *getPolyline(char *pAxeUID, scicos_block * block, int row)
629 {
630     char *pPolyline;
631     double d__0 = 0.0;
632     BOOL b__true = TRUE;
633
634     int color;
635
636     sco_data *sco = (sco_data *) * (block->work);
637
638     // fast path for an existing object
639     if (sco->scope.cachedPolylinesUIDs != NULL && sco->scope.cachedPolylinesUIDs[row] != NULL)
640     {
641         return sco->scope.cachedPolylinesUIDs[row];
642     }
643
644     pPolyline = findChildWithKindAt(pAxeUID, __GO_POLYLINE__, row);
645
646     /*
647      * Allocate if necessary
648      */
649     if (pPolyline == NULL)
650     {
651         pPolyline = createGraphicObject(__GO_POLYLINE__);
652
653         if (pPolyline != NULL)
654         {
655             createDataObject(pPolyline, __GO_POLYLINE__);
656             setGraphicObjectRelationship(pAxeUID, pPolyline);
657         }
658     }
659
660     /*
661      * Setup on first access
662      */
663     if (pPolyline != NULL)
664     {
665
666         /*
667          * Default setup (will crash if removed)
668          */
669         {
670             int polylineSize[2] = { 1, block->ipar[2] };
671             setGraphicObjectProperty(pPolyline, __GO_DATA_MODEL_NUM_ELEMENTS_ARRAY__, polylineSize, jni_int_vector, 2);
672         }
673
674         setGraphicObjectProperty(pPolyline, __GO_DATA_MODEL_X__, &d__0, jni_double_vector, 1);
675         setGraphicObjectProperty(pPolyline, __GO_DATA_MODEL_Y__, &d__0, jni_double_vector, 1);
676
677         color = block->ipar[3 + row];
678         if (color > 0)
679         {
680             setGraphicObjectProperty(pPolyline, __GO_LINE_MODE__, &b__true, jni_bool, 1);
681             setGraphicObjectProperty(pPolyline, __GO_LINE_COLOR__, &color, jni_int, 1);
682         }
683         else
684         {
685             color = -color;
686             setGraphicObjectProperty(pPolyline, __GO_MARK_MODE__, &b__true, jni_bool, 1);
687             setGraphicObjectProperty(pPolyline, __GO_MARK_STYLE__, &color, jni_int, 1);
688         }
689
690         {
691             int iClipState = 1; //on
692             setGraphicObjectProperty(pPolyline, __GO_CLIP_STATE__, &iClipState, jni_int, 1);
693         }
694     }
695
696     /*
697      * then cache with local storage
698      */
699     if (pPolyline != NULL && sco->scope.cachedPolylinesUIDs != NULL && sco->scope.cachedPolylinesUIDs[row] == NULL)
700     {
701         sco->scope.cachedPolylinesUIDs[row] = strdup(pPolyline);
702         releaseGraphicObjectProperty(__GO_PARENT__, pPolyline, jni_string, 1);
703     }
704     return sco->scope.cachedPolylinesUIDs[row];
705 }
706
707 static BOOL setPolylinesBuffers(scicos_block * block, int input, int maxNumberOfPoints)
708 {
709     int i;
710
711     char const* 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, 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 const* pFigureUID;
733     char *pAxeUID;
734
735     double dataBounds[6];
736     double period = block->rpar[3];
737
738     dataBounds[0] = periodCounter * period; // xMin
739     dataBounds[1] = (periodCounter + 1) * period;   // xMax
740     dataBounds[2] = block->rpar[1]; // yMin
741     dataBounds[3] = block->rpar[2]; // yMax
742     dataBounds[4] = -1.0;       // zMin
743     dataBounds[5] = 1.0;        // zMax
744
745     pFigureUID = getFigure(block);
746     pAxeUID = getAxe(pFigureUID, block, input);
747     return setGraphicObjectProperty(pAxeUID, __GO_DATA_BOUNDS__, dataBounds, jni_double_vector, 6);
748 }