Example #1
0
        public bool NearLastIbeamPoint(Point p)
        {
            if (lastIbeamPoint == null)
            {
                return(false);
            }

            int nearbyMm     = 7; //mm
            int nearbyPixels = ScreenPixelHelper.ConvertMmToPixels(nearbyMm);

            return(PointHelper.GetPointDistance(p, lastIbeamPoint) <= nearbyPixels);
        }
Example #2
0
        public static int distanceToBox(Point gp, Box b)
        {
            int xclose = gp.X;

            xclose = (gp.X < b.left) ? b.left : xclose;
            xclose = (gp.X > b.right) ? b.right : xclose;

            int yclose = gp.Y;

            yclose = (gp.Y < b.top) ? b.top : yclose;
            yclose = (gp.Y > b.bottom) ? b.bottom : yclose;

            return(PointHelper.GetPointDistance(gp, new Point(xclose, yclose)));
        }
Example #3
0
        //TODO: refactor these methods to same one and use enums

        /*
         *  Returns true if:
         *      All gazepoints for the past minMs duration are within radiusMm of the first gaze point in the observed set.
         *
         *  This function should be called when you want to limit the fixation region to a certain millimeters within the
         *  gazepoint at minMs milliseconds ago.
         */
        public static bool IsStationaryFixation(int minMs, int radiusMm)
        {
            if (warpPointer == null)
            {
                return(false);
            }

            List <Point> rawGazePoints = warpPointer.GetGazeHistory();

            if (rawGazePoints == null || rawGazePoints.Count == 0)
            {
                return(false);
            }

            int sampleRate = warpPointer.GetSampleRate();

            //total number of samples that should be meet the fixation width criteria
            int lookbackSampleCount = (int)Math.Max(1, sampleRate * minMs / 1000);

            if (lookbackSampleCount > rawGazePoints.Count)
            {
                return(false);
            }

            //Find the average of all the lookbackSamples ...
            int   startIndex = Math.Max(0, rawGazePoints.Count - lookbackSampleCount);
            Point runningSum = new Point(0, 0);
            Point runningAvg = new Point(0, 0);

            for (int i = startIndex; i < rawGazePoints.Count; i++)
            {
                runningSum.X += rawGazePoints[i].X;
                runningSum.Y += rawGazePoints[i].Y;
            }
            runningAvg.X = runningSum.X / lookbackSampleCount;
            runningAvg.Y = runningSum.Y / lookbackSampleCount;

            //Check if distances between all points are within radius
            for (int i = startIndex + 1; i < rawGazePoints.Count; i++)
            {
                if (PointHelper.GetPointDistance(runningAvg, rawGazePoints[i]) > ScreenPixelHelper.ConvertMmToPixels(radiusMm))
                {
                    return(false); //this point is too far from the first point
                }
            }
            return(true);
        }
        //This function gets called pretty much every precision gaze mouse form refresh
        public void ShowCursorOverlay(Point p)
        {
            if (activeGazeButton == GAZE_BUTTON_TYPE.DOWN || activeGazeButton == GAZE_BUTTON_TYPE.UP)
            {
                if (cursorOverlayForm == null || cursorOverlayForm.IsDisposed)
                {
                    Logger.WriteMsg("New Cursor Overlay Form being created.");
                    cursorOverlayForm = new CursorOverlayForm();
                }

                //Only update the overlay location if there is a change by more than 5 mm
                if (PointHelper.GetPointDistance(p, cursorOverlayForm.currentPoint) > ScreenPixelHelper.ConvertMmToPixels(5))
                {
                    cursorOverlayForm.currentPoint = p;

                    if (activeGazeButton == GAZE_BUTTON_TYPE.DOWN)
                    {
                        cursorOverlayForm.UpdateOverlayType(CursorOverlayForm.OVERLAY_TYPE.DOWNARROW);
                    }
                    else if (activeGazeButton == GAZE_BUTTON_TYPE.UP)
                    {
                        cursorOverlayForm.UpdateOverlayType(CursorOverlayForm.OVERLAY_TYPE.UPARROW);
                    }

                    if (!cursorOverlayForm.Visible)
                    {
                        Logger.WriteMsg("Cursor OverlyForm about to be shown.");
                        cursorOverlayForm.Show();
                    }
                    else
                    {
                        cursorOverlayForm.Invalidate();
                    }
                }
            }
            else
            {
                HideCursorOverlayForm();
            }
        }
        private Point GetSideOffset(Point p, int a, int b)
        {
            if (a >= adjGridOffset.Length || b >= adjGridOffset.Length)
            {
                return(new Point(0, 0));
            }

            double da, db, dtotal, wa, wb, wtotal;

            da     = PointHelper.GetPointDistance(p, adjGrid[a]);
            db     = PointHelper.GetPointDistance(p, adjGrid[b]);
            dtotal = da + db;

            wa     = dtotal - da;
            wb     = dtotal - db;
            wtotal = wa + wb;


            //Apply weighted sum of a and b offsets
            return(new Point(Convert.ToInt16(adjGridOffset[a].X * (wa / wtotal) + adjGridOffset[b].X * (wb / wtotal)),
                             Convert.ToInt16(adjGridOffset[a].Y * (wa / wtotal) + adjGridOffset[b].Y * (wb / wtotal))));
        }
        //whenever this method is called, it paints from scratch and does not append to what's already on the screen
        //this method is typically called whenever RefreshScreen is called (60 fps)
        protected override void OnPaint(PaintEventArgs e)
        {
            //Draw overall target circle (which shrinks with duration)
            int       radius   = GetTargetCircleRadius(iteration);
            Point     calpoint = ca.adjGrid[currentCircle];
            Rectangle rec      = new Rectangle(calpoint.X - radius, calpoint.Y - radius, radius * 2, radius * 2);

            if (iteration == 0)
            {
                e.Graphics.DrawEllipse(Pens.White, rec);
            }
            else if (iteration < ConfigManager.calibrationIterationCountMax - 1)
            {
                e.Graphics.DrawEllipse(Pens.DarkCyan, rec);
                //also draw inner circles to guide
                radius = GetTargetCircleRadius(iteration + 1);
                rec    = new Rectangle(calpoint.X - radius, calpoint.Y - radius, radius * 2, radius * 2);
                e.Graphics.DrawEllipse(Pens.DarkMagenta, rec);
            }
            else
            {
                e.Graphics.FillEllipse(Brushes.DarkMagenta, rec);
                e.Graphics.DrawEllipse(Pens.DarkCyan, rec);
            }

            //Reset the iterations and calibrating this point if user has already been trying longer than expected
            if (iteration > ConfigManager.calibrationIterationCountMax + 1)
            {
                calstate  = CalState.WAITING_FOCUS;
                iteration = 0;
            }


            Point gp = controller.WarpPointer.GetNextPoint(controller.WarpPointer.GetGazePoint());

            //detect whether gaze point is close to the calibration circle (at most 7cm of screen away)
            if (PointHelper.GetPointDistance(gp, calpoint) < ScreenPixelHelper.ConvertMmToPixels(70))
            {
                //draw gray gaze point as long as it's not the final iteration(s)
                int   gazeRadius = ScreenPixelHelper.ConvertMmToPixels(1) / 2;
                Point p          = controller.WarpPointer.GetNextPoint(controller.WarpPointer.GetGazePoint());
                p.Offset(ca.adjGridOffset[currentCircle]);
                rec = new Rectangle(p.X - gazeRadius, p.Y - gazeRadius, gazeRadius * 2, gazeRadius * 2);

                if (iteration < ConfigManager.calibrationIterationCountMax)
                {
                    e.Graphics.FillEllipse(Brushes.Gray, rec);

                    if (iteration > 0)
                    {
                        //Get antipoint, which is basically located on other cide of currentCircle
                        int   xDiff = p.X - ca.adjGrid[currentCircle].X;
                        int   yDiff = p.Y - ca.adjGrid[currentCircle].Y;
                        Point ap    = new Point(p.X - xDiff * 2, p.Y - yDiff * 2);

                        rec = new Rectangle(ap.X - gazeRadius, ap.Y - gazeRadius, gazeRadius * 2, gazeRadius * 2);
                        e.Graphics.FillEllipse(Brushes.Gray, rec);
                    }
                }


                if (calstate == CalState.WAITING_FOCUS)
                {
                    StartTimer();
                    calstate = CalState.FOCUS_IN_PROGRESS;
                    return;
                }
                else if (calstate == CalState.FOCUS_IN_PROGRESS)
                {
                    if (DoneWaitingTime(150))
                    {
                        calstate = CalState.FOCUSED;
                    }
                    else
                    {
                        return;
                    }
                }
                else if (calstate == CalState.FOCUSED)
                {
                    //Done waiting the focus time and eyes are focussed
                    UpdateCalibrationAdjustmentWithCurrentGaze();

                    StartTimer();
                    sampleCollectionStartTime = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
                    calstate  = CalState.COLLECTING_SAMPLES;
                    iteration = 0;
                    return;
                }
                else if (calstate == CalState.COLLECTING_SAMPLES)
                {
                    //Done waiting time to start collecting samples

                    //We don't want the gaze to lag across the screen so we trick the user thinking it's centered already
                    if (iteration == 0 || iteration == 1)
                    {
                        UpdateCalibrationAdjustmentWithCurrentGaze();
                    }

                    if (DoneWaitingTime(ConfigManager.calibrationIterationTimeMs))
                    {
                        //Check if current gaze point is close to the center of the target circle
                        iteration++;
                        Point  wp                     = controller.WarpPointer.GetWarpPoint();
                        double pointDistance          = PointHelper.GetPointDistance(calpoint, wp);
                        int    pointDistanceThreshold = ScreenPixelHelper.ConvertMmToPixels(ConfigManager.calibrationCompletedThresholdMm);
                        UpdateCalibrationAdjustmentWithCurrentGaze();
                        if (iteration >= ConfigManager.calibrationIterationCountMax &&
                            pointDistance < pointDistanceThreshold)
                        {
                            //calibration for this point is successful
                            currentCircle++;
                            if (currentCircle == 9)
                            {
                                ca.writeCalibrationAdjustmentCsv();
                                currentCircle = 0;
                                Cursor.Show();
                                this.Close();
                            }
                        }
                        else
                        {
                            StartTimer();
                        }
                        this.Invalidate();// This forces the form to repaint
                    }
                }
            }
            else
            {
                iteration = 0;
                calstate  = CalState.WAITING_FOCUS;
            }
        }
