-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCursorController.py
More file actions
106 lines (75 loc) · 3.76 KB
/
Copy pathCursorController.py
File metadata and controls
106 lines (75 loc) · 3.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# -*- coding: utf-8 -*-
"""
Created on Thu Sep 25 17:43:39 2025
@author: Luca DalCanto, Purdue University
on behalf of the Eyecue team of ES@P
The purpose of this class is to take the 9-dot calibration information,
position of the eye at any given time, and head angles at any given
time (from gyro) to calculate the correct position of the mouse on the
screen.
How to use:
- import this file ("import CursorController") or something idk
- create an instance of this class after calibration:
"controller = CursorController(...parameters here...)"
parameters of instantitaion:
- leftAngle: nine-dot left calibration horizontal angle in degrees
- rightAngle: nine-dot right calibration horizontal angle in degrees
- topAngle: nine-dot top calibration vertical angle in degrees
- bottomAngle: nine-dot bottom calibration vertical angle in degrees
- gyroH: initial horizontal angle of gyro
- gyroV: intial vertial angle of gyro
- frameRate: intended number of updates per second
- every frame, call the update function:
"controller.update_target(...parameters here...)"
parameters of update_target:
- angleH: current horizontal eye angle in degrees
- angleV: current vertical eye angle in degrees
- gyroH: current horizontal gyro angle
- gyroV: current vertical gyro angle
Areas for improvement:
- calculates correct mapping with screen distance and screen size using the
horizontal angles only -> mapping for vertical angles may be imperfect.
-
"""
import pyautogui
import math
import numpy as np
class CursorController:
# Constructor method
def __init__(self, leftAngle, rightAngle, topAngle, bottomAngle, gyroH, gyroV, frameRate):
self.frameRate = frameRate
self.screenWidth, self.screenHeight = pyautogui.size()
leftAngle *= math.pi / 180
rightAngle *= math.pi / 180
topAngle *= math.pi/180
bottomAngle *= math.pi/180
self.screenDistance = self.screenWidth / (2 * math.tan(abs(leftAngle - rightAngle)/2))
self.gyroCenter = [gyroH, gyroV]
self.eyeCenter = [(leftAngle + rightAngle) / 2, (topAngle + bottomAngle) / 2]
print(self.eyeCenter[1])
# takes angle of vision (angle H - horizontal, angleV - vertical) in degrees
# takes angle of head (gyroH, gyroV) in degrees
def update_target(self, angleH, angleV, gyroH, gyroV):
# convert horizontal and vertical rotations to radians
angleH *= math.pi / 180
angleV *= -math.pi / 180
# account for initial calibration
angleH -= self.eyeCenter[0]
angleV += self.eyeCenter[1]
# account for head rotation
angleH += (gyroH - self.gyroCenter[0]) * math.pi / 180
angleV += (gyroV - self.gyroCenter[1]) * math.pi / 180
# gaze vector
unitVector = np.array([
np.sin(angleH) * np.cos(angleV), # x-component
np.cos(angleH) * np.cos(angleV), # y-component
np.sin(angleV) # z-component
])
# scale unit vector according to distance from screen
scaleFactor = self.screenDistance / unitVector[1] # cos theta sub
# calculate position in coordinates
x = (unitVector[0] * scaleFactor) + (self.screenWidth / 2)
y = (unitVector[2] * scaleFactor) + (self.screenHeight/2)
pyautogui.moveTo(x, y, duration=1.0/self.frameRate)
# control = CursorController(0, 56.8, 16.3, -16.3, 0, 0)
# control.update_target(0, -17.3, 0, 0)