aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThéo de la Hogue2023-05-09 17:50:51 +0200
committerThéo de la Hogue2023-05-09 17:50:51 +0200
commitb4d8bed97612dc57cd9f4df97133bad193f43e7b (patch)
tree373126285d90674739e7f2a49a4af373d090cacc
parent7b44d6cebe8b7428c129d81c899070bc35866b5e (diff)
downloadargaze-b4d8bed97612dc57cd9f4df97133bad193f43e7b.zip
argaze-b4d8bed97612dc57cd9f4df97133bad193f43e7b.tar.gz
argaze-b4d8bed97612dc57cd9f4df97133bad193f43e7b.tar.bz2
argaze-b4d8bed97612dc57cd9f4df97133bad193f43e7b.tar.xz
Defining VisualScan interface.
-rw-r--r--src/argaze/GazeFeatures.py103
1 files changed, 102 insertions, 1 deletions
diff --git a/src/argaze/GazeFeatures.py b/src/argaze/GazeFeatures.py
index a65f55b..bfe19fb 100644
--- a/src/argaze/GazeFeatures.py
+++ b/src/argaze/GazeFeatures.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-from typing import TypeVar, Tuple
+from typing import TypeVar, Tuple, Any
from dataclasses import dataclass, field
import math
import ast
@@ -285,3 +285,104 @@ class GazeMovementIdentifier():
continue
return ts_fixations, ts_saccades, ts_status
+
+VisualScanNodeType = TypeVar('VisualScanNode', bound="VisualScanNode")
+# Type definition for type annotation convenience
+
+@dataclass(frozen=True)
+class VisualScanNode():
+ """Define a visual scan node as a set of successive gaze movements onto a same AOI."""
+
+ movements: TimeStampedGazeMovements
+ """ """
+
+ aoi: str = field(default='')
+ """Name of the looked AOI"""
+
+VisualScanArcType = TypeVar('VisualScanArc', bound="VisualScanArc")
+# Type definition for type annotation convenience
+
+@dataclass(frozen=True)
+class VisualScanArc():
+ """Define a visual scan arc as a saccade to a next visual scan node."""
+
+ saccade: Saccade
+ """Saccade to the next scan node"""
+
+ to: VisualScanNode
+ """Node where the saccade ends"""
+
+class VisualScan(list):
+ """Build a linear path made of nodes relied by arcs from successive timestamped gaze movements over aoi."""
+
+ def __init__(self):
+
+ super().__init__()
+
+ self.__movements = TimeStampedGazeMovements()
+ self.__last_aoi = ''
+
+ def __repr__(self):
+ """String representation"""
+
+ return str(super())
+
+ def __str__(self) -> str:
+ """String display"""
+
+ output = ''
+
+ for arc in self:
+
+ output += f'> {arc.to.aoi} '
+
+ return output
+
+ def append_saccade(self, ts, saccade):
+ """Append a new saccade to visual scan."""
+
+ self.__movements[ts] = saccade
+
+ def append_fixation(self, ts, fixation, looked_aoi: str) -> bool:
+ """Append a new fixation to visual scan and return True if a new arc have been created."""
+
+ # Is the fixation onto a new aoi?
+ if looked_aoi != self.__last_aoi and len(self.__movements) > 0:
+
+ # Last movement should be a saccade
+ last_ts, last_movement = self.__movements.pop_last()
+ assert(type(last_movement).__bases__[0] == Saccade)
+
+ # Edit new node and arc
+ new_node = VisualScanNode(self.__movements, looked_aoi)
+ new_arc = VisualScanArc(last_movement, new_node)
+
+ # Append new arc
+ super().append(new_arc)
+
+ # Clear movements
+ self.__movements = TimeStampedGazeMovements()
+
+ # Append new fixation
+ self.__movements[ts] = fixation
+
+ # Remember new aoi
+ self.__last_aoi = looked_aoi
+
+ # Notify that a new arc have been created
+ return True
+
+ else:
+
+ # Append new fixation
+ self.__movements[ts] = fixation
+
+ return False
+
+class VisualScanAnalyzer():
+ """Abstract class to define what should provide a visual scan analyser."""
+
+ def analyze(self, visual_scan: list[VisualScanArcType]) -> Any:
+ """Analyze visual scan."""
+
+ raise NotImplementedError('analyze() method not implemented')