Example #7
0
        /*
         *  Returns true if:
         *      The speed of eye movement does not exceed (radiusMM / ConfigManager.fixationMinDurationMs) millimeters/seconds
         *      measured over a total duration of minMs.
         *
         *  This function should be called when it's acceptable for the eyes to continue moving slowly and still count it as fixation.
         */
        public static bool IsMovingFixation(int minMs, int radiusMm)
        {
            if (warpPointer == null)
            {
                return(false);
            }

            List <Point> rawGazePoints = warpPointer.GetGazeHistory();

            if (rawGazePoints == null || rawGazePoints.Count == 0)
            {
                return(false);
            }

            int sampleRate = warpPointer.GetSampleRate();

            //total number of samples that should be meet the fixation width criteria
            int lookbackSampleCount = (int)Math.Max(1, sampleRate * minMs / 1000);

            if (lookbackSampleCount > rawGazePoints.Count)
            {
                return(false);
            }

            //moving window samples amount
            int windowSampleCount = Math.Max(sampleRate * ConfigManager.fixationMinDurationMs / 1000, 1);
            int stopIndex         = Math.Max(0, rawGazePoints.Count - windowSampleCount);

            //Get average of the first window
            //We start at the most recent point in history

            Point runningSum = new Point(0, 0);
            Point runningAvg = new Point(0, 0);

            for (int i = rawGazePoints.Count - 1; i >= stopIndex; i--)
            {
                runningSum.X += rawGazePoints[i].X;
                runningSum.Y += rawGazePoints[i].Y;
            }
            runningAvg.X = runningSum.X / windowSampleCount;
            runningAvg.Y = runningSum.Y / windowSampleCount;

            //Check that each point in the current window meets the fixation width criteria
            for (int i = rawGazePoints.Count - 1; i >= stopIndex; i--)
            {
                if (PointHelper.GetPointDistance(runningAvg, rawGazePoints[i]) > ScreenPixelHelper.ConvertMmToPixels(radiusMm))
                {
                    return(false); //this point is too far from the avg
                }
            }

            //slide window until we complete lookbackSampleCount many
            stopIndex = Math.Max(0, rawGazePoints.Count - lookbackSampleCount);
            int lowerIndex = rawGazePoints.Count - 1 - windowSampleCount;

            while (lowerIndex >= stopIndex)
            {
                int upperIndex = lowerIndex + windowSampleCount;
                //Remove one point from window
                runningSum.X -= rawGazePoints[upperIndex].X;
                runningSum.Y -= rawGazePoints[upperIndex].Y;

                //Add another point to the window
                runningSum.X += rawGazePoints[lowerIndex].X;
                runningSum.Y += rawGazePoints[lowerIndex].Y;

                //Recalculate avg
                runningAvg.X = runningSum.X / windowSampleCount;
                runningAvg.Y = runningSum.Y / windowSampleCount;

                //See if newest point is within the average
                if (PointHelper.GetPointDistance(runningAvg, rawGazePoints[lowerIndex]) > ScreenPixelHelper.ConvertMmToPixels(radiusMm))
                {
                    return(false); //this point is too far from the avg
                }

                lowerIndex--;
            }

            //Been through lookbackSampleCount and all of them according to the moving window are within fixation width requirement
            return(true);
        }
        //a and b are same horizontally, and     c and d are same horizontally
        private Point GetCentralOffset(Point p, int a, int b, int c, int d)
        {
            if (a >= adjGridOffset.Length || b >= adjGridOffset.Length || c >= adjGridOffset.Length || d >= adjGridOffset.Length)
            {
                return(new Point(0, 0));
            }

            double da, db, dc, dd, dtotal, wa = 0, wb = 0, wc = 0, wd = 0;

            da     = PointHelper.GetPointDistance(p, adjGrid[a]);
            db     = PointHelper.GetPointDistance(p, adjGrid[b]);
            dc     = PointHelper.GetPointDistance(p, adjGrid[c]);
            dd     = PointHelper.GetPointDistance(p, adjGrid[d]);
            dtotal = da + db + dc + dd;

            Point A = adjGrid[a];
            Point B = adjGrid[b];
            Point C = adjGrid[c];
            Point D = adjGrid[d];

            List <Point> corners = new List <Point>()
            {
                A, B, C, D
            };

            List <double> distances   = new List <double>();
            double        distanceSum = 0;

            //compute distances
            for (int i = 0; i < corners.Count; i++)
            {
                Point  corner   = corners[i];
                double distance = PointHelper.GetPointDistance(p, corner);
                distances.Add(distance);
                distanceSum += distance;
            }

            double fractionLeft = 1; //100%

            while (corners.Count > 0)
            {
                int currentMinIndex = IndexOfMinDistance(distances);

                //1 - (di / dt) * (n - 1)
                double contribution = 1 - (distances[currentMinIndex] / distanceSum) * (distances.Count - 1);

                if (corners[currentMinIndex] == A)
                {
                    wa = fractionLeft * contribution;
                }
                else if (corners[currentMinIndex] == B)
                {
                    wb = fractionLeft * contribution;
                }
                else if (corners[currentMinIndex] == C)
                {
                    wc = fractionLeft * contribution;
                }
                else if (corners[currentMinIndex] == D)
                {
                    wd = fractionLeft * contribution;
                }

                fractionLeft -= contribution * fractionLeft;
                distanceSum  -= distances[currentMinIndex];
                distances.RemoveAt(currentMinIndex);
                corners.RemoveAt(currentMinIndex);
            }

            //Apply weighted sum of offsets
            return(new Point(
                       Convert.ToInt16(adjGridOffset[a].X * wa
                                       + adjGridOffset[b].X * wb
                                       + adjGridOffset[c].X * wc
                                       + adjGridOffset[d].X * wd),
                       Convert.ToInt16(
                           adjGridOffset[a].Y * wa
                           + adjGridOffset[b].Y * wb
                           + adjGridOffset[c].Y * wc
                           + adjGridOffset[d].Y * wd
                           )));
        }