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(