diff --git a/navipy/resources/twocylinders_world.blend b/navipy/resources/twocylinders_world.blend new file mode 100644 index 0000000000000000000000000000000000000000..1536511213bcddda2be43592dd2c2da781ba281f Binary files /dev/null and b/navipy/resources/twocylinders_world.blend differ diff --git a/navipy/sensors/blendtest_renderer.py b/navipy/sensors/blendtest_renderer.py new file mode 100644 index 0000000000000000000000000000000000000000..6189b00566a73d98a6259de2e91e2e0dc12a49ef --- /dev/null +++ b/navipy/sensors/blendtest_renderer.py @@ -0,0 +1,67 @@ +""" + Unittesting under blender +""" +import unittest +import numpy as np +from navipy.sensors.renderer import BlenderRender + + +class TestBlenderRender(unittest.TestCase): + @classmethod + def setUpClass(self): + self.cyberbee = BlenderRender() + + def test_class_assigments_error(self): + """ Test that error are correctly raised + List of tested function: + * camera_rotation_mode + * cycle_samples + * camera_fov + * camera_gaussian_width + * camera_resolution + """ + with self.assertRaises(TypeError): + # Should a string + self.cyberbee.camera_rotation_mode = 0 + with self.assertRaises(TypeError): + # Should be an integer + self.cyberbee.cycle_samples = 0.1 + with self.assertRaises(TypeError): + # Should be a tuple list or np.ndarray + self.cyberbee.camera_fov = 'bla' + with self.assertRaises(TypeError): + # Should be a float or int, so not a complex + self.cyberbee.camera_gaussian_width = 4 + 4j + with self.assertRaises(TypeError): + # Should be a tuple, list or nd.array + self.cyberbee.camera_resolution = 'bla' + + def test_class_assigments(self): + """ Test set/get match + + * camera_rotation_mode + * cycle_samples + * camera_fov + * camera_gaussian_width + * camera_resolution + """ + # camera cycle_samples + val = 100 + self.cyberbee.cycle_samples = val + self.assertEqual(val, self.cyberbee.cycle_samples) + # camera rotation mode + val = 'XYZ' + self.cyberbee.camera_rotation_mode = val + self.assertEqual(val, self.cyberbee.camera_rotation_mode) + # camera fov + val = np.array([[-90, 90], [-180, 180]]) + self.cyberbee.camera_fov = val + np.testing.assert_allclose(val, self.cyberbee.camera_fov) + # camera gaussian + val = 1.5 + self.cyberbee.gaussian_width = val + self.assertEqual(val, self.cyberbee.gaussian_width) + # camera resolution + val = np.array([360, 180]) + self.cyberbee.camera_resolution = val + np.testing.assert_allclose(val, self.cyberbee.camera_resolution) diff --git a/navipy/sensors/blendunittest.py b/navipy/sensors/blendunittest.py new file mode 100644 index 0000000000000000000000000000000000000000..889dc2e5b8fba33d7f42c6107322ebe14692928b --- /dev/null +++ b/navipy/sensors/blendunittest.py @@ -0,0 +1,89 @@ +""" + +""" +import unittest +import sys +import argparse +import pkg_resources +import tempfile +import os +import inspect + + +def parser_blendnavipy(): + # Create command line options + parser = argparse.ArgumentParser() + arghelp = 'Path to the environment (.blend) in which your agent lives' + defaultworld = pkg_resources.resource_filename( + 'navipy', 'resources/twocylinders_world.blend') + parser.add_argument('--blender-world', + type=str, + default=defaultworld, + help=arghelp) + arghelp = 'Path to start searching for blendtest*.py' + parser.add_argument('--start-dir', + type=str, + default='navipy', + help=arghelp) + arghelp = 'Command to run blender\n' + arghelp += 'If not provided, the script will try to find the command' + arghelp += " by using: shutil.which('blender')" + parser.add_argument('--blender-command', + type=str, + default=None, + help=arghelp) + + arghelp = 'To display some stuff \n' + arghelp += ' * -v print command \n' + arghelp += ' * -vv print also script' + parser.add_argument('-v', '--verbose', + action='count', + default=0, + help=arghelp) + return parser + + +def run(start_dir): + suite = unittest.defaultTestLoader.discover(start_dir=start_dir, + pattern='blendtest*.py') + success = unittest.TextTestRunner().run(suite).wasSuccessful() + print(success) + if not success: + raise Exception('Tests Failed') + + +def main(): + # encoding for temporary file + encoding = 'utf-8' + + # Fetch arguments + args = parser_blendnavipy().parse_args() + + # Create tempfile with testing code and then call blendnavipy + header = '# Generated by {}\n'.format(sys.argv[0]) + with tempfile.NamedTemporaryFile() as tfile: + # Start of file + tfile.write(header.encode(encoding)) + tfile.write('import unittest \n'.encode(encoding)) + for line in inspect.getsourcelines(run)[0]: + tfile.write(line.encode(encoding)) + tfile.write('\n\n'.encode(encoding)) + tfile.write('try:\n'.encode(encoding)) + tfile.write(' run("{}")\n'.format(args.start_dir).encode(encoding)) + tfile.write(' sys.exit(0)\n'.encode(encoding)) + tfile.write('except Exception:\n'.encode(encoding)) + tfile.write(' sys.exit(1)\n'.encode(encoding)) + tfile.seek(0) + + command = 'blendnavipy --blender-world {} --python-script {}' + command = command.format(args.blender_world, tfile.name) + if args.blender_command is not None: + command += ' --blender-command {}'.format(args.blender_command) + for _ in range(args.verbose): + command += ' -v' + os.system(command) + + +if __name__ == "__main__": + # execute only if run as a script + main() diff --git a/navipy/sensors/renderer.py b/navipy/sensors/renderer.py index 4005523e4962ffea36aa1f1454a727d28f5c5ac7..2c998493cfbdd0dec00c574f83cd80bf9b2daa84 100644 --- a/navipy/sensors/renderer.py +++ b/navipy/sensors/renderer.py @@ -136,23 +136,23 @@ class BlenderRender(): """get fov of camera :returns: the field of view of the camera as \ -[min,max],[longitude,latitude] in degrees - :rtype: dict + [[minimum latitude, maximum latitude], + [minimum longitude, maximum longitude]] + (in deg) + :rtype: np.array ..todo Change assert to if -> raise TypeError/KeyError """ - if self.camera.data.type != 'PANO': + if not self.camera.data.type == 'PANO': raise TypeError('Camera is not panoramic') - if self.camera.cycles.panorama_type == 'EQUIRECTANGULAR': + if not self.camera.data.cycles.panorama_type == 'EQUIRECTANGULAR': raise TypeError('The panoramic camera is not equirectangular') - fov = dict() - fov['latitude_min'] = np.rad2ged(self.camera.data.cycles.latitude_min) - fov['latitude_max'] = np.rad2ged(self.camera.data.cycles.latitude_max) - fov['longitude_min'] = np.rad2ged( - self.camera.data.cycles.longitude_min) - fov['longitude_max'] = np.rad2ged( - self.camera.data.cycles.longitude_max) + fov = np.zeros((2, 2)) + fov[0, 0] = np.rad2deg(self.camera.data.cycles.latitude_min) + fov[0, 1] = np.rad2deg(self.camera.data.cycles.latitude_max) + fov[1, 0] = np.rad2deg(self.camera.data.cycles.longitude_min) + fov[1, 1] = np.rad2deg(self.camera.data.cycles.longitude_max) return fov @camera_fov.setter diff --git a/setup.py b/setup.py index 5026698f58cd8a62dc40cd12daa2bf2fa0452aa1..004da086a0e223055398ba1503f6f4443e9d6220 100644 --- a/setup.py +++ b/setup.py @@ -41,10 +41,11 @@ setup_dict = {'name': 'navipy', 'networkx', 'sphinx-argparse'], 'package_data': {'navipy': ['resources/database.db', - 'resources/forest_world.blend']}, + 'resources/*.blend']}, 'include_package_data': True, 'entry_points': { 'console_scripts': [ - 'blendnavipy=navipy.sensors.blendnavipy:main']}, } + 'blendnavipy=navipy.sensors.blendnavipy:main', + 'blendunittest=navipy.sensors.blendunittest:main']}, } setup(**setup_dict)