2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2011-2012 - Scilab Enterprises - Clement DAVID
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
15 #include "dynlib_scicos_blocks.h"
19 #include "elementary_functions.h"
21 #include "getGraphicObjectProperty.h"
22 #include "setGraphicObjectProperty.h"
23 #include "graphicObjectProperties.h"
24 #include "createGraphicObject.h"
25 #include "deleteGraphicObject.h"
27 #include "CurrentFigure.h"
28 #include "CurrentObject.h"
30 #include "scicos_block4.h"
33 #include "localization.h"
35 #include "strdup_windows.h"
38 #include "FigureList.h"
39 #include "BuildObjects.h"
40 #include "AxesModel.h"
42 #define HISTORY_POINTS_THRESHOLD 4096
44 /*****************************************************************************
45 * Internal container structure
46 ****************************************************************************/
56 double ***bufferCoordinates;
57 int maxNumberOfPoints;
58 double ***historyCoordinates;
65 BOOL disableBufferUpdate;
66 int historyUpdateCounter;
68 char const* cachedFigureUID;
70 char **cachedBufferPolylinesUIDs;
71 char **cachedHistoryPolylinesUIDs;
76 * Get (and allocate on demand) the internal data used on this scope
77 * \param block the block
78 * \return the scope data
80 static sco_data *getScoData(scicos_block * block);
83 * Release any internal data
85 * \param block the block
87 static void freeScoData(scicos_block * block);
90 * Realloc the history buffer data
92 * \param block the block
93 * \param numberOfPoints realloc to store this number of points
95 static sco_data *reallocHistoryBuffer(scicos_block * block, int numberOfPoints);
98 * Set values into the coordinates buffer.
100 * \param block the block
101 * \param coordinates the buffer
102 * \param numberOfPoints the number of points to set (actual)
103 * \param bufferPoints the buffer size (max)
104 * \param t the time to set
105 * \param value the value to set
107 static void setBuffersCoordinates(scicos_block * block, double* coordinates, const int numberOfPoints,
108 const int bufferPoints, const double t, const double value);
111 * Append the data to the current data
113 * \param block the block
114 * \param input the input (0-indexed)
115 * \param t the current time
116 * \param data the data to append
118 static void appendData(scicos_block * block, int input, double t, double *data);
121 * Push the block data to the polyline
123 * \param block the block
124 * \param input the selected input
125 * \param row the selected row
126 * \param pPolylineUID the polyline uid
129 static BOOL pushData(scicos_block * block, int input, int row);
131 /*****************************************************************************
133 ****************************************************************************/
136 * Get (and allocate on demand) the figure associated with the block
137 * \param block the block
138 * \return a valid figure UID or NULL on error
140 static char const* getFigure(scicos_block * block);
143 * Get (and allocate on demand) the axe associated with the input
145 * \param pFigureUID the parent figure UID
146 * \param block the block
147 * \param input the current input index (0-indexed)
148 * \return a valid axe UID or NULL on error
150 static char *getAxe(char const* pFigureUID, scicos_block * block, int input);
153 * Get (and allocate on demand) the polyline associated with the row
155 * \param pAxeUID the parent axe UID
156 * \param block the block
157 * \param row the current row index (0-indexed)
158 * \param history get the history polyline
159 * \return a valid polyline UID or NULL on error
161 static char *getPolyline(char *pAxeUID, scicos_block * block, int row, BOOL history);
164 * Delete all the buffer polylines.
166 * \param block the block
168 static void deleteBufferPolylines(scicos_block * block);
171 * Set the polylines history size and push the history buffer
173 * \param block the block
174 * \param input the input port index
175 * \param maxNumberOfPoints the size of the buffer
177 static BOOL pushHistory(scicos_block * block, int input, int maxNumberOfPoints);
180 * Set the polylines bounds
182 * \param block the block
183 * \param input the input port index
184 * \param periodCounter number of past periods since startup
186 static BOOL setPolylinesBounds(scicos_block * block, int input, int periodCounter);
188 /*****************************************************************************
189 * Simulation function
190 ****************************************************************************/
192 /** \fn void cscope(scicos_block * block,int flag)
193 \brief the computational function
194 \param block A pointer to a scicos_block
195 \param flag An int which indicates the state of the block (init, update, ending)
197 SCICOS_BLOCKS_IMPEXP void cscope(scicos_block * block, scicos_flag flag)
199 char const* pFigureUID;
212 sco = getScoData(block);
218 pFigureUID = getFigure(block);
219 if (pFigureUID == NULL)
228 pFigureUID = getFigure(block);
229 if (pFigureUID == NULL)
236 t = get_scicos_time();
237 u = GetRealInPortPtrs(block, 1);
239 appendData(block, 0, t, u);
241 for (i = 0; i < block->insz[0]; i++)
243 result = pushData(block, 0, i);
246 Coserror("%s: unable to push some data.", "cscope");
253 sco = getScoData(block);
254 sco = reallocHistoryBuffer(block, sco->internal.maxNumberOfPoints + sco->internal.numberOfPoints);
255 sco->scope.disableBufferUpdate = FALSE;
256 sco->scope.historyUpdateCounter = 0;
257 pushHistory(block, 0, sco->internal.maxNumberOfPoints);
258 deleteBufferPolylines(block);
267 /*-------------------------------------------------------------------------*/
269 /*****************************************************************************
271 * Container management
273 ****************************************************************************/
275 static sco_data *getScoData(scicos_block * block)
277 sco_data *sco = (sco_data *) * (block->work);
286 sco = (sco_data *) MALLOC(sizeof(sco_data));
289 goto error_handler_sco;
292 // 0 points out of a block->ipar[2] points buffer
293 sco->internal.numberOfPoints = 0;
295 sco->internal.bufferCoordinates = (double ***)CALLOC(block->nin, sizeof(double **));
296 if (sco->internal.bufferCoordinates == NULL)
298 goto error_handler_bufferCoordinates;
301 for (i = 0; i < block->nin; i++)
303 sco->internal.bufferCoordinates[i] = (double **)CALLOC(block->insz[i], sizeof(double *));
304 if (sco->internal.bufferCoordinates[i] == NULL)
306 goto error_handler_bufferCoordinates_i;
309 for (i = 0; i < block->nin; i++)
311 for (j = 0; j < block->insz[i]; j++)
313 sco->internal.bufferCoordinates[i][j] = (double *)CALLOC(3 * block->ipar[2], sizeof(double));
315 if (sco->internal.bufferCoordinates[i][j] == NULL)
317 goto error_handler_bufferCoordinates_ij;
322 // 0 points out of a 0 points history
323 sco->internal.maxNumberOfPoints = 0;
325 sco->internal.historyCoordinates = (double ***)CALLOC(block->nin, sizeof(double **));
326 if (sco->internal.historyCoordinates == NULL)
328 goto error_handler_historyCoordinates;
331 for (i = 0; i < block->nin; i++)
333 sco->internal.historyCoordinates[i] = (double **)CALLOC(block->insz[i], sizeof(double *));
334 if (sco->internal.historyCoordinates[i] == NULL)
336 goto error_handler_historyCoordinates_i;
340 sco->scope.periodCounter = 0;
342 // flag to avoid pushing the buffer each time
343 sco->scope.disableBufferUpdate = FALSE;
344 // counter use to delay the history push
345 sco->scope.historyUpdateCounter = 0;
347 sco->scope.cachedFigureUID = NULL;
348 sco->scope.cachedAxeUID = NULL;
349 sco->scope.cachedBufferPolylinesUIDs = (char **)CALLOC(block->insz[0], sizeof(char *));
350 sco->scope.cachedHistoryPolylinesUIDs = (char **)CALLOC(block->insz[0], sizeof(char *));
352 *(block->work) = sco;
358 * Error management (out of normal flow)
361 error_handler_historyCoordinates_i:
362 for (j = 0; j < i; j++)
364 FREE(sco->internal.historyCoordinates[i]);
366 FREE(sco->internal.historyCoordinates);
367 error_handler_historyCoordinates:
369 j = block->insz[i] - 1;
370 error_handler_bufferCoordinates_ij:
371 for (k = 0; k < i; k++)
373 for (l = 0; l < j; l++)
375 FREE(sco->internal.bufferCoordinates[k][l]);
379 error_handler_bufferCoordinates_i:
380 for (j = 0; j < i; j++)
382 FREE(sco->internal.bufferCoordinates[i]);
384 FREE(sco->internal.bufferCoordinates);
385 error_handler_bufferCoordinates:
393 static void freeScoData(scicos_block * block)
395 sco_data *sco = (sco_data *) * (block->work);
400 for (i = 0; i < block->nin; i++)
402 for (j = 0; j < block->insz[i]; j++)
404 if (sco->internal.historyCoordinates[i][j] != NULL)
406 FREE(sco->internal.historyCoordinates[i][j]);
408 FREE(sco->internal.bufferCoordinates[i][j]);
410 FREE(sco->internal.historyCoordinates[i]);
411 FREE(sco->internal.bufferCoordinates[i]);
413 FREE(sco->internal.historyCoordinates);
414 FREE(sco->internal.bufferCoordinates);
416 for (i = 0; i < block->insz[0]; i++)
418 FREE(sco->scope.cachedHistoryPolylinesUIDs[i]);
419 FREE(sco->scope.cachedBufferPolylinesUIDs[i]);
421 FREE(sco->scope.cachedAxeUID);
424 *(block->work) = NULL;
428 static sco_data *reallocHistoryBuffer(scicos_block * block, int numberOfPoints)
430 sco_data *sco = (sco_data *) * (block->work);
434 int allocatedNumberOfPoints;
436 int previousNumberOfPoints = sco->internal.maxNumberOfPoints;
437 int numberOfCopiedPoints = numberOfPoints - sco->internal.maxNumberOfPoints;
440 int bufferNumberOfPoints = block->ipar[2];
441 int bufferNewPointInc;
443 if (previousNumberOfPoints == 0)
445 allocatedNumberOfPoints = numberOfPoints;
446 bufferNewPointInc = 0;
450 allocatedNumberOfPoints = numberOfPoints - 1;
451 bufferNewPointInc = 1;
454 if (sco->scope.historyUpdateCounter <= 0)
456 if (numberOfPoints > HISTORY_POINTS_THRESHOLD)
458 sco->scope.disableBufferUpdate = TRUE;
459 sco->scope.historyUpdateCounter = numberOfPoints / HISTORY_POINTS_THRESHOLD;
463 sco->scope.disableBufferUpdate = FALSE;
464 sco->scope.historyUpdateCounter = 0;
468 for (i = 0; i < block->insz[0]; i++)
470 ptr = (double *)MALLOC(3 * allocatedNumberOfPoints * sizeof(double));
476 // memcpy existing X-axis values from the history
477 memcpy(ptr, sco->internal.historyCoordinates[0][i], previousNumberOfPoints * sizeof(double));
478 // memcpy existing Y-axis values from the history
479 memcpy(ptr + allocatedNumberOfPoints, sco->internal.historyCoordinates[0][i] + previousNumberOfPoints, previousNumberOfPoints * sizeof(double));
480 // clear the last points, the Z-axis values
481 memset(ptr + 2 * allocatedNumberOfPoints, 0, allocatedNumberOfPoints * sizeof(double));
483 // then set the last points to the last values for X-axis and Y-axis values from the buffer points
484 buffer = sco->internal.bufferCoordinates[0][i];
485 memcpy(ptr + previousNumberOfPoints, buffer + bufferNewPointInc, (numberOfCopiedPoints - bufferNewPointInc) * sizeof(double));
486 memcpy(ptr + allocatedNumberOfPoints + previousNumberOfPoints, buffer + bufferNumberOfPoints + bufferNewPointInc, (numberOfCopiedPoints - bufferNewPointInc) * sizeof(double));
488 FREE(sco->internal.historyCoordinates[0][i]);
489 sco->internal.historyCoordinates[0][i] = ptr;
492 sco->internal.maxNumberOfPoints = allocatedNumberOfPoints;
502 static void setBuffersCoordinates(scicos_block* block, double* coordinates, const int numberOfPoints,
503 const int bufferPoints, const double t, const double value)
506 sco_data *sco = (sco_data *) * (block->work);
508 if (sco->scope.disableBufferUpdate == TRUE)
510 coordinates[numberOfPoints] = t;
511 coordinates[bufferPoints + numberOfPoints] = value;
515 // X-axis values first
516 for (setLen = numberOfPoints; setLen < bufferPoints; setLen++)
518 coordinates[setLen] = t;
520 // then Y-axis values
521 for (setLen = numberOfPoints; setLen < bufferPoints; setLen++)
523 coordinates[bufferPoints + setLen] = value;
525 // then Z-axis values (always clear'ed)
528 static void appendData(scicos_block * block, int input, double t, double *data)
532 sco_data *sco = (sco_data *) * (block->work);
535 * Handle the case where the t is greater than the data_bounds
537 if (t > ((sco->scope.periodCounter + 1) * block->rpar[3]))
539 sco->scope.periodCounter++;
541 // set the buffer coordinates to the last point
542 for (i = 0; i < block->insz[input]; i++)
544 sco->internal.bufferCoordinates[input][i][0] = sco->internal.bufferCoordinates[input][i][sco->internal.numberOfPoints - 1];
545 sco->internal.bufferCoordinates[input][i][block->ipar[2]] = sco->internal.bufferCoordinates[input][i][block->ipar[2] + sco->internal.numberOfPoints - 1];
547 sco->internal.numberOfPoints = 1;
549 // clear the history coordinates
550 sco->internal.maxNumberOfPoints = 0;
551 for (i = 0; i < block->insz[input]; i++)
553 if (sco->internal.historyCoordinates[input][i] != NULL)
555 FREE(sco->internal.historyCoordinates[input][i]);
556 sco->internal.historyCoordinates[input][i] = NULL;
560 // configure scope setting
561 if (setPolylinesBounds(block, input, sco->scope.periodCounter) == FALSE)
570 * Handle the case where the scope has more points than maxNumberOfPoints
572 if (sco != NULL && sco->internal.numberOfPoints >= block->ipar[2])
574 int maxNumberOfPoints = sco->internal.maxNumberOfPoints;
576 // on a full scope, re-alloc history coordinates
577 maxNumberOfPoints = maxNumberOfPoints + block->ipar[2];
578 sco = reallocHistoryBuffer(block, maxNumberOfPoints);
580 // set the buffer coordinates to the last point
581 for (i = 0; i < block->insz[input]; i++)
583 sco->internal.bufferCoordinates[input][i][0] = sco->internal.bufferCoordinates[input][i][block->ipar[2] - 1];
584 sco->internal.bufferCoordinates[input][i][block->ipar[2]] = sco->internal.bufferCoordinates[input][i][2 * block->ipar[2] - 1];
586 sco->internal.numberOfPoints = 1;
588 // reconfigure related graphic objects
589 if (pushHistory(block, input, sco->internal.maxNumberOfPoints) == FALSE)
602 for (i = 0; i < block->insz[input]; i++)
604 const double value = data[i];
605 setBuffersCoordinates(block, sco->internal.bufferCoordinates[input][i], sco->internal.numberOfPoints, block->ipar[2], t, value);
608 sco->internal.numberOfPoints++;
612 static BOOL pushData(scicos_block * block, int input, int row)
614 char const* pFigureUID;
621 pFigureUID = getFigure(block);
622 pAxeUID = getAxe(pFigureUID, block, input);
623 pPolylineUID = getPolyline(pAxeUID, block, row, FALSE);
625 sco = getScoData(block);
631 // do not push any data if disabled
632 if (sco->scope.disableBufferUpdate == TRUE)
637 // select the right input and row
638 data = sco->internal.bufferCoordinates[input][row];
640 return setGraphicObjectProperty(pPolylineUID, __GO_DATA_MODEL_COORDINATES__, data, jni_double_vector, block->ipar[2]);
643 /*****************************************************************************
647 ****************************************************************************/
650 * Set properties on the figure.
652 * \param pFigureUID the figure uid
653 * \param block the current block
655 static void setFigureSettings(char const* pFigureUID, scicos_block * block)
659 int nipar = GetNipar(block);
660 int *ipar = GetIparPtrs(block);
665 win_pos[0] = ipar[(nipar - 1) - 3];
666 win_pos[1] = ipar[(nipar - 1) - 2];
667 win_dim[0] = ipar[(nipar - 1) - 1];
668 win_dim[1] = ipar[nipar - 1];
670 if (win_pos[0] > 0 && win_pos[1] > 0)
672 setGraphicObjectProperty(pFigureUID, __GO_POSITION__, &win_pos, jni_int_vector, 2);
675 if (win_dim[0] > 0 && win_dim[1] > 0)
677 setGraphicObjectProperty(pFigureUID, __GO_SIZE__, &win_dim, jni_int_vector, 2);
680 label = GetLabelPtrs(block);
683 if (strlen(label) > 0)
685 setGraphicObjectProperty(pFigureUID, __GO_NAME__, label, jni_string, 1);
690 /*****************************************************************************
694 ****************************************************************************/
696 static char const* getFigure(scicos_block * block)
699 char const* pFigureUID = NULL;
702 sco_data *sco = (sco_data *) * (block->work);
706 // assert the sco is not NULL
712 // fast path for an existing object
713 if (sco->scope.cachedFigureUID != NULL)
715 return sco->scope.cachedFigureUID;
718 figNum = block->ipar[0];
720 // with a negative id, use the block number indexed from a constant.
723 figNum = 20000 + get_block_number();
726 pFigureUID = getFigureFromIndex(figNum);
728 if (pFigureUID == NULL)
730 pFigureUID = createNewFigureWithAxes();
731 setGraphicObjectProperty(pFigureUID, __GO_ID__, &figNum, jni_int, 1);
733 // the stored uid is a reference to the figure map, not to the current figure
734 pFigureUID = getFigureFromIndex(figNum);
735 sco->scope.cachedFigureUID = pFigureUID;
737 // set configured parameters
738 setFigureSettings(pFigureUID, block);
740 // allocate the axes through the getter
741 for (i = 0; i < GetNin(block); i++)
743 pAxe = getAxe(pFigureUID, block, i);
746 * Setup according to block settings
748 setLabel(pAxe, __GO_X_AXIS_LABEL__, "t");
749 setLabel(pAxe, __GO_Y_AXIS_LABEL__, "y");
751 setGraphicObjectProperty(pAxe, __GO_X_AXIS_VISIBLE__, &i__1, jni_bool, 1);
752 setGraphicObjectProperty(pAxe, __GO_Y_AXIS_VISIBLE__, &i__1, jni_bool, 1);
754 setPolylinesBounds(block, i, 0);
758 if (sco->scope.cachedFigureUID == NULL)
760 sco->scope.cachedFigureUID = pFigureUID;
765 static char *getAxe(char const* pFigureUID, scicos_block * block, int input)
769 sco_data *sco = (sco_data *) * (block->work);
771 // assert the sco is not NULL
777 // fast path for an existing object
778 if (sco->scope.cachedAxeUID != NULL)
780 return sco->scope.cachedAxeUID;
783 pAxe = findChildWithKindAt(pFigureUID, __GO_AXES__, input);
786 * Allocate if necessary
790 cloneAxesModel(pFigureUID);
791 pAxe = findChildWithKindAt(pFigureUID, __GO_AXES__, input);
795 * Setup on first access
799 // allocate the polylines through the getter
800 for (i = 0; i < block->insz[input]; i++)
802 getPolyline(pAxe, block, i, TRUE);
804 for (i = 0; i < block->insz[input]; i++)
806 getPolyline(pAxe, block, i, FALSE);
811 * then cache with local storage
813 if (pAxe != NULL && sco->scope.cachedAxeUID == NULL)
815 sco->scope.cachedAxeUID = strdup(pAxe);
816 releaseGraphicObjectProperty(__GO_PARENT__, pAxe, jni_string, 1);
818 return sco->scope.cachedAxeUID;
821 static char *getPolyline(char *pAxeUID, scicos_block * block, int row, BOOL history)
828 char** polylinesUIDs;
830 int polylineDefaultNumElement;
832 sco_data *sco = (sco_data *) * (block->work);
834 // assert the sco is not NULL
842 polylinesUIDs = sco->scope.cachedBufferPolylinesUIDs;
843 polylineIndex = block->insz[0] + row;
844 polylineDefaultNumElement = block->ipar[2];
848 polylinesUIDs = sco->scope.cachedHistoryPolylinesUIDs;
850 polylineDefaultNumElement = 0;
853 // fast path for an existing object
854 if (polylinesUIDs != NULL && polylinesUIDs[row] != NULL)
856 return polylinesUIDs[row];
859 pPolyline = findChildWithKindAt(pAxeUID, __GO_POLYLINE__, polylineIndex);
862 * Allocate if necessary
864 if (pPolyline == NULL)
866 pPolyline = createGraphicObject(__GO_POLYLINE__);
868 if (pPolyline != NULL)
870 createDataObject(pPolyline, __GO_POLYLINE__);
871 setGraphicObjectRelationship(pAxeUID, pPolyline);
876 * Setup on first access
878 if (pPolyline != NULL)
882 * Default setup (will crash if removed)
885 int polylineSize[2] = { 1, polylineDefaultNumElement };
886 setGraphicObjectProperty(pPolyline, __GO_DATA_MODEL_NUM_ELEMENTS_ARRAY__, polylineSize, jni_int_vector, 2);
889 color = block->ipar[3 + row];
892 setGraphicObjectProperty(pPolyline, __GO_LINE_MODE__, &b__true, jni_bool, 1);
893 setGraphicObjectProperty(pPolyline, __GO_LINE_COLOR__, &color, jni_int, 1);
898 setGraphicObjectProperty(pPolyline, __GO_MARK_MODE__, &b__true, jni_bool, 1);
899 setGraphicObjectProperty(pPolyline, __GO_MARK_STYLE__, &color, jni_int, 1);
903 int iClipState = 1; //on
904 setGraphicObjectProperty(pPolyline, __GO_CLIP_STATE__, &iClipState, jni_int, 1);
909 * then cache with local storage
911 if (pPolyline != NULL && polylinesUIDs != NULL && polylinesUIDs[row] == NULL)
913 polylinesUIDs[row] = strdup(pPolyline);
914 releaseGraphicObjectProperty(__GO_PARENT__, pPolyline, jni_string, 1);
916 return polylinesUIDs[row];
919 static void deleteBufferPolylines(scicos_block * block)
927 sco = getScoData(block);
928 for (i = 0; i < block->nin; i++)
930 for (j = 0; j < block->insz[i]; j++)
932 pPolylineUID = sco->scope.cachedBufferPolylinesUIDs[j];
933 deleteGraphicObject(pPolylineUID);
938 static BOOL pushHistory(scicos_block * block, int input, int maxNumberOfPoints)
942 char const* pFigureUID;
950 int polylineSize[2] = { 1, maxNumberOfPoints };
952 sco = getScoData(block);
953 pFigureUID = getFigure(block);
954 pAxeUID = getAxe(pFigureUID, block, input);
956 // push the data only if the counter == 0, decrement the counter if positive
957 if (sco->scope.historyUpdateCounter > 0)
959 sco->scope.historyUpdateCounter--;
961 if (sco->scope.historyUpdateCounter > 0)
966 for (i = 0; i < block->insz[input]; i++)
968 pPolylineUID = getPolyline(pAxeUID, block, i, TRUE);
969 result &= setGraphicObjectProperty(pPolylineUID, __GO_DATA_MODEL_NUM_ELEMENTS_ARRAY__, polylineSize, jni_int_vector, 2);
971 data = sco->internal.historyCoordinates[input][i];
972 result &= setGraphicObjectProperty(pPolylineUID, __GO_DATA_MODEL_COORDINATES__, data, jni_double_vector, maxNumberOfPoints);
978 static BOOL setPolylinesBounds(scicos_block * block, int input, int periodCounter)
980 char const* pFigureUID;
983 double dataBounds[6];
984 double period = block->rpar[3];
986 dataBounds[0] = periodCounter * period; // xMin
987 dataBounds[1] = (periodCounter + 1) * period; // xMax
988 dataBounds[2] = block->rpar[1]; // yMin
989 dataBounds[3] = block->rpar[2]; // yMax
990 dataBounds[4] = -1.0; // zMin
991 dataBounds[5] = 1.0; // zMax
993 pFigureUID = getFigure(block);
994 pAxeUID = getAxe(pFigureUID, block, input);
995 return setGraphicObjectProperty(pAxeUID, __GO_DATA_BOUNDS__, dataBounds, jni_double_vector, 6);