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); }
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))); }
//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; } }
/* * 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 ))); }