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