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