/***************************************************************************** File Name: FIXFUNC.C Program Name: Eye Fixation Analysis Functions Company: LC Technologies, Inc. 9455 Silver King Court Fairfax, VA 22031 (703) 385-7133 Makers of the Eyegaze System, additional information about LC Technologies and its products may be found at http://www.eyegaze.com *****************************************************************************/ /* ARF additions */ #include #include #include #include typedef char BYTE; typedef char SBYTE; typedef int SINT; typedef long SLONG; typedef float SFLOAT; typedef void SVOID; #define MOVING 0 #define FIXATING 1 #define FIXATION_COMPLETED 2 #define TRUE 1 #define FALSE 0 /* end ARF additions */ /* FUNCTION, VARIABLE AND CONSTANT DEFINITONS: */ #define RING_SIZE 31 /* length of the delay line in */ /* DetectFixation() -- */ /* should be greater than */ /* minimum_fix_samples */ /****************************************************************************/ /* FUNCTION PROTOTYPES: */ SVOID ResetPresFixation(void); SVOID ResetNewFixation(void); SVOID StartPresFixAtGazepoint(float x_gaze, float y_gaze); SVOID StartNewFixAtGazepoint(float x_gaze, float y_gaze); SVOID UpdatePresFixation(float x_gaze, float y_gaze, int minimum_fix_samples); SVOID UpdateNewFixation(float x_gaze, float y_gaze); SVOID CalcGazeDeviationFromPresFix(float x_gaze, float y_gaze); SVOID CalcGazeDeviationFromNewFix(float x_gaze, float y_gaze); SVOID CheckIfFixating(int minimum_fix_samples); SVOID MoveNewFixToPresFix(int minimum_fix_samples); SVOID DeclareCompletedFixation(int minimum_fix_samples); SVOID RestoreOutPoints(void); /****************************************************************************/ /* GLOBAL FIXFUNC VARIABLES: */ SLONG lCallCount; /* number of times this function has been */ /* called since it was initialized */ /* (30ths or 60ths of a second, */ /* depending on eyetracking sample rate) */ SINT iNNoEyeFound; /* number of successive samples with no */ /* eye found */ /* DATA ON PREVIOUS FIXATION */ SLONG lPrevFixEndCount; /* count that the previous fixation ended */ /* DATA ON PRESENT FIXATION */ SLONG lPresFixStartCount; /* call count that the fixation starts */ SLONG lPresFixEndCount; /* call count that the fixation ends */ SINT iNPresFixSamples; /* number of samples in the present fix */ SFLOAT fXPresFixSum; /* summations for calculation of average */ SFLOAT fYPresFixSum; /* fixation position */ SFLOAT fXPresFix; /* average coordinate of the eye fixation */ SFLOAT fYPresFix; /* point (user selected units) */ SINT iNPresOut; /* number of samples outside the fixation */ SFLOAT fPresDr; /* difference between gazepoint and */ /* fixation (x, y, and radius) */ /* DATA ON NEW FIXATION */ SLONG new_fix_start_count; /* call count that the new fixation starts */ SLONG new_fix_end_count; /* call count that the new fixation ends */ SINT n_new_fix_samples; /* number of samples in the fixation */ SFLOAT x_new_fix_sum; /* summations for the FIR filter */ SFLOAT y_new_fix_sum; /* calculations of the eye motion */ SFLOAT x_new_fix; /* average coordinate of the eye fixation */ SFLOAT y_new_fix; /* point (user selected units) */ SFLOAT new_dr; /* difference between gazepoint and */ /* fixation (x, y, and radius) */ /* RING BUFFERS STORING PAST VALUES: */ SFLOAT x_gaze_ring[RING_SIZE]; SFLOAT y_gaze_ring[RING_SIZE]; SBYTE gaze_found_ring[RING_SIZE]; SINT eye_motion_state[RING_SIZE]; /* state of the eye motion: */ /* MOVING */ /* FIXATING */ /* FIXATION_COMPLETED */ SFLOAT x_fix_ring[RING_SIZE]; SFLOAT y_fix_ring[RING_SIZE]; SFLOAT gaze_deviation_ring[RING_SIZE]; SINT sac_duration_ring[RING_SIZE]; SINT fix_duration_ring[RING_SIZE]; SINT iRingIndex; /* ring index of the present gaze sample */ SINT iRingIndexDelay; /* ring index of the gaze sample taken */ /* minimum_fix_samples ago */ /****************************************************************************/ void InitFixation(int minimum_fix_samples) /* minimum number of gaze samples */ /* that can be considered a */ /* fixation */ /* Note: if the input value is */ /* is less than 3, the function */ /* sets it to 3 */ /* This function clears any previous, present and new fixations, and it */ /* initializes DetectFixation()'s internal ring buffers of prior */ /* gazepoint data. InitFixation() should be called prior to a sequence */ /* of calls to DetectFixation(). */ { /* Initialize the internal ring buffer. */ for (iRingIndex = 0; iRingIndex < RING_SIZE; iRingIndex++) { x_gaze_ring[iRingIndex] = 0.0F; y_gaze_ring[iRingIndex] = 0.0F; gaze_found_ring[iRingIndex] = FALSE; eye_motion_state[iRingIndex] = MOVING; x_fix_ring[iRingIndex] = 0.0F; y_fix_ring[iRingIndex] = 0.0F; gaze_deviation_ring[iRingIndex] = -0.1F; sac_duration_ring[iRingIndex] = 0; fix_duration_ring[iRingIndex] = 0; } iRingIndex = 0; iRingIndexDelay = RING_SIZE - minimum_fix_samples; /* Set the call count to zero, and initialize the previous fixation end */ /* count so the first saccade duration is a legitimate count. */ lCallCount = 0; lPrevFixEndCount = 0; /* Reset the present fixation data. */ ResetPresFixation(); /* Reset the new fixation data. */ ResetNewFixation(); /* Initialize the number of successive samples with no eye found. */ iNNoEyeFound = 0; } /****************************************************************************/ int DetectFixation( /* INPUT PARAMETERS: */ BYTE gazepoint_found, /* flag indicating whether or not */ /* the image processing algo */ /* detected the eye and computed */ /* a valid gazepoint (TRUE/FALSE) */ float x_gaze, /* present gazepoint */ float y_gaze, /* (user specified units) */ float gaze_deviation_threshold, /* distance that a gazepoint may */ /* vary from the average fixation */ /* point and still be considered */ /* part of the fixation */ /* (user specified units) */ int minimum_fix_samples, /* minimum number of gaze samples */ /* that can be considered a */ /* fixation */ /* Note: if the input value is */ /* is less than 3, the function */ /* sets it to 3 */ /* OUTPUT PARAMETERS: */ /* Delayed Gazepoint data with */ /* fixation annotations: */ BYTE *ptr_gazepoint_found_delayed, /* sample gazepoint-found flag, */ /* min_fix_samples ago */ float *ptr_x_gaze_delayed, /* sample gazepoint coordinates, */ float *ptr_y_gaze_delayed, /* min_fix_samples ago */ float *ptr_gaze_deviation_delayed, /* deviation of the gaze from the */ /* present fixation, */ /* min_fix_samples ago */ /* Fixation data - delayed: */ float *ptr_x_fix_delayed, /* fixation point as estimated */ float *ptr_y_fix_delayed, /* min_fix_samples ago */ int *ptr_saccade_duration_delayed, /* duration of the saccade */ /* preceeding the preset fixation */ /* (samples) */ int *ptr_fix_duration_delayed)/* duration of the present fixation */ /* (samples) */ /* RETURN VALUES - Eye Motion State: */ /* */ /* MOVING 0 The eye was in motion min_fix_samples ago. */ /* FIXATING 1 The eye was fixating min_fix_samples ago. */ /* FIXATION_COMPLETED 2 A completed fixation has just been detected; */ /* the fixation ended min_fix_samples ago. */ /* */ /* Include FIXFUNC.H for function prototype and above constant definitions. */ /* */ /* SUMMARY */ /* */ /* This function converts a series of uniformly-sampled (raw) gaze */ /* points into a series of variable-duration saccades and fixations. */ /* Fixation analysis may be performed in real time or after the fact. To */ /* allow eye fixation analysis during real-time eyegaze data collection, */ /* the function is designed to be called once per sample. When the eye */ /* is in motion, ie during saccades, the function returns 0 (MOVING). */ /* When the eye is still, ie during fixations, the function returns 1 */ /* (FIXATING). Upon the detected completion of a fixation, the function */ /* returns 2 (FIXATION_COMPLETED) and produces: */ /* a) the time duration of the saccade between the last and present */ /* eye fixation (eyegaze samples) */ /* b) the time duration of the present, just completed fixation */ /* (eyegaze samples) */ /* c) the average x and y coordinates of the eye fixation */ /* (in user defined units of x_gaze and y_gaze) */ /* Note: Although this function is intended to work in "real time", there */ /* is a delay of minimum_fix_samples in the filter which detects the */ /* motion/fixation condition of the eye. */ /* */ /* PRINCIPLE OF OPERATION */ /* */ /* This function detects fixations by looking for sequences of gaze- */ /* point measurements that remain relatively constant. If a new gazepoint */ /* lies within a circular region around the running average of an on-going */ /* fixation, the fixation is extended to include the new gazepoint. */ /* (The radius of the acceptance circle is user specified by setting the */ /* value of the function argument gaze_deviation_threshold.) */ /* To accommodate noisy eyegaze measurements, a gazepoint that exceeds */ /* the deviation threshold is included in an on-going fixation if the */ /* subsequent gazepoint returns to a position within the threshold. */ /* If a gazepoint is not found, during a blink for example, a fixation */ /* is extended if a) the next legitimate gazepoint measurement falls within */ /* the acceptance circle, and b) there are less than minimum_fix_samples */ /* of successive missed gazepoints. Otherwise, the previous fixation */ /* is considered to end at the last good gazepoint measurement. */ /* */ /* UNITS OF MEASURE */ /* */ /* The gaze position/direction may be expressed in any units (e.g. */ /* millimeters, pixels, or radians), but the filter threshold must be */ /* expressed in the same units. */ /* */ /* INITIALIZING THE FUNCTION */ /* */ /* Prior to analyzing a sequence of gazepoint data, the InitFixation */ /* function should be called to clear any previous, present and new */ /* fixations and to initialize the ring buffers of prior gazepoint data. */ /* */ /* PROGRAM NOTES */ /* */ /* For purposes of describing an ongoing sequence of fixations, fixations */ /* in this program are referred to as "previous", "present", and "new". */ /* The present fixation is the one that is going on right now, or, if a */ /* new fixation has just started, the present fixation is the one that */ /* just finished. The previous fixation is the one immediatly preceeding */ /* the present one, and a new fixation is the one immediately following */ /* the present one. Once the present fixation is declared to be completed, */ /* the present fixation becomes the previous one, the new fixation becomes */ /* the present one, and there is not yet a new fixation. */ /*--------------------------------------------------------------------------*/ { /* Make sure the minimum fix time is at least 3 samples. */ if (minimum_fix_samples < 3) minimum_fix_samples = 3; /* Make sure the ring size is large enough to handle the delay. */ if (minimum_fix_samples >= RING_SIZE) { //ARF comment out this, don't know what it is //lct_settextmode(); printf("minimum_fix_samples %i >= RING_SIZE %i\n", minimum_fix_samples, RING_SIZE); printf("Press any key to terminate..."); getch(); exit(99); } /* Increment the call count, the ring index, and the delayed ring index. */ lCallCount++; iRingIndex++; if (iRingIndex >= RING_SIZE) iRingIndex = 0; iRingIndexDelay = iRingIndex - minimum_fix_samples; if (iRingIndexDelay < 0) iRingIndexDelay += RING_SIZE; /* Update the storage rings. */ x_gaze_ring[iRingIndex] = x_gaze; y_gaze_ring[iRingIndex] = y_gaze; gaze_found_ring[iRingIndex] = gazepoint_found; /* Initially assume the eye is moving. */ /* Note: These values are updated during the processing of this and */ /* subsequent gazepoints. */ eye_motion_state[iRingIndex] = MOVING; x_fix_ring[iRingIndex] = -0.0F; y_fix_ring[iRingIndex] = -0.0F; gaze_deviation_ring[iRingIndex] = -0.1F; sac_duration_ring[iRingIndex] = 0; fix_duration_ring[iRingIndex] = 0; /*- - - - - - - - - - - - - Process Tracked Eye - - - - - - - - - - - - - -*/ /* A1 If the eye's gazepoint was successfully measured this sample, */ if (gazepoint_found == TRUE) { /* The number of successive no-tracks is zero. */ iNNoEyeFound = 0; /* B1 If there is a present fixation, */ if (iNPresFixSamples > 0) { /* Compute the deviation of the gazepoint from the present fixation. */ CalcGazeDeviationFromPresFix(x_gaze, y_gaze); /* C1 If the gazepoint is within the present fixation region, */ if (fPresDr <= gaze_deviation_threshold) { /* Restore any previous gazepoints that were temporarily left */ /* out of the fixation. */ RestoreOutPoints(); /* Update the present fixation hypothesis, and check if there */ /* are enough samples to declare that the eye is fixating. */ UpdatePresFixation(x_gaze, y_gaze, minimum_fix_samples); } /* C2 Otherwise, if the point is outside the present fixation region, */ else /* if (fPresDr > gaze_deviation_threshold) */ { /* Increment the number of gazepoint samples outside the */ /* present fix. */ iNPresOut++; /* D1 If the present fixation is finished, i.e. if there have */ /* been minimum_fix_samples since the gazepoint last matched */ /* the present fixation, and the present fixation is long */ /* enough to count as a real fixation, */ if (((int)(lCallCount - lPresFixEndCount) >= minimum_fix_samples) && (iNPresFixSamples >= minimum_fix_samples)) { /* Declare the present fixation to be completed, move the */ /* present fixation to the prior, move the new fixation to */ /* the present, and check if the new (now present) fixation */ /* has enough points for the eye to be declared to be fixating. */ DeclareCompletedFixation(minimum_fix_samples); /* Compute the deviation of the gazepoint from the now */ /* present fixation. */ CalcGazeDeviationFromPresFix(x_gaze, y_gaze); /* E1 If the gazepoint is within the now present fixation region, */ if (fPresDr <= gaze_deviation_threshold) { /* Update the present fixation data, and check if there */ /* are enough samples to declare that the eye is fixating. */ UpdatePresFixation(x_gaze, y_gaze, minimum_fix_samples); } /* E2 Otherwise, if the gazepoint is outside the now present */ /* fixation, */ else /* if (fPresDr > gaze_deviation_threshold) */ { /* Start a new fixation at the gazepoint. */ StartNewFixAtGazepoint(x_gaze, y_gaze); } } /* D2 Otherwise, if the present fixation is not finished, */ else { /* F1 If there is a new fixation hypothesis, */ if (n_new_fix_samples > 0) { /* Compute the deviation of the gazepoint from the new */ /* fixation. */ CalcGazeDeviationFromNewFix(x_gaze, y_gaze); /* G1 If the new point falls within the new fix, */ if (new_dr <= gaze_deviation_threshold) { /* Update the new fixation hypothesis. */ UpdateNewFixation(x_gaze, y_gaze); /* H. If there are now enough points in the new fix */ /* to declare it a real fix, */ if (n_new_fix_samples == minimum_fix_samples) { /* Drop the present fixation data, move the new */ /* new fixation into the present fixation, and see */ /* if the new (now present) fixation has enough */ /* points to declare the eye to be fixating. */ MoveNewFixToPresFix(minimum_fix_samples); } } /* G2 Otherwise, if the point is outside the new fix, */ else /* if (new_dr <= gaze_deviation_threshold) */ { /* Start the new fixation at the new gazepoint. */ StartNewFixAtGazepoint(x_gaze, y_gaze); } } /* F2 Otherwise, If there is not a new fix, */ else /* if (n_new_fix_counts == 0) */ { /* Start the new fixation at the gazepoint. */ StartNewFixAtGazepoint(x_gaze, y_gaze); } } } } /* B2 Otherwise, if there is not a present fixation, */ else /* if (iNPresFixSamples = 0) */ { /* Start the present fixation at the gazepoint and reset the */ /* new fixation. */ StartPresFixAtGazepoint(x_gaze, y_gaze); } } /*- - - - - - - - - - - - - Process Untracked Eye - - - - - - - - - - - - -*/ /* A2 Otherwise, if the eye's gazepoint was not successfully measured */ /* this sample, */ else /* if (gazepoint_found == FALSE) */ { /* Increment the number of successive samples with no eye found. */ iNNoEyeFound++; /*I If it has been min-fix-samples since the last sample in the */ /* present fixation, */ if ((int)(lCallCount - lPresFixEndCount) >= minimum_fix_samples) { /* J If there had been a fixation prior to loosing track of the eye, */ if (iNPresFixSamples >= minimum_fix_samples) { /* Declare the present fixation to be completed, move the */ /* present fixation to the prior, move the new fixation to */ /* the present, and check if the new (now present) fixation */ /* has enough points for the eye to be declared to be fixating. */ DeclareCompletedFixation(minimum_fix_samples); } /* Reset the present fixation data. */ ResetPresFixation(); } } /*---------------------------- Pass Data Back ------------------------------*/ /* Pass the delayed gazepoint data, with the relevant saccade/fixation */ /* data, back to the calling function. */ *ptr_x_gaze_delayed = x_gaze_ring[iRingIndexDelay]; *ptr_y_gaze_delayed = y_gaze_ring[iRingIndexDelay]; *ptr_gazepoint_found_delayed = gaze_found_ring[iRingIndexDelay]; *ptr_x_fix_delayed = x_fix_ring[iRingIndexDelay]; *ptr_y_fix_delayed = y_fix_ring[iRingIndexDelay]; *ptr_gaze_deviation_delayed = gaze_deviation_ring[iRingIndexDelay]; *ptr_saccade_duration_delayed= sac_duration_ring[iRingIndexDelay]; *ptr_fix_duration_delayed = fix_duration_ring[iRingIndexDelay]; /* Return the eye motion/fixation state for the delayed point. */ return(eye_motion_state[iRingIndexDelay]); } /****************************************************************************/ SVOID ResetPresFixation(void) /* This function resets the present fixation, i.e. declares it nonexistent. */ { lPresFixStartCount = 0; lPresFixEndCount = 0; iNPresFixSamples = 0; fXPresFixSum = 0.0F; fYPresFixSum = 0.0F; fXPresFix = 0.0F; fYPresFix = 0.0F; iNPresOut = 0; } /****************************************************************************/ SVOID ResetNewFixation(void) /* This function resets the new fixation, i.e. declares it nonexistent. */ { new_fix_start_count = 0; new_fix_end_count = 0; n_new_fix_samples = 0; x_new_fix_sum = 0.0F; y_new_fix_sum = 0.0F; x_new_fix = 0.0F; y_new_fix = 0.0F; } /****************************************************************************/ SVOID StartPresFixAtGazepoint(float x_gaze, float y_gaze) /* This function starts the present fixation at the argument gazepoint */ /* and makes sure there is no new fixation hypothesis. */ { /* Start the present fixation at the argument gazepoint. */ iNPresFixSamples = 1; fXPresFixSum = x_gaze; fYPresFixSum = y_gaze; fXPresFix = x_gaze; fYPresFix = y_gaze; lPresFixStartCount = lCallCount; lPresFixEndCount = lCallCount; iNPresOut = 0; /* Make sure there is no new fixation. */ ResetNewFixation(); } /****************************************************************************/ SVOID StartNewFixAtGazepoint(float x_gaze, float y_gaze) /* This function starts the new fixation at the argument gazepoint. */ { n_new_fix_samples = 1; x_new_fix_sum = x_gaze; y_new_fix_sum = y_gaze; x_new_fix = x_gaze; y_new_fix = y_gaze; new_fix_start_count = lCallCount; new_fix_end_count = lCallCount; } /****************************************************************************/ SVOID UpdatePresFixation(float x_gaze, float y_gaze, int minimum_fix_samples) /* This function updates the present fixation with the argument gazepoint, */ /* checks if there are enough samples to declare that the eye is now */ /* fixating, and makes sure there is no hypothesis for a new fixation. */ { /* Update the present fixation with the argument gazepoint. */ iNPresFixSamples++; fXPresFixSum += x_gaze; fYPresFixSum += y_gaze; fXPresFix = fXPresFixSum / iNPresFixSamples; fYPresFix = fYPresFixSum / iNPresFixSamples; lPresFixEndCount = lCallCount; iNPresOut = 0; /* Check if there are enough samples in the present fixation hypothesis */ /* to declare that the eye is fixating. */ CheckIfFixating(minimum_fix_samples); /* There is no hypothesis for a new fixation. */ ResetNewFixation(); } /****************************************************************************/ SVOID UpdateNewFixation(float x_gaze, float y_gaze) /* This function updates the new fixation with the argument gazepoint. */ { /* Update the new fixation with the argument gazepoint. */ n_new_fix_samples++; x_new_fix_sum += x_gaze; y_new_fix_sum += y_gaze; x_new_fix = x_new_fix_sum / n_new_fix_samples; y_new_fix = y_new_fix_sum / n_new_fix_samples; new_fix_end_count = lCallCount; } /****************************************************************************/ SVOID CalcGazeDeviationFromPresFix(float x_gaze, float y_gaze) /* This function calculates the deviation of the gazepoint from the */ /* present fixation location. */ { float dx, dy; /* horizontal and vertical deviations */ dx = x_gaze - fXPresFix; dy = y_gaze - fYPresFix; fPresDr = (float)sqrt(dx * dx + dy * dy); /* Put the deviation in the ring buffer for future reference. */ gaze_deviation_ring[iRingIndex] = fPresDr; } /****************************************************************************/ SVOID CalcGazeDeviationFromNewFix(float x_gaze, float y_gaze) /* This function calculate the deviation of the gazepoint from the new */ /* fixation location. */ { float dx, dy; /* horizontal and vertical deviations */ dx = x_gaze - x_new_fix; dy = y_gaze - y_new_fix; new_dr = (float)sqrt(dx * dx + dy * dy); } /****************************************************************************/ SVOID CheckIfFixating(int minimum_fix_samples) /* This function checks to see whether there are enough samples in the */ /* presently hypothesized fixation to declare that the eye is fixating */ /* yet, and if there is a true fixation going on, it updates the ring */ /* buffers to reflect the fixation. */ { int i, ii; /* dummy ring indices */ /* If there are enough samples for a fixation, */ if (iNPresFixSamples >= minimum_fix_samples) { /* Declare the eye to be fixating. Go back through the last */ /* minimum_fix_samples entries of the ring buffer making sure that all */ /* samples from the present fixation are marked as fixating, and set */ /* the entries with the newest estimate of the fixation location. */ for (i = 0; i < minimum_fix_samples; i++) { ii = iRingIndex - i; if (ii < 0) ii += RING_SIZE; eye_motion_state[ii] = FIXATING; x_fix_ring[ii] = fXPresFix; y_fix_ring[ii] = fYPresFix; sac_duration_ring[ii] = (int)(lPresFixStartCount - lPrevFixEndCount - 1); fix_duration_ring[ii] = (int)(lPresFixEndCount - lPresFixStartCount + 1 - i); } } } /****************************************************************************/ SVOID MoveNewFixToPresFix(int minimum_fix_samples) /* This function copies the new fixation data into the present fixation, */ /* and resets the new fixation. */ { /* Move the new fixation to the present fixation. */ iNPresFixSamples = n_new_fix_samples; fXPresFixSum = x_new_fix_sum; fYPresFixSum = y_new_fix_sum; fXPresFix = x_new_fix; fYPresFix = y_new_fix; lPresFixStartCount = new_fix_start_count; lPresFixEndCount = new_fix_end_count; iNPresOut = 0; /* Reset the new fixation. */ ResetNewFixation(); /* Check if there are enough samples in the new (now present) fixation to */ /* declare that the eye is fixating. */ CheckIfFixating(minimum_fix_samples); } /****************************************************************************/ SVOID DeclareCompletedFixation(int minimum_fix_samples) /* This function: */ /* a) declares the present fixation to be completed, */ /* b) moves the present fixation to the prior fixation, */ /* c) moves the new fixation, if any, to the present fixation, and */ { /* Declare the present fixation to be completed. */ eye_motion_state[iRingIndexDelay] = FIXATION_COMPLETED; /* Move the present fixation to the previous fixation. This saves the */ /* end time of the present fixation for later computation of the saccade */ /* period between this and the next fixation. */ lPrevFixEndCount = lPresFixEndCount; /* Move the new fixation data, if any, to the present fixation, reset */ /* the new fixation, and check if there are enough samples in the new */ /* (now present) fixation to declare that the eye is fixating. */ MoveNewFixToPresFix(minimum_fix_samples); } /****************************************************************************/ SVOID RestoreOutPoints(void) /* This function restores any previous gazepoints that were left out of */ /* the fixation and are now known to be part of the present fixation. */ { int i, ii; /* dummy ring indices */ /* If there were some previous points that temporarily went out of the */ /* fixation region, */ if (iNPresOut > 0) { /* Undo the hypothesis that they were outside the fixation and declare */ /* them now to be part of the fix. */ for (i = 1; i <= iNPresOut; i++) { ii = iRingIndex - i; if (ii < 0) ii += RING_SIZE; if (gaze_found_ring[ii] == TRUE) { iNPresFixSamples++; fXPresFixSum += x_gaze_ring[ii]; fYPresFixSum += y_gaze_ring[ii]; eye_motion_state[ii] = FIXATING; } } /* Set the number of "out" points to be zero. */ iNPresOut = 0; } } /****************************************************************************/