aboutsummaryrefslogtreecommitdiff
path: root/src/argaze.test/AreaOfInterest
diff options
context:
space:
mode:
authorThéo de la Hogue2022-11-21 16:57:58 +0100
committerThéo de la Hogue2022-11-21 16:57:58 +0100
commitb9c6bee54f5bce60636b01190c523a4faf448a08 (patch)
tree60d72c50fd4484d2f2eb5998922a5ff6ae4b7aff /src/argaze.test/AreaOfInterest
parenteb621720e5c9bdd94144557ea3190497de4a9aaa (diff)
downloadargaze-b9c6bee54f5bce60636b01190c523a4faf448a08.zip
argaze-b9c6bee54f5bce60636b01190c523a4faf448a08.tar.gz
argaze-b9c6bee54f5bce60636b01190c523a4faf448a08.tar.bz2
argaze-b9c6bee54f5bce60636b01190c523a4faf448a08.tar.xz
Testing AreaOfInterest submodule.
Diffstat (limited to 'src/argaze.test/AreaOfInterest')
-rw-r--r--src/argaze.test/AreaOfInterest/AOI2DScene.py154
-rw-r--r--src/argaze.test/AreaOfInterest/AOI3DScene.py76
-rw-r--r--src/argaze.test/AreaOfInterest/AOIFeatures.py204
-rw-r--r--src/argaze.test/AreaOfInterest/utils/scene.obj15
4 files changed, 449 insertions, 0 deletions
diff --git a/src/argaze.test/AreaOfInterest/AOI2DScene.py b/src/argaze.test/AreaOfInterest/AOI2DScene.py
new file mode 100644
index 0000000..8a71ffb
--- /dev/null
+++ b/src/argaze.test/AreaOfInterest/AOI2DScene.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python
+
+import unittest
+import math
+
+from argaze.AreaOfInterest import AOIFeatures, AOI2DScene
+from argaze import GazeFeatures
+
+import numpy
+
+class TestAOI2DSceneClass(unittest.TestCase):
+ """Test AOI2DScene class."""
+
+ def test_new(self):
+ """Test AOI2DScene creation."""
+
+ # Check empty AOI2DScene creation
+ aoi_2d_scene = AOI2DScene.AOI2DScene()
+
+ self.assertEqual(aoi_2d_scene.dimension, 2)
+ self.assertEqual(len(aoi_2d_scene.items()), 0)
+
+ # Check AOI2DScene creation
+ aoi_2D_A = AOIFeatures.AreaOfInterest([[0, 0], [0, 1], [1, 1], [1, 0]])
+ aoi_2D_B = AOIFeatures.AreaOfInterest([[1, 1], [1, 2], [2, 2], [2, 1]])
+ aoi_2d_scene = AOI2DScene.AOI2DScene({"A": aoi_2D_A, "B": aoi_2D_B})
+
+ self.assertEqual(aoi_2d_scene.dimension, 2)
+
+ def test_raycast(self):
+ """Test AOI2DScene raycast method."""
+
+ aoi_2D_A = AOIFeatures.AreaOfInterest([[0, 0], [0, 1], [1, 1], [1, 0]])
+ aoi_2D_B = AOIFeatures.AreaOfInterest([[1, 1], [1, 2], [2, 2], [2, 1]])
+ aoi_2d_scene = AOI2DScene.AOI2DScene({"A": aoi_2D_A, "B": aoi_2D_B})
+
+ gaze_position_A = GazeFeatures.GazePosition((0.5, 0.5))
+ gaze_position_B = GazeFeatures.GazePosition((1.5, 1.5))
+ gaze_position_C = GazeFeatures.GazePosition((0.5, 1.5))
+
+ # Check raycast results for gaze postion A
+ for name, aoi, looked in aoi_2d_scene.raycast(gaze_position_A):
+
+ if name == "A":
+
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi, aoi_2D_A))
+ self.assertEqual(looked, True)
+
+ elif name == "B":
+
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi, aoi_2D_B))
+ self.assertEqual(looked, False)
+
+ # Check raycast results for gaze postion B
+ for name, aoi, looked in aoi_2d_scene.raycast(gaze_position_B):
+
+ if name == "A":
+
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi, aoi_2D_A))
+ self.assertEqual(looked, False)
+
+ elif name == "B":
+
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi, aoi_2D_B))
+ self.assertEqual(looked, True)
+
+ # Check raycast results for gaze postion C
+ for name, aoi, looked in aoi_2d_scene.raycast(gaze_position_C):
+
+ if name == "A":
+
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi, aoi_2D_A))
+ self.assertEqual(looked, False)
+
+ elif name == "B":
+
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi, aoi_2D_B))
+ self.assertEqual(looked, False)
+
+ def test_circlecast(self):
+ """Test AOI2DScene circlecast method."""
+
+ aoi_2D_A = AOIFeatures.AreaOfInterest([[0, 0], [0, 1], [1, 1], [1, 0]])
+ aoi_2D_B = AOIFeatures.AreaOfInterest([[1, 1], [1, 2], [2, 2], [2, 1]])
+ aoi_2d_scene = AOI2DScene.AOI2DScene({"A": aoi_2D_A, "B": aoi_2D_B})
+
+ gaze_position_A = GazeFeatures.GazePosition((0.5, 0.5), accuracy=0.5)
+ gaze_position_B = GazeFeatures.GazePosition((1.5, 1.5), accuracy=0.5)
+ gaze_position_C = GazeFeatures.GazePosition((1., 1.), accuracy=1)
+ gaze_position_D = GazeFeatures.GazePosition((0.5, 1.5), accuracy=0.25)
+
+ # Check circlecast results for gaze postion A
+ for name, aoi, looked_region, aoi_ratio, gaze_ratio in aoi_2d_scene.circlecast(gaze_position_A):
+
+ if name == "A":
+
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi, aoi_2D_A))
+ self.assertTrue(math.isclose(aoi_ratio, math.pi / 4, abs_tol=1e-2))
+ self.assertTrue(math.isclose(gaze_ratio, 1., abs_tol=1e-3))
+
+ elif name == "B":
+
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi, aoi_2D_B))
+ self.assertEqual(aoi_ratio, 0.)
+ self.assertEqual(gaze_ratio, 0.)
+
+ # Check circlecast results for gaze postion B
+ for name, aoi, looked_region, aoi_ratio, gaze_ratio in aoi_2d_scene.circlecast(gaze_position_B):
+
+ if name == "A":
+
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi, aoi_2D_A))
+ self.assertEqual(aoi_ratio, 0.)
+ self.assertEqual(gaze_ratio, 0.)
+
+ elif name == "B":
+
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi, aoi_2D_B))
+ self.assertTrue(math.isclose(aoi_ratio, math.pi / 4, abs_tol=1e-2))
+ self.assertTrue(math.isclose(gaze_ratio, 1., abs_tol=1e-3))
+
+ # Check circlecast results for gaze postion C
+ for name, aoi, looked_region, aoi_ratio, gaze_ratio in aoi_2d_scene.circlecast(gaze_position_C):
+
+ if name == "A":
+
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi, aoi_2D_A))
+ self.assertTrue(math.isclose(aoi_ratio, math.pi / 4, abs_tol=1e-2))
+ self.assertTrue(math.isclose(gaze_ratio, 1 / 4, abs_tol=1e-3))
+
+ elif name == "B":
+
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi, aoi_2D_B))
+ self.assertTrue(math.isclose(aoi_ratio, math.pi / 4, abs_tol=1e-2))
+ self.assertTrue(math.isclose(gaze_ratio, 1 / 4, abs_tol=1e-3))
+
+ # Check circlecast results for gaze postion D
+ for name, aoi, looked_region, aoi_ratio, gaze_ratio in aoi_2d_scene.circlecast(gaze_position_D):
+
+ if name == "A":
+
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi, aoi_2D_A))
+ self.assertEqual(aoi_ratio, 0.)
+ self.assertEqual(gaze_ratio, 0.)
+
+ elif name == "B":
+
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi, aoi_2D_B))
+ self.assertEqual(aoi_ratio, 0.)
+ self.assertEqual(gaze_ratio, 0.)
+
+if __name__ == '__main__':
+
+ unittest.main() \ No newline at end of file
diff --git a/src/argaze.test/AreaOfInterest/AOI3DScene.py b/src/argaze.test/AreaOfInterest/AOI3DScene.py
new file mode 100644
index 0000000..5a575bb
--- /dev/null
+++ b/src/argaze.test/AreaOfInterest/AOI3DScene.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+
+import unittest
+import math
+import os
+
+from argaze.AreaOfInterest import AOIFeatures, AOI3DScene, AOI2DScene
+from argaze import GazeFeatures
+
+import numpy
+
+class TestAOI3DSceneClass(unittest.TestCase):
+ """Test AOI3DScene class."""
+
+ def test_new(self):
+ """Test AOI3DScene creation."""
+
+ # Check empty AOI3DScene creation
+ aoi_3d_scene = AOI3DScene.AOI3DScene()
+
+ self.assertEqual(aoi_3d_scene.dimension, 3)
+ self.assertEqual(len(aoi_3d_scene.items()), 0)
+
+ # Check AOI3DScene creation
+ aoi_3D_A = AOIFeatures.AreaOfInterest([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0]])
+ aoi_3D_B = AOIFeatures.AreaOfInterest([[1, 1, 0], [1, 2, 0], [2, 2, 0], [2, 1, 0]])
+ aoi_3d_scene = AOI3DScene.AOI3DScene({"A": aoi_3D_A, "B": aoi_3D_B})
+
+ self.assertEqual(aoi_3d_scene.dimension, 3)
+
+ def test_load(self):
+ """Test AOI3DScene load method."""
+
+ aoi_3d_scene_tt = AOI3DScene.AOI3DScene()
+
+ current_directory = os.path.dirname(os.path.abspath(__file__))
+ aoi_3d_scene_tt.load(os.path.join(current_directory, 'utils/scene.obj'))
+
+ self.assertEqual(len(aoi_3d_scene_tt.items()), 2)
+
+ def test_project(self):
+ """Test AOI3DScene project method."""
+
+ aoi_3D_A = AOIFeatures.AreaOfInterest([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0]])
+ aoi_3D_B = AOIFeatures.AreaOfInterest([[1, 1, 0], [1, 2, 0], [2, 2, 0], [2, 1, 0]])
+ aoi_3d_scene = AOI3DScene.AOI3DScene({"A": aoi_3D_A, "B": aoi_3D_B})
+
+ # Check default projection
+ aoi_2d_scene = aoi_3d_scene.project()
+
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi_2d_scene['A'], [[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]))
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi_2d_scene['B'], [[1.0, 1.0], [1.0, 2.0], [2.0, 2.0], [2.0, 1.0]]))
+
+ # Check projection with translation
+ aoi_2d_scene = aoi_3d_scene.project(T = numpy.array([1., 0., 0.]))
+
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi_2d_scene['A'], [[1.0, 0.0], [1.0, 1.0], [2.0, 1.0], [2.0, 0.0]]))
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi_2d_scene['B'], [[2.0, 1.0], [2.0, 2.0], [3.0, 2.0], [3.0, 1.0]]))
+
+ def test_transform(self):
+ """Test AOI3DScene transform method."""
+
+ aoi_3D_A = AOIFeatures.AreaOfInterest([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0]])
+ aoi_3D_B = AOIFeatures.AreaOfInterest([[1, 1, 0], [1, 2, 0], [2, 2, 0], [2, 1, 0]])
+ aoi_3d_scene = AOI3DScene.AOI3DScene({"A": aoi_3D_A, "B": aoi_3D_B})
+
+ # Check translation transformation
+ aoi_3d_scene = aoi_3d_scene.transform(T = numpy.array([1., 0., 0.]))
+
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi_3d_scene['A'], [[1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [2.0, 1.0, 0.0], [2.0, 0.0, 0.0]]))
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi_3d_scene['B'], [[2.0, 1.0, 0.0], [2.0, 2.0, 0.0], [3.0, 2.0, 0.0], [3.0, 1.0, 0.0]]))
+
+
+if __name__ == '__main__':
+
+ unittest.main() \ No newline at end of file
diff --git a/src/argaze.test/AreaOfInterest/AOIFeatures.py b/src/argaze.test/AreaOfInterest/AOIFeatures.py
new file mode 100644
index 0000000..650411a
--- /dev/null
+++ b/src/argaze.test/AreaOfInterest/AOIFeatures.py
@@ -0,0 +1,204 @@
+#!/usr/bin/env python
+
+import unittest
+import math
+
+from argaze.AreaOfInterest import AOIFeatures
+
+import numpy
+
+class TestAreaOfInterestClass(unittest.TestCase):
+ """Test AreaOfInterest class."""
+
+ def test_new(self):
+ """Test AreaOfInterest creation."""
+
+ # Check that 0D AreaOfInterest creation fails
+ with self.assertRaises(TypeError):
+
+ aoi_0D = AOIFeatures.AreaOfInterest()
+
+ # Check 1 point 1D AreaOfInterest creation
+ aoi_1D = AOIFeatures.AreaOfInterest([[0]])
+
+ self.assertEqual(aoi_1D.dimension, 1)
+ self.assertEqual(aoi_1D.size, 1)
+ self.assertEqual(aoi_1D.center, [0])
+
+ with self.assertRaises(AssertionError):
+ self.assertEqual(aoi_1D.bounding_box, 1)
+
+ # Check 2 points 1D AreaOfInterest creation
+ aoi_1D = AOIFeatures.AreaOfInterest([[0], [1]])
+
+ self.assertEqual(aoi_1D.dimension, 1)
+ self.assertEqual(aoi_1D.size, 2)
+ self.assertEqual(aoi_1D.center, [0.5])
+
+ with self.assertRaises(AssertionError):
+ self.assertEqual(aoi_1D.bounding_box, 1)
+
+ # Check 4 points 2D AreaOfInterest creation
+ aoi_2D = AOIFeatures.AreaOfInterest([[0, 0], [0, 1], [1, 0], [1, 1]])
+
+ self.assertEqual(aoi_2D.dimension, 2)
+ self.assertEqual(aoi_2D.size, 4)
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi_2D.center, [0.5, 0.5]))
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi_2D.bounding_box, [[0, 0], [1, 0], [1, 1], [0, 1]]))
+
+ # Check 8 points 3D AreaOfInterest creation
+ aoi_3D = AOIFeatures.AreaOfInterest([[0, 0, 0], [0, 1, 0], [1, 0, 0], [1, 1, 0], [0, 0, 1], [0, 1, 1], [1, 0, 1], [1, 1, 1]])
+
+ self.assertEqual(aoi_3D.dimension, 3)
+ self.assertEqual(aoi_3D.size, 8)
+
+ with self.assertRaises(AssertionError):
+ self.assertEqual(aoi_3D.bounding_box, 1)
+
+ def test___repr__(self):
+ """Test AreaOfInterest string representation."""
+
+ aoi_2D_int = AOIFeatures.AreaOfInterest([[0, 0], [0, 1], [1, 0], [1, 1]])
+
+ self.assertEqual(repr(aoi_2D_int), "[[0, 0], [0, 1], [1, 0], [1, 1]]")
+
+ aoi_2D_float = AOIFeatures.AreaOfInterest([[0, 0], [0, math.pi], [math.pi, 0], [math.pi, math.pi]])
+
+ self.assertEqual(repr(aoi_2D_float), f'[[0.0, 0.0], [0.0, {repr(math.pi)}], [{repr(math.pi)}, 0.0], [{repr(math.pi)}, {repr(math.pi)}]]')
+
+ def text_clockwise(self):
+ """Test AreaOfInterest clockwise method."""
+
+ aoi_2D_clockwise = AOIFeatures.AreaOfInterest([[0, 0], [0, 1], [1, 0], [1, 1]]).clockwise()
+
+ assertEqual(type(aoi_2D_clockwise), AOIFeatures.AreaOfInterest)
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi_2D_clockwise, [0, 0], [0, 1], [1, 1], [1, 0]))
+
+ def test_contains_point(self):
+ """Test AreaOfInterest contains_point method."""
+
+ aoi_2D = AOIFeatures.AreaOfInterest([[0, 0], [0, 1], [1, 1], [1, 0]])
+
+ self.assertTrue(aoi_2D.contains_point((0.1, 0.9)))
+ self.assertFalse(aoi_2D.contains_point((1.1, 0.9)))
+
+ def test_inner_axis(self):
+
+ aoi_2D = AOIFeatures.AreaOfInterest([[0, 0], [0, 2], [2, 2], [2, 0]])
+
+ self.assertEqual(aoi_2D.inner_axis((1, 1)), (0.5, 0.5))
+
+ def test_outter_axis(self):
+
+ aoi_2D = AOIFeatures.AreaOfInterest([[0, 0], [0, 2], [2, 2], [2, 0]])
+
+ self.assertEqual(aoi_2D.outter_axis((0.5, 0.5)), (1, 1))
+
+ def test_circle_intersection(self):
+
+ aoi_2D = AOIFeatures.AreaOfInterest([[0, 0], [0, 1], [1, 1], [1, 0]])
+ intersection, aoi_ratio, circle_ratio = aoi_2D.circle_intersection((0, 0), 1)
+
+ self.assertTrue(math.isclose(aoi_ratio, math.pi / 4, abs_tol=1e-2))
+ self.assertTrue(math.isclose(circle_ratio, 1 / 4, abs_tol=1e-3))
+
+class TestAOISceneClass(unittest.TestCase):
+ """Test AOIScene class."""
+
+ def test_new(self):
+ """Test AOIScene creation."""
+
+ # Check that 0D AOIScene creation fails
+ with self.assertRaises(AssertionError):
+ aoi_0D_scene = AOIFeatures.AOIScene(0)
+
+ # Check empty 2D AOIScene creation
+ aoi_2d_scene = AOIFeatures.AOIScene(2)
+
+ self.assertEqual(aoi_2d_scene.dimension, 2)
+ self.assertEqual(len(aoi_2d_scene.items()), 0)
+
+ # Check 2D AOIScene creation
+ aoi_2D = AOIFeatures.AreaOfInterest([[0, 0], [0, 1], [1, 1], [1, 0]])
+ aoi_2d_scene = AOIFeatures.AOIScene(2, {"A": aoi_2D})
+
+ self.assertEqual(aoi_2d_scene.dimension, 2)
+ self.assertEqual(len(aoi_2d_scene.items()), 1)
+ self.assertEqual(list(aoi_2d_scene.keys()), ["A"])
+
+ def test___repr__(self):
+ """Test AOIScene string representation."""
+
+ # Check empty 2D AOIScene representation
+ self.assertEqual(repr(AOIFeatures.AOIScene(2)), "{}")
+
+ # Check 2D AOIScene representation with integer AOI
+ aoi_2D_int = AOIFeatures.AreaOfInterest([[0, 0], [0, 1], [1, 1], [1, 0]])
+ aoi_2d_scene = AOIFeatures.AOIScene(2, {"A": aoi_2D_int})
+
+ self.assertEqual(repr(aoi_2d_scene), "{'A': [[0, 0], [0, 1], [1, 1], [1, 0]]}")
+
+ # Check 2D AOIScene representation with float AOI
+ aoi_2D_float = AOIFeatures.AreaOfInterest([[0, 0], [0, math.pi], [math.pi, 0], [math.pi, math.pi]])
+ aoi_2d_scene = AOIFeatures.AOIScene(2, {"A": aoi_2D_float})
+
+ expected_representation = "{'A': " + f'[[0.0, 0.0], [0.0, {repr(math.pi)}], [{repr(math.pi)}, 0.0], [{repr(math.pi)}, {repr(math.pi)}]]' + "}"
+ self.assertEqual(repr(aoi_2d_scene), expected_representation)
+
+ def test_properties(self):
+ """Test AOIScene properties."""
+
+ aoi_2D_A = AOIFeatures.AreaOfInterest([[0, 0], [0, 1], [1, 1], [1, 0]])
+ aoi_2D_B = AOIFeatures.AreaOfInterest([[1, 1], [1, 2], [2, 2], [2, 1]])
+ aoi_2d_scene = AOIFeatures.AOIScene(2, {"A": aoi_2D_A, "B": aoi_2D_B})
+
+ self.assertEqual(aoi_2d_scene.dimension, 2)
+ self.assertEqual(len(aoi_2d_scene.items()), 2)
+ self.assertEqual(list(aoi_2d_scene.keys()), ["A", "B"])
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi_2d_scene.bounds, [[0, 0], [2, 2]]))
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi_2d_scene.center, [1, 1]))
+ self.assertIsNone(numpy.testing.assert_array_equal(aoi_2d_scene.size, [2, 2]))
+
+ def test_copy(self):
+ """Test AOIScene copy method."""
+
+ aoi_2D_A = AOIFeatures.AreaOfInterest([[0, 0], [0, 1], [1, 1], [1, 0]])
+ aoi_2D_B = AOIFeatures.AreaOfInterest([[1, 1], [1, 2], [2, 2], [2, 1]])
+ aoi_2d_scene = AOIFeatures.AOIScene(2, {"A": aoi_2D_A, "B": aoi_2D_B})
+
+ # Check full copy
+ aoi_2d_scene_copy = aoi_2d_scene.copy()
+
+ self.assertEqual(aoi_2d_scene_copy.dimension, 2)
+ self.assertEqual(len(aoi_2d_scene_copy.items()), 2)
+ self.assertEqual(list(aoi_2d_scene_copy.keys()), ["A", "B"])
+
+ # Check cpy with exclusion
+ aoi_2d_scene_copy = aoi_2d_scene.copy(exclude=["B"])
+
+ self.assertEqual(aoi_2d_scene_copy.dimension, 2)
+ self.assertEqual(len(aoi_2d_scene_copy.items()), 1)
+ self.assertEqual(list(aoi_2d_scene_copy.keys()), ["A"])
+
+class TestTimeStampedAOIScenesClass(unittest.TestCase):
+ """Test TimeStampedAOIScenes class."""
+
+ def test___setitem__(self):
+ """Test TimeStampedAOIScenes creation."""
+
+ aoi_2D_A = AOIFeatures.AreaOfInterest([[0, 0], [0, 1], [1, 1], [1, 0]])
+ aoi_2D_B = AOIFeatures.AreaOfInterest([[1, 1], [1, 2], [2, 2], [2, 1]])
+ aoi_2d_scene = AOIFeatures.AOIScene(2, {"A": aoi_2D_A, "B": aoi_2D_B})
+
+ ts_aois_scenes = AOIFeatures.TimeStampedAOIScenes()
+
+ ts_aois_scenes[0] = aoi_2d_scene
+
+ # Check that only AOIScene can be added
+ with self.assertRaises(AssertionError):
+
+ ts_aois_scenes[1] = "This string is not an AOIScene"
+
+if __name__ == '__main__':
+
+ unittest.main() \ No newline at end of file
diff --git a/src/argaze.test/AreaOfInterest/utils/scene.obj b/src/argaze.test/AreaOfInterest/utils/scene.obj
new file mode 100644
index 0000000..cc9c092
--- /dev/null
+++ b/src/argaze.test/AreaOfInterest/utils/scene.obj
@@ -0,0 +1,15 @@
+# .OBJ file for AOI3DScene unitary test
+o A_Plan
+v 0.000000 0.000000 0.000000
+v 1.000000 0.000000 0.000000
+v 0.000000 1.000000 0.000000
+v 1.000000 1.000000 0.000000
+s off
+f 1 2 4 3
+o B_Plan
+v 1.000000 1.000000 0.000000
+v 2.000000 1.000000 0.000000
+v 1.000000 2.000000 0.000000
+v 2.000000 2.000000 0.000000
+s off
+f 5 6 8 7