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