* Bug #8784 fixed - Autoscale for cscope graph
[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-2012 - Scilab Enterprises - Clement DAVID
4  *  Copyright (C) 2016-2017 - FOSSEE IIT Bombay - Dipti Ghosalkar
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13  *
14  */
15
16 #include <string.h>
17
18 #include "dynlib_scicos_blocks.h"
19 #include "scoUtils.h"
20
21 #include "sci_malloc.h"
22 #include "elementary_functions.h"
23
24 #include "getGraphicObjectProperty.h"
25 #include "setGraphicObjectProperty.h"
26 #include "graphicObjectProperties.h"
27 #include "createGraphicObject.h"
28 #include "deleteGraphicObject.h"
29
30 #include "CurrentFigure.h"
31 #include "CurrentObject.h"
32
33 #include "scicos_block4.h"
34 #include "scicos.h"
35
36 #include "localization.h"
37 #include "os_string.h"
38
39 #include "FigureList.h"
40 #include "BuildObjects.h"
41 #include "AxesModel.h"
42
43 #define HISTORY_POINTS_THRESHOLD 4096
44
45 /*****************************************************************************
46  * Internal container structure
47  ****************************************************************************/
48
49 /**
50  * Container structure
51  */
52 typedef struct
53 {
54     struct
55     {
56         int numberOfPoints;
57         double ***bufferCoordinates;
58         int maxNumberOfPoints;
59         double ***historyCoordinates;
60     } internal;
61
62     struct
63     {
64         int periodCounter;
65
66         BOOL disableBufferUpdate;
67         int historyUpdateCounter;
68
69         int cachedFigureUID;
70         int cachedAxeUID;
71         int* cachedBufferPolylinesUIDs;
72         int* cachedHistoryPolylinesUIDs;
73     } scope;
74 } sco_data;
75
76 /**
77  * Get (and allocate on demand) the internal data used on this scope
78  * \param block the block
79  * \return the scope data
80  */
81 static sco_data *getScoData(scicos_block * block);
82
83 /**
84  * Release any internal data
85  *
86  * \param block the block
87  */
88 static void freeScoData(scicos_block * block);
89
90 /**
91  * Realloc the history buffer data
92  *
93  * \param block the block
94  * \param numberOfPoints realloc to store this number of points
95  */
96 static sco_data *reallocHistoryBuffer(scicos_block * block, int numberOfPoints);
97
98 /**
99  * Set values into the coordinates buffer.
100  *
101  * \param block the block
102  * \param coordinates the buffer
103  * \param numberOfPoints the number of points to set (actual)
104  * \param bufferPoints the buffer size (max)
105  * \param t the time to set
106  * \param value the value to set
107  */
108 static void setBuffersCoordinates(scicos_block * block, double* coordinates, const int numberOfPoints,
109                                   const int bufferPoints, const double t, const double value);
110
111 /**
112  * Append the data to the current data
113  *
114  * \param block the block
115  * \param input the input (0-indexed)
116  * \param t the current time
117  * \param data the data to append
118  */
119 static void appendData(scicos_block * block, int input, double t, double *data);
120
121 /**
122  * Push the block data to the polyline
123  *
124  * \param block the block
125  * \param input the selected input
126  * \param row the selected row
127  * \param iPolylineUID the polyline uid
128  *
129  */
130 static BOOL pushData(scicos_block * block, int input, int row);
131
132 /*****************************************************************************
133  * Graphics utils
134  ****************************************************************************/
135
136 /**
137  * Get (and allocate on demand) the figure associated with the block
138  * \param block the block
139  * \return a valid figure UID or NULL on error
140  */
141 static int getFigure(scicos_block * block);
142
143 /**
144  * Get (and allocate on demand) the axe associated with the input
145  *
146  * \param iFigureUID the parent figure UID
147  * \param block the block
148  * \param input the current input index (0-indexed)
149  * \return a valid axe UID or NULL on error
150  */
151 static int getAxe(int iFigureUID, scicos_block * block, int input);
152
153 /**
154  * Get (and allocate on demand) the polyline associated with the row
155  *
156  * \param iAxeUID the parent axe UID
157  * \param block the block
158  * \param row the current row index (0-indexed)
159  * \param history get the history polyline
160  * \return a valid polyline UID or NULL on error
161  */
162 static int getPolyline(int iAxeUID, scicos_block * block, int row, BOOL history);
163
164 /**
165  * Delete all the buffer polylines.
166  *
167  * \param block the block
168  */
169 static void deleteBufferPolylines(scicos_block * block);
170
171 /**
172  * Set the polylines history size and push the history buffer
173  *
174  * \param block the block
175  * \param input the input port index
176  * \param maxNumberOfPoints the size of the buffer
177  */
178 static BOOL pushHistory(scicos_block * block, int input, int maxNumberOfPoints);
179
180 /**
181  * Set the polylines bounds
182  *
183  * \param block the current block
184  * \param iAxeUID the axe uid
185  * \param periodCounter number of past periods since startup
186  */
187 static BOOL setPolylinesBounds(scicos_block * block, int iAxeUID, int periodCounter);
188
189 /*****************************************************************************
190  * Simulation function
191  ****************************************************************************/
192
193 /** \fn void cscope(scicos_block * block,int flag)
194     \brief the computational function
195     \param block A pointer to a scicos_block
196     \param flag An int which indicates the state of the block (init, update, ending)
197 */
198 SCICOS_BLOCKS_IMPEXP void cscope(scicos_block * block, scicos_flag flag)
199 {
200     int iFigureUID;
201
202     double t;
203     double *u;
204     sco_data *sco;
205
206     int i;
207     BOOL result;
208
209     switch (flag)
210     {
211
212         case Initialization:
213             sco = getScoData(block);
214             if (sco == NULL)
215             {
216                 set_block_error(-5);
217                 break;
218             }
219             iFigureUID = getFigure(block);
220             if (iFigureUID == 0)
221             {
222                 // allocation error
223                 set_block_error(-5);
224                 break;
225             }
226             break;
227
228         case StateUpdate:
229             iFigureUID = getFigure(block);
230             if (iFigureUID == 0)
231             {
232                 // allocation error
233                 set_block_error(-5);
234                 break;
235             }
236
237             t = get_scicos_time();
238             u = GetRealInPortPtrs(block, 1);
239
240             appendData(block, 0, t, u);
241
242             for (i = 0; i < block->insz[0]; i++)
243             {
244                 result = pushData(block, 0, i);
245                 if (result == FALSE)
246                 {
247                     Coserror("%s: unable to push some data.", "cscope");
248                     break;
249                 }
250             }
251             break;
252
253         case Ending:
254             sco = getScoData(block);
255             sco = reallocHistoryBuffer(block, sco->internal.maxNumberOfPoints + sco->internal.numberOfPoints);
256             sco->scope.disableBufferUpdate = FALSE;
257             sco->scope.historyUpdateCounter = 0;
258 #ifdef WITH_GUI
259             pushHistory(block, 0, sco->internal.maxNumberOfPoints);
260             deleteBufferPolylines(block);
261 #endif
262             freeScoData(block);
263             break;
264
265         default:
266             break;
267     }
268 }
269
270 /*-------------------------------------------------------------------------*/
271
272 /*****************************************************************************
273  *
274  * Container management
275  *
276  ****************************************************************************/
277
278 static sco_data *getScoData(scicos_block * block)
279 {
280     sco_data *sco = (sco_data *) * (block->work);
281     int i, j;
282
283     if (sco == NULL)
284     {
285         /*
286          * Data allocation
287          */
288
289         sco = (sco_data *) MALLOC(sizeof(sco_data));
290         if (sco == NULL)
291         {
292             goto error_handler_sco;
293         }
294
295         // 0 points out of a block->ipar[2] points buffer
296         sco->internal.numberOfPoints = 0;
297
298         sco->internal.bufferCoordinates = (double ***)CALLOC(block->nin, sizeof(double **));
299         if (sco->internal.bufferCoordinates == NULL)
300         {
301             goto error_handler_bufferCoordinates;
302         }
303
304         for (i = 0; i < block->nin; i++)
305         {
306             sco->internal.bufferCoordinates[i] = (double **)CALLOC(block->insz[i], sizeof(double *));
307             if (sco->internal.bufferCoordinates[i] == NULL)
308             {
309                 goto error_handler_bufferCoordinates_i;
310             }
311         }
312         for (i = 0; i < block->nin; i++)
313         {
314             for (j = 0; j < block->insz[i]; j++)
315             {
316                 sco->internal.bufferCoordinates[i][j] = (double *)CALLOC(3 * block->ipar[2], sizeof(double));
317
318                 if (sco->internal.bufferCoordinates[i][j] == NULL)
319                 {
320                     goto error_handler_bufferCoordinates_ij;
321                 }
322             }
323         }
324
325         // 0 points out of a 0 points history
326         sco->internal.maxNumberOfPoints = 0;
327
328         sco->internal.historyCoordinates = (double ***)CALLOC(block->nin, sizeof(double **));
329         if (sco->internal.historyCoordinates == NULL)
330         {
331             goto error_handler_historyCoordinates;
332         }
333
334         for (i = 0; i < block->nin; i++)
335         {
336             sco->internal.historyCoordinates[i] = (double **)CALLOC(block->insz[i], sizeof(double *));
337             if (sco->internal.historyCoordinates[i] == NULL)
338             {
339                 goto error_handler_historyCoordinates_i;
340             }
341         }
342
343         sco->scope.periodCounter = 0;
344
345         // flag to avoid pushing the buffer each time
346         sco->scope.disableBufferUpdate = FALSE;
347         // counter use to delay the history push
348         sco->scope.historyUpdateCounter = 0;
349
350         sco->scope.cachedFigureUID = 0;
351         sco->scope.cachedAxeUID = 0;
352         sco->scope.cachedBufferPolylinesUIDs = (int*)CALLOC(block->insz[0], sizeof(int));
353         sco->scope.cachedHistoryPolylinesUIDs = (int*)CALLOC(block->insz[0], sizeof(int));
354
355         *(block->work) = sco;
356     }
357
358     return sco;
359
360     /*
361      * Error management (out of normal flow)
362      */
363
364 error_handler_historyCoordinates_i:
365     for (j = 0; j < i; j++)
366     {
367         FREE(sco->internal.historyCoordinates[j]);
368     }
369     FREE(sco->internal.historyCoordinates);
370 error_handler_historyCoordinates:
371 error_handler_bufferCoordinates_ij:
372     for (i = 0; i < block->nin - 1; i++)
373     {
374         for (j = 0; j < block->insz[i] - 1; j++)
375         {
376             double* ptr = sco->internal.bufferCoordinates[i][j];
377             if (ptr != NULL)
378             {
379                 FREE(ptr);
380             }
381         }
382     }
383     i = block->nin - 1;
384 error_handler_bufferCoordinates_i:
385     for (j = 0; j < i; j++)
386     {
387         FREE(sco->internal.bufferCoordinates[j]);
388     }
389     FREE(sco->internal.bufferCoordinates);
390 error_handler_bufferCoordinates:
391     FREE(sco);
392 error_handler_sco:
393     // allocation error
394     set_block_error(-5);
395     return NULL;
396 }
397
398 static void freeScoData(scicos_block * block)
399 {
400     sco_data *sco = (sco_data *) * (block->work);
401     int i, j;
402
403     if (sco != NULL)
404     {
405         for (i = 0; i < block->nin; i++)
406         {
407             for (j = 0; j < block->insz[i]; j++)
408             {
409                 if (sco->internal.historyCoordinates[i][j] != NULL)
410                 {
411                     FREE(sco->internal.historyCoordinates[i][j]);
412                 }
413                 FREE(sco->internal.bufferCoordinates[i][j]);
414             }
415             FREE(sco->internal.historyCoordinates[i]);
416             FREE(sco->internal.bufferCoordinates[i]);
417         }
418         FREE(sco->internal.historyCoordinates);
419         FREE(sco->internal.bufferCoordinates);
420
421         FREE(sco->scope.cachedHistoryPolylinesUIDs);
422         FREE(sco->scope.cachedBufferPolylinesUIDs);
423
424         FREE(sco);
425         *(block->work) = NULL;
426     }
427 }
428
429 static sco_data *reallocHistoryBuffer(scicos_block * block, int numberOfPoints)
430 {
431     sco_data *sco = (sco_data *) * (block->work);
432     int i;
433
434     double *ptr;
435     int allocatedNumberOfPoints;
436
437     int previousNumberOfPoints = sco->internal.maxNumberOfPoints;
438     int numberOfCopiedPoints = numberOfPoints - sco->internal.maxNumberOfPoints;
439
440     double *buffer;
441     int bufferNumberOfPoints = block->ipar[2];
442     int bufferNewPointInc;
443
444     if (previousNumberOfPoints == 0)
445     {
446         allocatedNumberOfPoints = numberOfPoints;
447         bufferNewPointInc = 0;
448     }
449     else
450     {
451         allocatedNumberOfPoints = numberOfPoints - 1;
452         bufferNewPointInc = 1;
453     }
454
455     if (sco->scope.historyUpdateCounter <= 0)
456     {
457         if (numberOfPoints > HISTORY_POINTS_THRESHOLD)
458         {
459             sco->scope.disableBufferUpdate = TRUE;
460             sco->scope.historyUpdateCounter = numberOfPoints / HISTORY_POINTS_THRESHOLD;
461         }
462         else
463         {
464             sco->scope.disableBufferUpdate = FALSE;
465             sco->scope.historyUpdateCounter = 0;
466         }
467     }
468
469     for (i = 0; i < block->insz[0]; i++)
470     {
471         ptr = (double *)MALLOC(3 * allocatedNumberOfPoints * sizeof(double));
472         if (ptr == NULL)
473         {
474             goto error_handler;
475         }
476
477         // memcpy existing X-axis values from the history
478         memcpy(ptr, sco->internal.historyCoordinates[0][i], previousNumberOfPoints * sizeof(double));
479         // memcpy existing Y-axis values from the history
480         memcpy(ptr + allocatedNumberOfPoints, sco->internal.historyCoordinates[0][i] + previousNumberOfPoints, previousNumberOfPoints * sizeof(double));
481         // clear the last points, the Z-axis values
482         memset(ptr + 2 * allocatedNumberOfPoints, 0, allocatedNumberOfPoints * sizeof(double));
483
484         // then set the last points to the last values for X-axis and Y-axis values from the buffer points
485         buffer = sco->internal.bufferCoordinates[0][i];
486         memcpy(ptr + previousNumberOfPoints, buffer + bufferNewPointInc, (numberOfCopiedPoints - bufferNewPointInc) * sizeof(double));
487         memcpy(ptr + allocatedNumberOfPoints + previousNumberOfPoints, buffer + bufferNumberOfPoints + bufferNewPointInc, (numberOfCopiedPoints - bufferNewPointInc) * sizeof(double));
488
489         FREE(sco->internal.historyCoordinates[0][i]);
490         sco->internal.historyCoordinates[0][i] = ptr;
491     }
492
493     sco->internal.maxNumberOfPoints = allocatedNumberOfPoints;
494     return sco;
495
496 error_handler:
497     freeScoData(block);
498     // allocation error
499     set_block_error(-5);
500     return NULL;
501 }
502
503 static void setBuffersCoordinates(scicos_block* block, double* coordinates, const int numberOfPoints,
504                                   const int bufferPoints, const double t, const double value)
505 {
506     int setLen;
507     sco_data *sco = (sco_data *) * (block->work);
508
509     if (sco->scope.disableBufferUpdate == TRUE)
510     {
511         coordinates[numberOfPoints] = t;
512         coordinates[bufferPoints + numberOfPoints] = value;
513         return;
514     }
515
516     // X-axis values first
517     for (setLen = numberOfPoints; setLen < bufferPoints; setLen++)
518     {
519         coordinates[setLen] = t;
520     }
521     // then Y-axis values
522     for (setLen = numberOfPoints; setLen < bufferPoints; setLen++)
523     {
524         coordinates[bufferPoints + setLen] = value;
525     }
526     // then Z-axis values (always clear'ed)
527 }
528
529 static void appendData(scicos_block * block, int input, double t, double *data)
530 {
531     int i;
532
533     sco_data *sco = (sco_data *) * (block->work);
534
535     if (sco != NULL)
536     {
537
538         /*
539          * Handle the case where the t is greater than the data_bounds
540          */
541         if (t > ((sco->scope.periodCounter + 1) * block->rpar[3]))
542         {
543             sco->scope.periodCounter++;
544
545             // set the buffer coordinates to the last point
546             for (i = 0; i < block->insz[input]; i++)
547             {
548                 sco->internal.bufferCoordinates[input][i][0] = sco->internal.bufferCoordinates[input][i][sco->internal.numberOfPoints - 1];
549                 sco->internal.bufferCoordinates[input][i][block->ipar[2]] = sco->internal.bufferCoordinates[input][i][block->ipar[2] + sco->internal.numberOfPoints - 1];
550             }
551             sco->internal.numberOfPoints = 1;
552
553             // clear the history coordinates
554             sco->internal.maxNumberOfPoints = 0;
555             for (i = 0; i < block->insz[input]; i++)
556             {
557                 if (sco->internal.historyCoordinates[input][i] != NULL)
558                 {
559                     FREE(sco->internal.historyCoordinates[input][i]);
560                     sco->internal.historyCoordinates[input][i] = NULL;
561                 }
562             }
563
564             // configure scope setting
565             if (setPolylinesBounds(block, getAxe(getFigure(block), block, input), sco->scope.periodCounter) == FALSE)
566             {
567                 set_block_error(-5);
568                 freeScoData(block);
569                 sco = NULL;
570             }
571         }
572
573         /*
574          * Handle the case where the scope has more points than maxNumberOfPoints
575          */
576         if (sco->internal.numberOfPoints >= block->ipar[2])
577         {
578             int maxNumberOfPoints = sco->internal.maxNumberOfPoints;
579
580             // on a full scope, re-alloc history coordinates
581             maxNumberOfPoints = maxNumberOfPoints + block->ipar[2];
582             sco = reallocHistoryBuffer(block, maxNumberOfPoints);
583
584             // set the buffer coordinates to the last point
585             for (i = 0; i < block->insz[input]; i++)
586             {
587                 sco->internal.bufferCoordinates[input][i][0] = sco->internal.bufferCoordinates[input][i][block->ipar[2] - 1];
588                 sco->internal.bufferCoordinates[input][i][block->ipar[2]] = sco->internal.bufferCoordinates[input][i][2 * block->ipar[2] - 1];
589             }
590             sco->internal.numberOfPoints = 1;
591
592             // reconfigure related graphic objects
593             if (pushHistory(block, input, sco->internal.maxNumberOfPoints) == FALSE)
594             {
595                 set_block_error(-5);
596                 freeScoData(block);
597                 sco = NULL;
598             }
599         }
600
601         /*
602          * Update data
603          */
604
605         for (i = 0; i < block->insz[input]; i++)
606         {
607             const double value = data[i];
608             setBuffersCoordinates(block, sco->internal.bufferCoordinates[input][i], sco->internal.numberOfPoints, block->ipar[2], t, value);
609         }
610
611         sco->internal.numberOfPoints++;
612     }
613 }
614
615 static BOOL pushData(scicos_block * block, int input, int row)
616 {
617     int iFigureUID;
618     int iAxeUID;
619     int iPolylineUID;
620
621     double *data;
622     sco_data *sco;
623
624     iFigureUID = getFigure(block);
625     iAxeUID = getAxe(iFigureUID, block, input);
626     iPolylineUID = getPolyline(iAxeUID, block, row, FALSE);
627
628     sco = getScoData(block);
629     if (sco == NULL)
630     {
631         return FALSE;
632     }
633
634     // do not push any data if disabled
635     if (sco->scope.disableBufferUpdate == TRUE)
636     {
637         return TRUE;
638     }
639
640     // select the right input and row
641     data = sco->internal.bufferCoordinates[input][row];
642
643     return setGraphicObjectProperty(iPolylineUID, __GO_DATA_MODEL_COORDINATES__, data, jni_double_vector, block->ipar[2]);
644 }
645
646 /*****************************************************************************
647  *
648  * Graphic utils
649  *
650  ****************************************************************************/
651
652 /**
653  * Set properties on the figure.
654  *
655  * \param iFigureUID the figure uid
656  * \param block the current block
657  */
658 static void setFigureSettings(int iFigureUID, scicos_block * block)
659 {
660     char *label = NULL;
661
662     int nipar = GetNipar(block);
663     int *ipar = GetIparPtrs(block);
664
665     int win_pos[2];
666     int win_dim[2];
667
668     win_pos[0] = ipar[(nipar - 1) - 3];
669     win_pos[1] = ipar[(nipar - 1) - 2];
670     win_dim[0] = ipar[(nipar - 1) - 1];
671     win_dim[1] = ipar[nipar - 1];
672
673     if (win_pos[0] > 0 && win_pos[1] > 0)
674     {
675         setGraphicObjectProperty(iFigureUID, __GO_POSITION__, &win_pos, jni_int_vector, 2);
676     }
677
678     if (win_dim[0] > 0 && win_dim[1] > 0)
679     {
680         setGraphicObjectProperty(iFigureUID, __GO_SIZE__, &win_dim, jni_int_vector, 2);
681     }
682
683     label = GetLabelPtrs(block);
684     if (label != NULL)
685     {
686         if (strlen(label) > 0)
687         {
688             setGraphicObjectProperty(iFigureUID, __GO_NAME__, label, jni_string, 1);
689         }
690     }
691 };
692
693 /*****************************************************************************
694  *
695  * Graphic
696  *
697  ****************************************************************************/
698
699 static int getFigure(scicos_block * block)
700 {
701 #ifdef WITH_GUI
702     signed int figNum;
703     int iFigureUID = 0;
704     int iAxe = 0;
705     int i__1 = 1;
706     sco_data *sco = (sco_data *) * (block->work);
707
708     int i;
709
710     // assert the sco is not NULL
711     if (sco == NULL)
712     {
713         return 0;
714     }
715
716     // fast path for an existing object
717     if (sco->scope.cachedFigureUID != 0)
718     {
719         return sco->scope.cachedFigureUID;
720     }
721
722     figNum = block->ipar[0];
723
724     // with a negative id, use the block number indexed from a constant.
725     if (figNum < 0)
726     {
727         figNum = 20000 + get_block_number();
728     }
729
730     iFigureUID = getFigureFromIndex(figNum);
731     // create on demand
732     if (iFigureUID == 0)
733     {
734         iFigureUID = createNewFigureWithAxes();
735         setGraphicObjectProperty(iFigureUID, __GO_ID__, &figNum, jni_int, 1);
736
737         // the stored uid is a reference to the figure map, not to the current figure
738         iFigureUID = getFigureFromIndex(figNum);
739         sco->scope.cachedFigureUID = iFigureUID;
740
741         // set configured parameters
742         setFigureSettings(iFigureUID, block);
743
744         // allocate the axes through the getter
745         for (i = 0; i < GetNin(block); i++)
746         {
747             iAxe = getAxe(iFigureUID, block, i);
748
749             /*
750              * Setup according to block settings
751              */
752             setLabel(iAxe, __GO_X_AXIS_LABEL__, "t");
753             setLabel(iAxe, __GO_Y_AXIS_LABEL__, "y");
754
755             setGraphicObjectProperty(iAxe, __GO_X_AXIS_VISIBLE__, &i__1, jni_bool, 1);
756             setGraphicObjectProperty(iAxe, __GO_Y_AXIS_VISIBLE__, &i__1, jni_bool, 1);
757
758             setPolylinesBounds(block, iAxe, 0);
759         }
760     }
761     else
762     {
763         // set configured parameters
764         setFigureSettings(iFigureUID, block);
765     }
766
767     if (sco->scope.cachedFigureUID == 0)
768     {
769         sco->scope.cachedFigureUID = iFigureUID;
770     }
771     return iFigureUID;
772 #else
773     Coserror("%s: Scilab is compiled without GUI, can not use Scope.", "cscope");
774     return 0;
775 #endif
776 }
777
778 static int getAxe(int iFigureUID, scicos_block * block, int input)
779 {
780     int iAxe;
781     int i;
782     sco_data *sco = (sco_data *) * (block->work);
783
784     // assert the sco is not NULL
785     if (sco == NULL)
786     {
787         return 0;
788     }
789
790     // fast path for an existing object
791     if (sco->scope.cachedAxeUID != 0)
792     {
793         return sco->scope.cachedAxeUID;
794     }
795
796     iAxe = findChildWithKindAt(iFigureUID, __GO_AXES__, input);
797
798     /*
799      * Allocate if necessary
800      */
801     if (iAxe == 0)
802     {
803         cloneAxesModel(iFigureUID);
804         iAxe = findChildWithKindAt(iFigureUID, __GO_AXES__, input);
805     }
806
807     /*
808      * Setup on first access
809      */
810     if (iAxe != 0)
811     {
812         // allocate the polylines through the getter
813         for (i = 0; i < block->insz[input]; i++)
814         {
815             getPolyline(iAxe, block, i, TRUE);
816         }
817         for (i = 0; i < block->insz[input]; i++)
818         {
819             getPolyline(iAxe, block, i, FALSE);
820         }
821     }
822     else
823     {
824         return 0;
825     }
826
827     /*
828      * then cache with local storage
829      */
830     sco->scope.cachedAxeUID = iAxe;
831     return sco->scope.cachedAxeUID;
832 }
833
834 static int getPolyline(int iAxeUID, scicos_block * block, int row, BOOL history)
835 {
836     int iPolyline;
837     BOOL b__true = TRUE;
838
839     int color;
840
841     int* polylinesUIDs;
842     int polylineIndex;
843
844     sco_data *sco = (sco_data *) * (block->work);
845
846     // assert the sco is not NULL
847     if (sco == NULL)
848     {
849         return 0;
850     }
851
852     if (!history)
853     {
854         polylinesUIDs = sco->scope.cachedBufferPolylinesUIDs;
855         polylineIndex = block->insz[0] + row;
856     }
857     else
858     {
859         polylinesUIDs = sco->scope.cachedHistoryPolylinesUIDs;
860         polylineIndex = row;
861     }
862
863     // assert that the structure is in a good shape
864     if (polylinesUIDs == NULL)
865     {
866         return 0;
867     }
868
869     // fast path for an existing object
870     if (polylinesUIDs[row] != 0)
871     {
872         return polylinesUIDs[row];
873     }
874
875     iPolyline = findChildWithKindAt(iAxeUID, __GO_POLYLINE__, polylineIndex);
876
877     /*
878      * Allocate if necessary
879      */
880     if (iPolyline == 0)
881     {
882         iPolyline = createGraphicObject(__GO_POLYLINE__);
883
884         if (iPolyline != 0)
885         {
886             createDataObject(iPolyline, __GO_POLYLINE__);
887             setGraphicObjectRelationship(iAxeUID, iPolyline);
888         }
889         else
890         {
891             return 0;
892         }
893     }
894
895     /*
896      * Setup on first access
897      */
898
899     /*
900      * Default setup of the nGons property
901      */
902     {
903         int nGons = 1;
904         setGraphicObjectProperty(iPolyline, __GO_DATA_MODEL_NUM_GONS__, &nGons, jni_int, 1);
905     }
906
907     color = block->ipar[3 + row];
908     if (color > 0)
909     {
910         setGraphicObjectProperty(iPolyline, __GO_LINE_MODE__, &b__true, jni_bool, 1);
911         setGraphicObjectProperty(iPolyline, __GO_LINE_COLOR__, &color, jni_int, 1);
912     }
913     else
914     {
915         int iMarkSize = 4;
916         color = -color;
917         setGraphicObjectProperty(iPolyline, __GO_MARK_MODE__, &b__true, jni_bool, 1);
918         setGraphicObjectProperty(iPolyline, __GO_MARK_STYLE__, &color, jni_int, 1);
919         setGraphicObjectProperty(iPolyline, __GO_MARK_SIZE__, &iMarkSize, jni_int, 1);
920     }
921
922     {
923         int iClipState = 1; //on
924         setGraphicObjectProperty(iPolyline, __GO_CLIP_STATE__, &iClipState, jni_int, 1);
925     }
926
927     /*
928      * then cache with local storage
929      */
930     polylinesUIDs[row] = iPolyline;
931     return polylinesUIDs[row];
932 }
933
934 static void deleteBufferPolylines(scicos_block * block)
935 {
936     int i, j;
937
938     int iPolylineUID;
939
940     sco_data *sco;
941
942     sco = getScoData(block);
943     for (i = 0; i < block->nin; i++)
944     {
945         for (j = 0; j < block->insz[i]; j++)
946         {
947             iPolylineUID = sco->scope.cachedBufferPolylinesUIDs[j];
948             deleteGraphicObject(iPolylineUID);
949         }
950     }
951 }
952
953 static BOOL pushHistory(scicos_block * block, int input, int maxNumberOfPoints)
954 {
955     int i;
956
957     int iFigureUID;
958     int iAxeUID;
959     int iPolylineUID;
960
961     double *data;
962     sco_data *sco;
963
964     BOOL result = TRUE;
965
966     sco = getScoData(block);
967     iFigureUID = getFigure(block);
968     iAxeUID = getAxe(iFigureUID, block, input);
969
970     // push the data only if the counter == 0, decrement the counter if positive
971     if (sco->scope.historyUpdateCounter > 0)
972     {
973         sco->scope.historyUpdateCounter--;
974     }
975     if (sco->scope.historyUpdateCounter > 0)
976     {
977         return result;
978     }
979
980     for (i = 0; i < block->insz[input]; i++)
981     {
982         iPolylineUID = getPolyline(iAxeUID, block, i, TRUE);
983
984         data = sco->internal.historyCoordinates[input][i];
985         result = setGraphicObjectProperty(iPolylineUID, __GO_DATA_MODEL_COORDINATES__, data, jni_double_vector, maxNumberOfPoints);
986         if (result == FALSE)
987         {
988             return result;
989         }
990     }
991
992     return result;
993 }
994
995 static BOOL setPolylinesBounds(scicos_block * block, int iAxeUID, int periodCounter)
996 {
997     double dataBounds[6];
998     double period = block->rpar[3];
999
1000     dataBounds[0] = periodCounter * period; // xMin
1001     dataBounds[1] = (periodCounter + 1) * period;   // xMax
1002     dataBounds[2] = block->rpar[1]; // yMin
1003     dataBounds[3] = block->rpar[2]; // yMax
1004     dataBounds[4] = -1.0;       // zMin
1005     dataBounds[5] = 1.0;        // zMax
1006
1007     return setGraphicObjectProperty(iAxeUID, __GO_DATA_BOUNDS__, dataBounds, jni_double_vector, 6);
1008 }