Skip to content

Commit 88e2f03

Browse files
committed
Fix precision bugs in ConvexHull and SaccadeVelocity
- Remove (long) casts in ConvexHull that truncated floating-point values - Return NaN instead of 0.0 when all velocities exceed threshold - Fix writeCSV.csv line endings (CRLF to LF) for OpenCSV 5.x compatibility
1 parent 0d98f08 commit 88e2f03

File tree

3 files changed

+25
-24
lines changed

3 files changed

+25
-24
lines changed

src/main/java/com/github/thed2lab/analysis/ConvexHull.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,8 @@ public int compare(Point2D.Double a, Point2D.Double b) {
180180
return 0;
181181
}
182182

183-
// use longs to guard against int-underflow
184-
double thetaA = Math.atan2((long)a.y - lowest.y, (long)a.x - lowest.x);
185-
double thetaB = Math.atan2((long)b.y - lowest.y, (long)b.x - lowest.x);
183+
double thetaA = Math.atan2(a.y - lowest.y, a.x - lowest.x);
184+
double thetaB = Math.atan2(b.y - lowest.y, b.x - lowest.x);
186185

187186
if(thetaA < thetaB) {
188187
return -1;
@@ -193,11 +192,10 @@ else if(thetaA > thetaB) {
193192
else {
194193
// collinear with the 'lowest' point, let the point closest to it come first
195194

196-
// use longs to guard against int-over/underflow
197-
double distanceA = Math.sqrt((((long)lowest.x - a.x) * ((long)lowest.x - a.x)) +
198-
(((long)lowest.y - a.y) * ((long)lowest.y - a.y)));
199-
double distanceB = Math.sqrt((((long)lowest.x - b.x) * ((long)lowest.x - b.x)) +
200-
(((long)lowest.y - b.y) * ((long)lowest.y - b.y)));
195+
double distanceA = Math.sqrt(((lowest.x - a.x) * (lowest.x - a.x)) +
196+
((lowest.y - a.y) * (lowest.y - a.y)));
197+
double distanceB = Math.sqrt(((lowest.x - b.x) * (lowest.x - b.x)) +
198+
((lowest.y - b.y) * (lowest.y - b.y)));
201199

202200
if(distanceA < distanceB) {
203201
return -1;
@@ -235,9 +233,8 @@ else if(thetaA > thetaB) {
235233
*/
236234
protected static Turn getTurn(Point2D.Double a, Point2D.Double b, Point2D.Double c) {
237235

238-
// use longs to guard against int-over/underflow
239-
long crossProduct = (long) ((((long)b.x - a.x) * ((long)c.y - a.y)) -
240-
(((long)b.y - a.y) * ((long)c.x - a.x)));
236+
double crossProduct = ((b.x - a.x) * (c.y - a.y)) -
237+
((b.y - a.y) * (c.x - a.x));
241238

242239
if(crossProduct > 0) {
243240
return Turn.COUNTER_CLOCKWISE;

src/main/java/com/github/thed2lab/analysis/SaccadeVelocity.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,14 @@ static double getPeakVelocity(List<Double[]> saccadePoints) {
159159
if (saccadePoints.size() == 0 || saccadePoints.size() == 1) {
160160
return Double.NaN;
161161
}
162-
162+
163163
final double PIXELS_TO_CM = 0.0264583333; // Convert from pixels to cms
164164
final double VELOCITY_THRESHOLD = 700; // Maximum possible saccadic velocity
165165
final double PARTICIPANT_DISTANCE = 65; // assume an average distance of 65cm from the participant to the screen
166166
final double RADIAN_TO_DEGREES = 180/Math.PI;
167167
double peakVelocity = 0;
168-
168+
boolean foundValidVelocity = false;
169+
169170
for (int i = 1; i < saccadePoints.size(); i++) {
170171
Double[] currPoint = saccadePoints.get(i);
171172
Double[] prevPoint = saccadePoints.get(i - 1);
@@ -174,19 +175,22 @@ static double getPeakVelocity(List<Double[]> saccadePoints) {
174175
double y1 = currPoint[1];
175176
double x2 = prevPoint[0];
176177
double y2 = prevPoint[1];
177-
178+
178179
double dx = Math.sqrt(Math.pow(Math.abs(x1 - x2), 2) + Math.pow(Math.abs(y1 - y2), 2)) * PIXELS_TO_CM;
179180
double dt = Math.abs(currPoint[2] - prevPoint[2]);
180181
double amplitude = RADIAN_TO_DEGREES * Math.atan(dx/PARTICIPANT_DISTANCE);
181-
182+
182183
double velocity = amplitude/dt;
183-
184-
if (velocity > peakVelocity && velocity <= VELOCITY_THRESHOLD) {
185-
peakVelocity = velocity;
184+
185+
if (velocity <= VELOCITY_THRESHOLD) {
186+
if (velocity > peakVelocity) {
187+
peakVelocity = velocity;
188+
}
189+
foundValidVelocity = true;
186190
}
187191
}
188-
189-
return peakVelocity;
192+
193+
return foundValidVelocity ? peakVelocity : Double.NaN;
190194
}
191195

192196
/**

src/test/java/com/github/thed2lab/analysis/SaccadeVelocityTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ public void testGetPeakVelocity_onePoint_returnNaN() {
3131
}
3232

3333
@Test
34-
public void testGetPeakVelocity_aboveThreshold_return0() {
34+
public void testGetPeakVelocity_aboveThreshold_returnNaN() {
3535
List<Double[]> saccadePoints = List.of(
3636
new Double[]{0., 0., 0.},
3737
new Double[]{0., 10., 0.0001}
3838
);
39-
// 2332.21914 > 700 => return 0
40-
boolean isZero = SaccadeVelocity.getPeakVelocity(saccadePoints) == 0;
41-
assertTrue(isZero);
39+
// 2332.21914 > 700 => return NaN (excluded from statistics)
40+
boolean isNaN = Double.isNaN(SaccadeVelocity.getPeakVelocity(saccadePoints));
41+
assertTrue(isNaN);
4242
}
4343

4444
@Test

0 commit comments

Comments
 (0)