Autoscale for cmscope graph
[scilab.git] / scilab / modules / scicos_blocks / src / c / scale_cmscope.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 // #include <stdio.h>
44
45 // #define LOG(...) printf(__VA_ARGS__)
46 #define LOG(...)
47 // #define PUSH_LOG(...) printf(__VA_ARGS__)
48 #define PUSH_LOG(...)
49
50
51 #define HISTORY_POINTS_THRESHOLD 4096
52
53 /*****************************************************************************
54  * Internal container structure
55  ****************************************************************************/
56
57 /**
58  * Container structure
59  */
60 typedef struct
61 {
62     struct
63     {
64         int *numberOfPoints;
65         double ***bufferCoordinates;
66         int *maxNumberOfPoints;
67         double ***historyCoordinates;
68     } internal;
69
70     struct
71     {
72         int *periodCounter;
73
74         BOOL *disableBufferUpdate;
75         int *historyUpdateCounter;
76
77         int cachedFigureUID;
78         int* cachedAxeUID;
79         int** cachedBufferPolylinesUIDs;
80         int** cachedHistoryPolylinesUIDs;
81     } scope;
82 } sco_data;
83
84 /**
85  * Get (and allocate on demand) the internal data used on this scope
86  * \param block the block
87  * \return the scope data
88  */
89 static sco_data *getScoData(scicos_block * block);
90
91 /**
92  * Release any internal data
93  *
94  * \param block the block
95  */
96 static void freeScoData(scicos_block * block);
97
98 /**
99  * Realloc the history buffer data
100  *
101  * \param block the block
102  * \param input the selected input
103  * \param numberOfPoints realloc to store this number of points
104  */
105 static sco_data *reallocHistoryBuffer(scicos_block * block, int input, int numberOfPoints);
106
107 /**
108  * Set values into the coordinates buffer.
109  *
110  * \param block the block
111  * \param input the selected input
112  * \param coordinates the buffer
113  * \param numberOfPoints the number of points to set (actual)
114  * \param bufferPoints the buffer size (max)
115  * \param t the time to set
116  * \param value the value to set
117  */
118 static void setBuffersCoordinates(scicos_block * block, int input, double* coordinates, const int numberOfPoints,
119                                   const int bufferPoints, const double t, const double value);
120
121 /**
122  * Append the data to the current data
123  *
124  * \param block the block
125  * \param input the input (0-indexed)
126  * \param t the current time
127  * \param data the data to append
128  */
129 static void appendData(scicos_block * block, int input, double t, double *data);
130
131 /**
132  * Push the block data to the polyline
133  *
134  * \param block the block
135  * \param input the selected input
136  * \param row the selected row
137  * \param iPolylineUID the polyline uid
138  *
139  */
140 static BOOL pushData(scicos_block * block, int input, int row);
141
142 /*****************************************************************************
143  * Graphics utils
144  ****************************************************************************/
145
146 /**
147  * Get (and allocate on demand) the figure associated with the block
148  * \param block the block
149  * \return a valid figure UID or NULL on error
150  */
151 static int getFigure(scicos_block * block);
152
153 /**
154  * Get (and allocate on demand) the axe associated with the input
155  *
156  * \param iFigureUID the parent figure UID
157  * \param block the block
158  * \param input the current input index (0-indexed)
159  * \return a valid axe UID or NULL on error
160  */
161 static int getAxe(int iFigureUID, scicos_block * block, int input);
162
163 /**
164  * Get (and allocate on demand) the polyline associated with the row
165  *
166  * \param iAxeUID the parent axe UID
167  * \param block the block
168  * \param input the current input index (0-indexed)
169  * \param row the current row index (0-indexed)
170  * \param history get the history polyline
171  * \return a valid polyline UID or NULL on error
172  */
173 static int getPolyline(int iAxeUID, scicos_block * block, int input, int row, BOOL history);
174
175 /**
176  * Delete all the buffer polylines.
177  *
178  * \param block the block
179  */
180 static void deleteBufferPolylines(scicos_block * block);
181
182 /**
183  * Set the polylines history size and push the history buffer
184  *
185  * \param block the block
186  * \param input the input port index
187  * \param maxNumberOfPoints the size of the buffer
188  */
189 static BOOL pushHistory(scicos_block * block, int input, int maxNumberOfPoints);
190
191 /**
192  * Set the polylines bounds
193  *
194  * \param block the block
195  * \param iAxeUID the axe id
196  * \param input the input number
197  * \param periodCounter number of past periods since startup
198  */
199 static BOOL setPolylinesBounds(scicos_block * block, int iAxeUID, int input, int periodCounter);
200
201 /*****************************************************************************
202  * Simulation function
203  ****************************************************************************/
204
205 /** \fn void scale_cmscope(scicos_block * block,int flag)
206     \brief the computational function
207     \param block A pointer to a scicos_block
208     \param flag An int which indicates the state of the block (init, update, ending)
209 */
210 SCICOS_BLOCKS_IMPEXP void scale_cmscope(scicos_block * block, scicos_flag flag)
211 {
212     int iFigureUID;
213
214     double t;
215     double *u;
216     sco_data *sco;
217
218     int i, j;
219     BOOL result;
220
221     switch (flag)
222     {
223
224     case Initialization:
225         sco = getScoData(block);
226         if (sco == NULL)
227         {
228             set_block_error(-5);
229             break;
230         }
231         iFigureUID = getFigure(block);
232         if (iFigureUID == 0)
233         {
234             // allocation error
235             set_block_error(-5);
236             break;
237         }
238         break;
239
240     case StateUpdate:
241         iFigureUID = getFigure(block);
242         if (iFigureUID == 0)
243         {
244             // allocation error
245             set_block_error(-5);
246             break;
247         }
248
249         t = get_scicos_time();
250         for (i = 0; i < block->nin; i++)
251         {
252             u = (double *)block->inptr[i];
253
254             appendData(block, i, t, u);
255             for (j = 0; j < block->insz[i]; j++)
256             {
257                 result = pushData(block, i, j);
258                 if (result == FALSE)
259                 {
260                     Coserror("%s: unable to push some data.", "scale_cmscope");
261                     break;
262                 }
263             }
264         }
265         break;
266
267     case Ending:
268         sco = getScoData(block);
269         for (i = 0; i < block->nin; i++)
270         {
271             sco = reallocHistoryBuffer(block, i, sco->internal.maxNumberOfPoints[i] + sco->internal.numberOfPoints[i]);
272             sco->scope.disableBufferUpdate[i] = FALSE;
273             sco->scope.historyUpdateCounter[i] = 0;
274             pushHistory(block, i, sco->internal.maxNumberOfPoints[i]);
275         }
276         deleteBufferPolylines(block);
277         freeScoData(block);
278         break;
279
280     default:
281         break;
282     }
283 }
284
285 /*-------------------------------------------------------------------------*/
286
287 /*****************************************************************************
288  *
289  * Container management
290  *
291  ****************************************************************************/
292
293 static sco_data *getScoData(scicos_block * block)
294 {
295     sco_data *sco = (sco_data *) * (block->work);
296     int i, j;
297
298     if (sco == NULL)
299     {
300         /*
301          * Data allocation
302          */
303
304         sco = (sco_data *) MALLOC(sizeof(sco_data));
305         if (sco == NULL)
306         {
307             goto error_handler_sco;
308         }
309
310         sco->internal.numberOfPoints = (int *) MALLOC(block->nin * sizeof(int));
311         if (sco->internal.numberOfPoints == NULL)
312         {
313             goto error_handler_numberOfPoints;
314         }
315         sco->internal.maxNumberOfPoints = (int *) MALLOC(block->nin * sizeof(int));
316         if (sco->internal.maxNumberOfPoints == NULL)
317         {
318             goto error_handler_maxNumberOfPoints;
319         }
320
321         for (i = 0; i < block->nin; i++)
322         {
323             // 0 points out of a block->ipar[2] points buffer
324             sco->internal.numberOfPoints[i] = 0;
325             // 0 points out of a 0 points history
326             sco->internal.maxNumberOfPoints[i] = 0;
327         }
328
329         sco->internal.bufferCoordinates = (double ***)CALLOC(block->nin, sizeof(double **));
330         if (sco->internal.bufferCoordinates == NULL)
331         {
332             goto error_handler_bufferCoordinates;
333         }
334
335         for (i = 0; i < block->nin; i++)
336         {
337             sco->internal.bufferCoordinates[i] = (double **)CALLOC(block->insz[i], sizeof(double *));
338             if (sco->internal.bufferCoordinates[i] == NULL)
339             {
340                 goto error_handler_bufferCoordinates_i;
341             }
342         }
343         for (i = 0; i < block->nin; i++)
344         {
345             for (j = 0; j < block->insz[i]; j++)
346             {
347                 sco->internal.bufferCoordinates[i][j] = (double *)CALLOC(3 * block->ipar[2], sizeof(double));
348
349                 if (sco->internal.bufferCoordinates[i][j] == NULL)
350                 {
351                     goto error_handler_bufferCoordinates_ij;
352                 }
353             }
354         }
355
356         sco->internal.historyCoordinates = (double ***)CALLOC(block->nin, sizeof(double **));
357         if (sco->internal.historyCoordinates == NULL)
358         {
359             goto error_handler_historyCoordinates;
360         }
361
362         for (i = 0; i < block->nin; i++)
363         {
364             sco->internal.historyCoordinates[i] = (double **)CALLOC(block->insz[i], sizeof(double *));
365             if (sco->internal.historyCoordinates[i] == NULL)
366             {
367                 goto error_handler_historyCoordinates_i;
368             }
369         }
370
371         sco->scope.periodCounter = (int *) CALLOC(block->nin, sizeof(int));
372         if (sco->scope.periodCounter == NULL)
373         {
374             goto error_handler_periodCounter;
375         }
376
377         sco->scope.disableBufferUpdate = (BOOL *) CALLOC(block->nin, sizeof(BOOL));
378         if (sco->scope.disableBufferUpdate == NULL)
379         {
380             goto error_handler_disableBufferUpdate;
381         }
382         sco->scope.historyUpdateCounter = (int *) CALLOC(block->nin, sizeof(int));
383         if (sco->scope.historyUpdateCounter == NULL)
384         {
385             goto error_handler_historyUpdateCounter;
386         }
387
388         sco->scope.cachedFigureUID = 0;
389         sco->scope.cachedAxeUID = (int*)CALLOC(block->nin, sizeof(int));
390
391         sco->scope.cachedBufferPolylinesUIDs = (int**)CALLOC(block->nin, sizeof(int*));
392         sco->scope.cachedHistoryPolylinesUIDs = (int**)CALLOC(block->nin, sizeof(int*));
393         for (i = 0; i < block->nin; i++)
394         {
395             sco->scope.cachedBufferPolylinesUIDs[i] = (int*)CALLOC(block->insz[i], sizeof(int));
396             sco->scope.cachedHistoryPolylinesUIDs[i] = (int*)CALLOC(block->insz[i], sizeof(int));
397         }
398
399         *(block->work) = sco;
400     }
401
402     return sco;
403
404     /*
405      * Error management (out of normal flow)
406      */
407
408 error_handler_historyUpdateCounter:
409     FREE(sco->scope.disableBufferUpdate);
410 error_handler_disableBufferUpdate:
411     FREE(sco->scope.periodCounter);
412 error_handler_periodCounter:
413     i = block->nin;
414 error_handler_historyCoordinates_i:
415     for (j = 0; j < i; j++)
416     {
417         FREE(sco->internal.historyCoordinates[j]);
418     }
419     FREE(sco->internal.historyCoordinates);
420 error_handler_historyCoordinates:
421 error_handler_bufferCoordinates_ij:
422     for (i = 0; i < block->nin - 1; i++)
423     {
424         for (j = 0; j < block->insz[i] - 1; j++)
425         {
426             double* ptr = sco->internal.bufferCoordinates[i][j];
427             if (ptr != NULL)
428             {
429                 FREE(ptr);
430             }
431         }
432     }
433     i = block->nin - 1;
434 error_handler_bufferCoordinates_i:
435     for (j = 0; j < i; j++)
436     {
437         FREE(sco->internal.bufferCoordinates[j]);
438     }
439     FREE(sco->internal.bufferCoordinates);
440 error_handler_bufferCoordinates:
441     FREE(sco->internal.maxNumberOfPoints);
442 error_handler_maxNumberOfPoints:
443     FREE(sco->internal.numberOfPoints);
444 error_handler_numberOfPoints:
445     FREE(sco);
446 error_handler_sco:
447     // allocation error
448     set_block_error(-5);
449     return NULL;
450 }
451
452 static void freeScoData(scicos_block * block)
453 {
454     sco_data *sco = (sco_data *) * (block->work);
455     int i, j;
456
457     if (sco != NULL)
458     {
459         for (i = 0; i < block->nin; i++)
460         {
461             for (j = 0; j < block->insz[i]; j++)
462             {
463                 if (sco->internal.historyCoordinates[i][j] != NULL)
464                 {
465                     FREE(sco->internal.historyCoordinates[i][j]);
466                 }
467                 FREE(sco->internal.bufferCoordinates[i][j]);
468             }
469             FREE(sco->internal.historyCoordinates[i]);
470             FREE(sco->internal.bufferCoordinates[i]);
471         }
472         FREE(sco->internal.historyCoordinates);
473         FREE(sco->internal.bufferCoordinates);
474
475         FREE(sco->scope.periodCounter);
476
477         FREE(sco->scope.disableBufferUpdate);
478         FREE(sco->scope.historyUpdateCounter);
479
480         for (i = 0; i < block->nin; i++)
481         {
482             FREE(sco->scope.cachedHistoryPolylinesUIDs[i]);
483             FREE(sco->scope.cachedBufferPolylinesUIDs[i]);
484         }
485
486         FREE(sco->scope.cachedHistoryPolylinesUIDs);
487         FREE(sco->scope.cachedBufferPolylinesUIDs);
488         FREE(sco->scope.cachedAxeUID);
489         FREE(sco);
490         *(block->work) = NULL;
491     }
492 }
493
494 static sco_data *reallocHistoryBuffer(scicos_block * block, int input, int numberOfPoints)
495 {
496     sco_data *sco = (sco_data *) * (block->work);
497     int i;
498
499     double *ptr;
500     int allocatedNumberOfPoints;
501
502     int previousNumberOfPoints = sco->internal.maxNumberOfPoints[input];
503     int numberOfCopiedPoints = numberOfPoints - sco->internal.maxNumberOfPoints[input];
504
505     double *buffer;
506     int bufferNumberOfPoints = block->ipar[2];
507     int bufferNewPointInc;
508
509     if (previousNumberOfPoints == 0)
510     {
511         allocatedNumberOfPoints = numberOfPoints;
512         bufferNewPointInc = 0;
513     }
514     else
515     {
516         allocatedNumberOfPoints = numberOfPoints - 1;
517         bufferNewPointInc = 1;
518     }
519
520     if (sco->scope.historyUpdateCounter[input] <= 0)
521     {
522         if (numberOfPoints > HISTORY_POINTS_THRESHOLD)
523         {
524             sco->scope.disableBufferUpdate[input] = TRUE;
525             sco->scope.historyUpdateCounter[input] = numberOfPoints / HISTORY_POINTS_THRESHOLD;
526         }
527         else
528         {
529             sco->scope.disableBufferUpdate[input] = FALSE;
530             sco->scope.historyUpdateCounter[input] = 0;
531         }
532     }
533
534     for (i = 0; i < block->insz[input]; i++)
535     {
536         ptr = (double *)MALLOC(3 * allocatedNumberOfPoints * sizeof(double));
537         if (ptr == NULL)
538         {
539             goto error_handler;
540         }
541
542         // memcpy existing X-axis values from the history
543         memcpy(ptr, sco->internal.historyCoordinates[input][i], previousNumberOfPoints * sizeof(double));
544         // memcpy existing Y-axis values from the history
545         memcpy(ptr + allocatedNumberOfPoints, sco->internal.historyCoordinates[input][i] + previousNumberOfPoints, previousNumberOfPoints * sizeof(double));
546         // clear the last points, the Z-axis values
547         memset(ptr + 2 * allocatedNumberOfPoints, 0, allocatedNumberOfPoints * sizeof(double));
548
549         // then set the last points to the last values for X-axis and Y-axis values from the buffer points
550         buffer = sco->internal.bufferCoordinates[input][i];
551         memcpy(ptr + previousNumberOfPoints, buffer + bufferNewPointInc, (numberOfCopiedPoints - bufferNewPointInc) * sizeof(double));
552         memcpy(ptr + allocatedNumberOfPoints + previousNumberOfPoints, buffer + bufferNumberOfPoints + bufferNewPointInc, (numberOfCopiedPoints - bufferNewPointInc) * sizeof(double));
553
554         FREE(sco->internal.historyCoordinates[input][i]);
555         sco->internal.historyCoordinates[input][i] = ptr;
556     }
557
558     sco->internal.maxNumberOfPoints[input] = allocatedNumberOfPoints;
559     return sco;
560
561 error_handler:
562     freeScoData(block);
563     // allocation error
564     set_block_error(-5);
565     return NULL;
566 }
567
568 static void setBuffersCoordinates(scicos_block * block, int input, double* coordinates, const int numberOfPoints,
569                                   const int bufferPoints, const double t, const double value)
570 {
571     int setLen;
572     sco_data *sco = (sco_data *) * (block->work);
573
574     if (sco->scope.disableBufferUpdate[input] == TRUE)
575     {
576         coordinates[numberOfPoints] = t;
577         coordinates[bufferPoints + numberOfPoints] = value;
578         return;
579     }
580
581     // X-axis values first
582     for (setLen = numberOfPoints; setLen < bufferPoints; setLen++)
583     {
584         coordinates[setLen] = t;
585     }
586     // then Y-axis values
587     for (setLen = numberOfPoints; setLen < bufferPoints; setLen++)
588     {
589         coordinates[bufferPoints + setLen] = value;
590     }
591     // then Z-axis values (always clear'ed)
592 }
593
594 static void appendData(scicos_block * block, int input, double t, double *data)
595 {
596     int i;
597
598     sco_data *sco = (sco_data *) * (block->work);
599
600     if (sco != NULL)
601     {
602
603         /*
604          * Handle the case where the t is greater than the data_bounds
605          */
606         if (t > ((sco->scope.periodCounter[input] + 1) * block->rpar[1 + input]))
607         {
608             sco->scope.periodCounter[input]++;
609
610             // set the buffer coordinates to the last point
611             for (i = 0; i < block->insz[input]; i++)
612             {
613                 sco->internal.bufferCoordinates[input][i][0] = sco->internal.bufferCoordinates[input][i][sco->internal.numberOfPoints[input] - 1];
614                 sco->internal.bufferCoordinates[input][i][block->ipar[2]] = sco->internal.bufferCoordinates[input][i][block->ipar[2] + sco->internal.numberOfPoints[input] - 1];
615             }
616             sco->internal.numberOfPoints[input] = 1;
617
618             // clear the history coordinates
619             sco->internal.maxNumberOfPoints[input] = 0;
620             for (i = 0; i < block->insz[input]; i++)
621             {
622                 if (sco->internal.historyCoordinates[input][i] != NULL)
623                 {
624                     FREE(sco->internal.historyCoordinates[input][i]);
625                     sco->internal.historyCoordinates[input][i] = NULL;
626                 }
627             }
628
629             // configure scope setting
630             if (setPolylinesBounds(block, getAxe(getFigure(block), block, input), input, sco->scope.periodCounter[input]) == FALSE)
631             {
632                 set_block_error(-5);
633                 freeScoData(block);
634                 sco = NULL;
635             }
636         }
637
638         /*
639          * Handle the case where the scope has more points than maxNumberOfPoints
640          */
641         if (sco->internal.numberOfPoints[input] >= block->ipar[2])
642         {
643             int maxNumberOfPoints = sco->internal.maxNumberOfPoints[input];
644
645             // on a full scope, re-alloc history coordinates
646             maxNumberOfPoints = maxNumberOfPoints + block->ipar[2];
647             sco = reallocHistoryBuffer(block, input, maxNumberOfPoints);
648
649             // set the buffer coordinates to the last point
650             for (i = 0; i < block->insz[input]; i++)
651             {
652                 sco->internal.bufferCoordinates[input][i][0] = sco->internal.bufferCoordinates[input][i][block->ipar[2] - 1];
653                 sco->internal.bufferCoordinates[input][i][block->ipar[2]] = sco->internal.bufferCoordinates[input][i][2 * block->ipar[2] - 1];
654             }
655             sco->internal.numberOfPoints[input] = 1;
656
657             // reconfigure related graphic objects
658             if (pushHistory(block, input, sco->internal.maxNumberOfPoints[input]) == FALSE)
659             {
660                 set_block_error(-5);
661                 freeScoData(block);
662                 sco = NULL;
663             }
664         }
665
666         /*
667          * Update data
668         */
669
670         for (i = 0; i < block->insz[input]; i++)
671         {
672             const double value = data[i];
673             setBuffersCoordinates(block, input, sco->internal.bufferCoordinates[input][i], sco->internal.numberOfPoints[input], block->ipar[2], t, value);
674
675
676             if(block->rpar[0]==1) {
677                 double max_curr_val,prev_max_curr_val,min_curr_val,prev_min_curr_val;
678                 //Get the current maximum value of the axes
679                 max_curr_val = block->rpar[block->nrpar - 2 * (block->nin) + 2 * input+1];
680                 prev_max_curr_val = max_curr_val;
681
682
683                 //Get the current minimum value of the axes
684                 min_curr_val = block->rpar[block->nrpar - 2 * (block->nin) + 2 * input];
685                 prev_min_curr_val = min_curr_val;
686
687                 /* If the value to be plotted exceeds the current range, then we update the range
688                  * We could update the range from current value to the new value i.e. (value + R)
689                  *where R varies from 0 to approx 150. So we have used (value + 100.0) to give the graph good feature.
690                  *
691                  * However, the auto-scaling feature implemented is general
692                 */
693
694                 //If the value to be plotted is greater than or equal to the current max, then update the current max
695                 if(value >= max_curr_val)
696                 {
697                     max_curr_val = value + 10.0;
698                     block->rpar[block->nrpar - 2 * (block->nin) + 2 * input+1] = max_curr_val;
699                 }
700
701                 //If the value to be plotted is smaller than or equal to the current min, then update the current min
702                 if(value <= min_curr_val)
703                 {
704                     min_curr_val = value - 10.0;
705                     block->rpar[block->nrpar - 2 * (block->nin) + 2 * input] = min_curr_val;
706                 }
707
708                 //If value has changed, call the setPolylinesBounds function to update the ranges
709                 if((max_curr_val != prev_max_curr_val) || (min_curr_val != prev_min_curr_val))
710                 {
711                     if (setPolylinesBounds(block, getAxe(getFigure(block), block, input), input, sco->scope.periodCounter[input]) == FALSE)
712                     {
713                         set_block_error(-5);
714                         freeScoData(block);
715                         sco = NULL;
716                     }
717                 }
718             }
719         }
720
721         sco->internal.numberOfPoints[input]++;
722     }
723 }
724
725 static BOOL pushData(scicos_block * block, int input, int row)
726 {
727     int iFigureUID;
728     int iAxeUID;
729     int iPolylineUID;
730
731     double *data;
732     sco_data *sco;
733
734     iFigureUID = getFigure(block);
735     iAxeUID = getAxe(iFigureUID, block, input);
736     iPolylineUID = getPolyline(iAxeUID, block, input, row, FALSE);
737
738     sco = getScoData(block);
739     if (sco == NULL)
740     {
741         return FALSE;
742     }
743
744     // do not push any data if disabled
745     if (sco->scope.disableBufferUpdate[input] == TRUE)
746     {
747         return TRUE;
748     }
749
750     // select the right input and row
751     data = sco->internal.bufferCoordinates[input][row];
752
753     PUSH_LOG("%s: %d\n", "pushData", block->ipar[2]);
754     return setGraphicObjectProperty(iPolylineUID, __GO_DATA_MODEL_COORDINATES__, data, jni_double_vector, block->ipar[2]);
755 }
756
757 /*****************************************************************************
758  *
759  * Graphic utils
760  *
761  ****************************************************************************/
762
763 /**
764  * Set properties on the figure.
765  *
766  * \param iFigureUID the figure uid
767  * \param block the current block
768  */
769 static void setFigureSettings(int iFigureUID, scicos_block * block)
770 {
771     int win_pos[2];
772     int win_dim[2];
773     char* label;
774
775     int *ipar = block->ipar;
776
777     win_pos[0] = ipar[3];
778     win_pos[1] = ipar[4];
779     win_dim[0] = ipar[5];
780     win_dim[1] = ipar[6];
781
782     if (win_pos[0] > 0 && win_pos[1] > 0)
783     {
784         setGraphicObjectProperty(iFigureUID, __GO_POSITION__, &win_pos, jni_int_vector, 2);
785     }
786
787     if (win_dim[0] > 0 && win_dim[1] > 0)
788     {
789         setGraphicObjectProperty(iFigureUID, __GO_SIZE__, &win_dim, jni_int_vector, 2);
790     }
791
792     label = GetLabelPtrs(block);
793     if (label != NULL)
794     {
795         if (strlen(label) > 0)
796         {
797             setGraphicObjectProperty(iFigureUID, __GO_NAME__, label, jni_string, 1);
798         }
799     }
800 }
801
802 /**
803  * Set properties on the axes.
804  *
805  * \param iAxeUID the axe uid
806  * \param block the current block
807  * \param index axe index (0-indexed)
808  */
809 static void setAxesSettings(int iAxeUID, scicos_block * block, int index)
810 {
811     double axesBounds[4];
812     double margins[4];
813
814     double nin = (double) block->nin;
815
816     axesBounds[0] = 0;              // x
817     axesBounds[1] = index / nin;    // y
818     axesBounds[2] = 1.0;            // w
819     axesBounds[3] = 1 / nin;        // h
820     setGraphicObjectProperty(iAxeUID, __GO_AXES_BOUNDS__, axesBounds, jni_double_vector, 4);
821
822     margins[0] = 0.125;
823     margins[1] = 0.125;
824     margins[2] = 0.125;
825     margins[3] = 0.125;
826     setGraphicObjectProperty(iAxeUID, __GO_MARGINS__, margins, jni_double_vector, 4);
827
828 };
829
830 /*****************************************************************************
831  *
832  * Graphic
833  *
834  ****************************************************************************/
835
836 static int getFigure(scicos_block * block)
837 {
838     signed int figNum;
839     int iFigureUID = 0;
840     int iAxe = 0;
841     int i__1 = 1;
842     sco_data *sco = (sco_data *) * (block->work);
843
844     int i;
845
846     // assert the sco is not NULL
847     if (sco == NULL)
848     {
849         return 0;
850     }
851
852     // fast path for an existing object
853     if (sco->scope.cachedFigureUID)
854     {
855         return sco->scope.cachedFigureUID;
856     }
857
858     figNum = block->ipar[0];
859
860     // with a negative id, use the block number indexed from a constant.
861     if (figNum < 0)
862     {
863         figNum = 20000 + get_block_number();
864     }
865
866     iFigureUID = getFigureFromIndex(figNum);
867     // create on demand
868     if (iFigureUID == 0)
869     {
870         iFigureUID = createNewFigureWithAxes();
871         setGraphicObjectProperty(iFigureUID, __GO_ID__, &figNum, jni_int, 1);
872
873         // the stored uid is a reference to the figure map, not to the current figure
874         iFigureUID = getFigureFromIndex(figNum);
875         sco->scope.cachedFigureUID = iFigureUID;
876
877         // set configured parameters
878         setFigureSettings(iFigureUID, block);
879
880         // allocate the axes through the getter
881         for (i = 0; i < GetNin(block); i++)
882         {
883             iAxe = getAxe(iFigureUID, block, i);
884
885             /*
886              * Setup according to block settings
887              */
888             setLabel(iAxe, __GO_X_AXIS_LABEL__, "t");
889             setLabel(iAxe, __GO_Y_AXIS_LABEL__, "y");
890
891             setGraphicObjectProperty(iAxe, __GO_X_AXIS_VISIBLE__, &i__1, jni_bool, 1);
892             setGraphicObjectProperty(iAxe, __GO_Y_AXIS_VISIBLE__, &i__1, jni_bool, 1);
893
894             setPolylinesBounds(block, iAxe, i, 0);
895         }
896     }
897     else
898     {
899         // set configured parameters
900         setFigureSettings(iFigureUID, block);
901     }
902
903     if (sco->scope.cachedFigureUID == 0)
904     {
905         sco->scope.cachedFigureUID = iFigureUID;
906     }
907     return iFigureUID;
908 }
909
910 static int getAxe(int iFigureUID, scicos_block * block, int input)
911 {
912     int iAxe;
913     int i;
914     sco_data *sco = (sco_data *) * (block->work);
915
916     // assert the sco is not NULL
917     if (sco == NULL || sco->scope.cachedAxeUID == NULL)
918     {
919         return 0;
920     }
921
922     // fast path for an existing object
923     if (sco->scope.cachedAxeUID[input])
924     {
925         return sco->scope.cachedAxeUID[input];
926     }
927
928     iAxe = findChildWithKindAt(iFigureUID, __GO_AXES__, input);
929
930     /*
931      * Allocate if necessary
932      */
933     if (iAxe == 0)
934     {
935         cloneAxesModel(iFigureUID);
936         iAxe = findChildWithKindAt(iFigureUID, __GO_AXES__, input);
937
938         // seems that the graphic is unable to clone the default axe model
939         if (iAxe == 0)
940         {
941             return 0;
942         }
943     }
944
945     /*
946      * Setup on first access
947      */
948     // allocate the polylines through the getter
949     for (i = 0; i < block->insz[input]; i++)
950     {
951         getPolyline(iAxe, block, input, i, TRUE);
952     }
953     for (i = 0; i < block->insz[input]; i++)
954     {
955         getPolyline(iAxe, block, input, i, FALSE);
956     }
957
958     setAxesSettings(iAxe, block, input);
959
960     /*
961      * then cache with local storage
962      */
963     sco->scope.cachedAxeUID[input] = iAxe;
964     return sco->scope.cachedAxeUID[input];
965 }
966
967 static int getPolyline(int iAxeUID, scicos_block * block, int input, int row, BOOL history)
968 {
969     int iPolyline;
970     BOOL b__true = TRUE;
971
972     int color;
973
974     int** piPolylinesUIDs;
975     int polylineIndex;
976
977     sco_data *sco = (sco_data *) * (block->work);
978
979     // assert the sco is not NULL
980     if (sco == NULL)
981     {
982         return 0;
983     }
984
985     if (!history)
986     {
987         piPolylinesUIDs = sco->scope.cachedBufferPolylinesUIDs;
988         polylineIndex = block->insz[input] + row;
989     }
990     else
991     {
992         piPolylinesUIDs = sco->scope.cachedHistoryPolylinesUIDs;
993         polylineIndex = row;
994     }
995
996     // assert that the structure is in a good shape
997     if (piPolylinesUIDs == NULL || piPolylinesUIDs[input] == NULL)
998     {
999         return 0;
1000     }
1001
1002     // fast path for an existing object
1003     if (piPolylinesUIDs[input][row] != 0)
1004     {
1005         return piPolylinesUIDs[input][row];
1006     }
1007
1008     iPolyline = findChildWithKindAt(iAxeUID, __GO_POLYLINE__, polylineIndex);
1009
1010     /*
1011      * Allocate if necessary
1012      */
1013     if (iPolyline == 0)
1014     {
1015         iPolyline = createGraphicObject(__GO_POLYLINE__);
1016
1017         if (iPolyline != 0)
1018         {
1019             createDataObject(iPolyline, __GO_POLYLINE__);
1020             setGraphicObjectRelationship(iAxeUID, iPolyline);
1021         }
1022         else
1023         {
1024             return 0;
1025         }
1026     }
1027
1028     /*
1029      * Setup on first access
1030      */
1031
1032     /*
1033      * Default setup of the nGons property
1034      */
1035     {
1036         int nGons = 1;
1037         setGraphicObjectProperty(iPolyline, __GO_DATA_MODEL_NUM_GONS__, &nGons, jni_int, 1);
1038     }
1039
1040     // ipar=[win;size(in,'*');N;wpos(:);wdim(:);in(:);clrs(:);heritance]
1041     //        1     1         1   2       2      nin   nin       1
1042     color = block->ipar[7 + block->nin + input + row];
1043     if (color > 0)
1044     {
1045         LOG("%s: %s at %d at %d to %d\n", "scale_cmscope", "set lines mode", input, row, color);
1046
1047         setGraphicObjectProperty(iPolyline, __GO_LINE_MODE__, &b__true, jni_bool, 1);
1048         setGraphicObjectProperty(iPolyline, __GO_LINE_COLOR__, &color, jni_int, 1);
1049     }
1050     else
1051     {
1052         int iMarkSize = 4;
1053         color = -color;
1054
1055         LOG("%s: %s at %d at %d to %d\n", "scale_cmscope", "set mark mode", input, row, -color);
1056
1057         setGraphicObjectProperty(iPolyline, __GO_MARK_MODE__, &b__true, jni_bool, 1);
1058         setGraphicObjectProperty(iPolyline, __GO_MARK_STYLE__, &color, jni_int, 1);
1059         setGraphicObjectProperty(iPolyline, __GO_MARK_SIZE__, &iMarkSize, jni_int, 1);
1060     }
1061
1062     {
1063         int iClipState = 1; //on
1064         setGraphicObjectProperty(iPolyline, __GO_CLIP_STATE__, &iClipState, jni_int, 1);
1065     }
1066
1067     /*
1068      * then cache with local storage
1069      */
1070     piPolylinesUIDs[input][row] = iPolyline;
1071     return piPolylinesUIDs[input][row];
1072 }
1073
1074 static void deleteBufferPolylines(scicos_block * block)
1075 {
1076     int i, j;
1077
1078     int iPolylineUID;
1079
1080     sco_data *sco;
1081
1082     sco = getScoData(block);
1083     for (i = 0; i < block->nin; i++)
1084     {
1085         for (j = 0; j < block->insz[i]; j++)
1086         {
1087             iPolylineUID = sco->scope.cachedBufferPolylinesUIDs[i][j];
1088             deleteGraphicObject(iPolylineUID);
1089         }
1090     }
1091 }
1092
1093 static BOOL pushHistory(scicos_block * block, int input, int maxNumberOfPoints)
1094 {
1095     int i;
1096
1097     int iFigureUID;
1098     int iAxeUID;
1099     int iPolylineUID;
1100
1101     double *data;
1102     sco_data *sco;
1103
1104     BOOL result = TRUE;
1105
1106     sco = getScoData(block);
1107     iFigureUID = getFigure(block);
1108     iAxeUID = getAxe(iFigureUID, block, input);
1109
1110     // push the data only if the counter == 0, decrement the counter if positive
1111     if (sco->scope.historyUpdateCounter[input] > 0)
1112     {
1113         sco->scope.historyUpdateCounter[input]--;
1114     }
1115     if (sco->scope.historyUpdateCounter[input] > 0)
1116     {
1117         return result;
1118     }
1119
1120     for (i = 0; i < block->insz[input]; i++)
1121     {
1122         iPolylineUID = getPolyline(iAxeUID, block, input, i, TRUE);
1123
1124         data = sco->internal.historyCoordinates[input][i];
1125
1126         PUSH_LOG("%s: %d\n", "pushHistory", maxNumberOfPoints);
1127         result = setGraphicObjectProperty(iPolylineUID, __GO_DATA_MODEL_COORDINATES__, data, jni_double_vector, maxNumberOfPoints);
1128         if (result == FALSE)
1129         {
1130             return result;
1131         }
1132     }
1133
1134     return result;
1135 }
1136
1137 static BOOL setPolylinesBounds(scicos_block * block, int iAxeUID, int input, int periodCounter)
1138 {
1139     double dataBounds[6];
1140     int nin = block->nin;
1141     double period = block->rpar[block->nrpar - 3 * nin + input];
1142
1143     dataBounds[0] = periodCounter * period; // xMin
1144     dataBounds[1] = (periodCounter + 1) * period;   // xMax
1145     dataBounds[2] = block->rpar[block->nrpar - 2 * nin + 2 * input];    // yMin
1146     dataBounds[3] = block->rpar[block->nrpar - 2 * nin + 2 * input + 1];    // yMax
1147     dataBounds[4] = -1.0;       // zMin
1148     dataBounds[5] = 1.0;        // zMax
1149
1150     LOG("%s: %s at %d to %f\n", "scale_cmscope", "setPolylinesBounds", input, dataBounds[1]);
1151
1152     return setGraphicObjectProperty(iAxeUID, __GO_DATA_BOUNDS__, dataBounds, jni_double_vector, 6);
1153 }