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