diff --git a/setup.py b/setup.py index 69899005b441bd7fdf26ccef89f9b44d0d8389f8..cf9a02e097472d9e1d6fea7da41f61eea81ced5c 100644 --- a/setup.py +++ b/setup.py @@ -32,18 +32,10 @@ def source_version(): .split("-") ) - if version_parts[-1] == "dirty": - dirty = True - version_parts = version_parts[:-1] - else: - dirty = False - version = version_parts[0] if len(version_parts) == 3: version += ".post0" version += f".dev{version_parts[1]}+{version_parts[2]}" - if dirty: - version += "+dirty" return version diff --git a/src/robofish/io/app.py b/src/robofish/io/app.py index eaf9d96ad63dcfcc162c8c46d35f8422bd509a34..4e73418db4a854c535bbeee1bbc193f4d73cd3bd 100644 --- a/src/robofish/io/app.py +++ b/src/robofish/io/app.py @@ -305,6 +305,13 @@ def update_individual_ids(args=None): nargs="+", help="The path to one or multiple files and/or folders.", ) + parser.add_argument( + "-o", + "--offset", + type=int, + default=0, + help="The offset for the first individual ids.", + ) if args is None: args = parser.parse_args() @@ -334,7 +341,7 @@ def update_individual_ids(args=None): # Find uniques in the first part of the split names _, unique_inverse = np.unique(split_file_names[:, 0], return_inverse=True) - running_individual_id = 0 + running_individual_id = args.offset for unique in range(max(unique_inverse) + 1): file_ids = np.where(unique_inverse == unique) diff --git a/src/robofish/io/file.py b/src/robofish/io/file.py index 2f7d146a320611ef6a37fc21f5ca03232de367fc..787333c98d0b115275b81ef2e116986403bd5e0f 100644 --- a/src/robofish/io/file.py +++ b/src/robofish/io/file.py @@ -66,6 +66,7 @@ class File(h5py.File): mode: str = "r", *, # PEP 3102 world_size_cm: List[int] = None, + world_shape: str = "rectangle", validate: bool = False, validate_when_saving: bool = True, strict_validate: bool = False, @@ -98,6 +99,8 @@ class File(h5py.File): world_size_cm : [int, int] , optional side lengths [x, y] of the world in cm. rectangular world shape is assumed. + world_shape : str, default="rectangle" + shape of the world. Currently "rectangle" and "ellipse" are supported. validate: bool, default=False Should the track be validated? This is normally switched off for performance reasons. strict_validate : bool, default=False @@ -207,6 +210,7 @@ class File(h5py.File): ), "world_size_cm and format_version have to be given when creating a new file." self.attrs["world_size_cm"] = np.array(world_size_cm, dtype=np.float32) + self.attrs["world_shape"] = world_shape self.attrs["format_version"] = np.array(format_version, dtype=np.int32) self.attrs["format_url"] = format_url @@ -499,7 +503,7 @@ class File(h5py.File): Args: category: The common category for the entities. The canonical values are ['organism', 'robot', 'obstacle']. - poses: three dimensional array, containing the poses of the entity. + poses: three dimensional array, containing the poses of the entity (n_fish, n_timesteps, 3). name: optional array of names of the entities. If no names are given, the category is used with an id (e.g. 'fish_1') individual_ids (Iterable[int]): optional array of individual ids of the entities. outlines: optional array, containing the outlines of the entities, either a three dimensional common outline array can be given, or a four dimensional array. @@ -1104,12 +1108,23 @@ class File(h5py.File): ] ] - border_vertices = np.array( - [ - np.array([-1, -1, 1, 1, -1]) * self.world_size[0] / 2, - np.array([-1, 1, 1, -1, -1]) * self.world_size[1] / 2, - ] - ) + def create_circle(sides): + angle = np.linspace(0, 2 * np.pi, sides, endpoint=False) + x = np.cos(angle) * self.world_size[0] / 2 + y = np.sin(angle) * self.world_size[1] / 2 + return np.array([x, y]) + + def create_square(): + x = np.array([-1, 1, 1, -1, -1]) * self.world_size[0] / 2 + y = np.array([-1, -1, 1, 1, -1]) * self.world_size[1] / 2 + return np.array([x, y]) + + if "world_shape" not in self.attrs or self.attrs["world_shape"] == "rectangle": + border_vertices = create_square() + elif self.attrs["world_shape"] == "ellipse": + border_vertices = create_circle(150) + else: + raise ValueError(f"Unknown world shape: {self.attrs['world_shape']}") spacing = 10 x = np.arange(