diff --git a/src/tg/detail/functions/normal.hh b/src/tg/detail/functions/normal.hh
index 7ba0244b71962b4356c3b2b6eede84419e7465c3..3dd2cf1a6b5cabaa34a3a45d9e01b4847e2c79fd 100644
--- a/src/tg/detail/functions/normal.hh
+++ b/src/tg/detail/functions/normal.hh
@@ -6,9 +6,12 @@
 #include "../../types/objects/ray.hh"
 #include "../../types/objects/segment.hh"
 #include "../../types/objects/triangle.hh"
+#include "../../types/objects/sphere.hh"
 #include "normalize.hh"
 
-// Computes the normal of an object
+// Computes the normal at the surface of an object
+// Some objects have a fixed normal everywhere, some only at defined positions
+// Evaluating the normal at other positions might be undefined or wrong
 
 namespace tg
 {
diff --git a/src/tg/types/objects/ball.hh b/src/tg/types/objects/ball.hh
new file mode 100644
index 0000000000000000000000000000000000000000..b48ede8a51a4bba800b13bf954141b8555a429a9
--- /dev/null
+++ b/src/tg/types/objects/ball.hh
@@ -0,0 +1,55 @@
+#pragma once
+
+#include "../pos.hh"
+#include "../scalar.hh"
+
+namespace tg
+{
+/**
+ * Volume bounded by of a sphere
+ */
+template <int D, class ScalarT>
+struct ball;
+
+// Common ball types
+
+using ball1 = ball<1, f32>;
+using ball2 = ball<2, f32>;
+using ball3 = ball<3, f32>;
+using ball4 = ball<4, f32>;
+
+using fball1 = ball<1, f32>;
+using fball2 = ball<2, f32>;
+using fball3 = ball<3, f32>;
+using fball4 = ball<4, f32>;
+
+using dball1 = ball<1, f64>;
+using dball2 = ball<2, f64>;
+using dball3 = ball<3, f64>;
+using dball4 = ball<4, f64>;
+
+using iball1 = ball<1, i32>;
+using iball2 = ball<2, i32>;
+using iball3 = ball<3, i32>;
+using iball4 = ball<4, i32>;
+
+using uball1 = ball<1, u32>;
+using uball2 = ball<2, u32>;
+using uball3 = ball<3, u32>;
+using uball4 = ball<4, u32>;
+
+
+// ======== IMPLEMENTATION ========
+
+template <int D, class ScalarT>
+struct ball
+{
+    using pos_t = pos<D, ScalarT>;
+
+    pos_t center;
+    ScalarT radius;
+
+    constexpr ball() = default;
+    constexpr ball(pos_t c, ScalarT r) : center(c), radius(r) {}
+};
+} // namespace tg
diff --git a/src/tg/types/objects/circle.hh b/src/tg/types/objects/circle.hh
new file mode 100644
index 0000000000000000000000000000000000000000..bdd33f82e5908f19879e9336098009cf5991be72
--- /dev/null
+++ b/src/tg/types/objects/circle.hh
@@ -0,0 +1,62 @@
+#pragma once
+
+#include "../pos.hh"
+#include "../vec.hh"
+
+namespace tg
+{
+/**
+ * 1D line that surrounds a disk
+ *
+ * a circle3 is a circle living on a plane in 3D
+ */
+template <int D, class ScalarT>
+struct circle;
+
+// Common circle types
+
+using circle2 = circle<2, f32>;
+using circle3 = circle<3, f32>;
+
+using fcircle2 = circle<2, f32>;
+using fcircle3 = circle<3, f32>;
+
+using dcircle2 = circle<2, f64>;
+using dcircle3 = circle<3, f64>;
+
+using icircle2 = circle<2, i32>;
+using icircle3 = circle<3, i32>;
+
+using ucircle2 = circle<2, u32>;
+using ucircle3 = circle<3, u32>;
+
+
+// ======== IMPLEMENTATION ========
+
+template <class ScalarT>
+struct circle<2, ScalarT>
+{
+    using pos_t = pos<2, ScalarT>;
+    using vec_t = vec<2, ScalarT>;
+
+    pos_t center;
+    ScalarT radius;
+
+    constexpr circle() = default;
+    constexpr circle(pos_t c, ScalarT r) : center(c), radius(r) {}
+};
+
+template <class ScalarT>
+struct circle<3, ScalarT>
+{
+    using pos_t = pos<3, ScalarT>;
+    using vec_t = vec<3, ScalarT>;
+
+    pos_t center;
+    ScalarT radius;
+    vec_t normal;
+
+    constexpr circle() = default;
+    constexpr circle(pos_t c, ScalarT r, vec_t n) : center(c), radius(r), normal(n) {}
+};
+} // namespace tg
diff --git a/src/tg/types/objects/disk.hh b/src/tg/types/objects/disk.hh
new file mode 100644
index 0000000000000000000000000000000000000000..c91dd4b304c4cce98ffb17ee111a3392263672dc
--- /dev/null
+++ b/src/tg/types/objects/disk.hh
@@ -0,0 +1,62 @@
+#pragma once
+
+#include "../pos.hh"
+#include "../vec.hh"
+
+namespace tg
+{
+/**
+ * 2D circular area enclosed by a circle
+ *
+ * a disk3 is a disk living on a plane in 3D
+ */
+template <int D, class ScalarT>
+struct disk;
+
+// Common disk types
+
+using disk2 = disk<2, f32>;
+using disk3 = disk<3, f32>;
+
+using fdisk2 = disk<2, f32>;
+using fdisk3 = disk<3, f32>;
+
+using ddisk2 = disk<2, f64>;
+using ddisk3 = disk<3, f64>;
+
+using idisk2 = disk<2, i32>;
+using idisk3 = disk<3, i32>;
+
+using udisk2 = disk<2, u32>;
+using udisk3 = disk<3, u32>;
+
+
+// ======== IMPLEMENTATION ========
+
+template <class ScalarT>
+struct disk<2, ScalarT>
+{
+    using pos_t = pos<2, ScalarT>;
+    using vec_t = vec<2, ScalarT>;
+
+    pos_t center;
+    ScalarT radius;
+
+    constexpr disk() = default;
+    constexpr disk(pos_t c, ScalarT r) : center(c), radius(r) {}
+};
+
+template <class ScalarT>
+struct disk<3, ScalarT>
+{
+    using pos_t = pos<3, ScalarT>;
+    using vec_t = vec<3, ScalarT>;
+
+    pos_t center;
+    ScalarT radius;
+    vec_t normal;
+
+    constexpr disk() = default;
+    constexpr disk(pos_t c, ScalarT r, vec_t n) : center(c), radius(r), normal(n) {}
+};
+} // namespace tg
diff --git a/src/tg/types/objects/objects.hh b/src/tg/types/objects/objects.hh
index 047dc85765e72c1c77bca6a3fd256993ff7efc32..1981242bec662eb76a1aa4a8e267f14b2da6dba2 100644
--- a/src/tg/types/objects/objects.hh
+++ b/src/tg/types/objects/objects.hh
@@ -1,7 +1,9 @@
 #pragma once
 
+#include "ball.hh"
 #include "box.hh"
 #include "cylinder.hh"
+#include "disk.hh"
 #include "frustum.hh"
 #include "line.hh"
 #include "plane.hh"
diff --git a/src/tg/types/objects/sphere.hh b/src/tg/types/objects/sphere.hh
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..33f30b896c9009d7448641f4427a64c1999a7f70 100644
--- a/src/tg/types/objects/sphere.hh
+++ b/src/tg/types/objects/sphere.hh
@@ -0,0 +1,55 @@
+#pragma once
+
+#include "../pos.hh"
+#include "../scalar.hh"
+
+namespace tg
+{
+/**
+ * Surface of a completely round ball
+ */
+template <int D, class ScalarT>
+struct sphere;
+
+// Common sphere types
+
+using sphere1 = sphere<1, f32>;
+using sphere2 = sphere<2, f32>;
+using sphere3 = sphere<3, f32>;
+using sphere4 = sphere<4, f32>;
+
+using fsphere1 = sphere<1, f32>;
+using fsphere2 = sphere<2, f32>;
+using fsphere3 = sphere<3, f32>;
+using fsphere4 = sphere<4, f32>;
+
+using dsphere1 = sphere<1, f64>;
+using dsphere2 = sphere<2, f64>;
+using dsphere3 = sphere<3, f64>;
+using dsphere4 = sphere<4, f64>;
+
+using isphere1 = sphere<1, i32>;
+using isphere2 = sphere<2, i32>;
+using isphere3 = sphere<3, i32>;
+using isphere4 = sphere<4, i32>;
+
+using usphere1 = sphere<1, u32>;
+using usphere2 = sphere<2, u32>;
+using usphere3 = sphere<3, u32>;
+using usphere4 = sphere<4, u32>;
+
+
+// ======== IMPLEMENTATION ========
+
+template <int D, class ScalarT>
+struct sphere
+{
+    using pos_t = pos<D, ScalarT>;
+
+    pos_t center;
+    ScalarT radius;
+
+    constexpr sphere() = default;
+    constexpr sphere(pos_t c, ScalarT r) : center(c), radius(r) {}
+};
+} // namespace tg
diff --git a/src/tg/types/pos.hh b/src/tg/types/pos.hh
index ad3782972080f244eb1464528f4f2767bebcedcd..0a4f873b424f9c412e9d8d2ebbcdc923cb23470f 100644
--- a/src/tg/types/pos.hh
+++ b/src/tg/types/pos.hh
@@ -40,11 +40,6 @@ using upos4 = pos<4, u32>;
 
 // ======== IMPLEMENTATION ========
 
-template <int D, class ScalarT>
-struct pos
-{
-};
-
 template <class ScalarT>
 struct pos<1, ScalarT>
 {
diff --git a/src/tg/types/size.hh b/src/tg/types/size.hh
index 91795ab080e43233d8d331593b42eb1b2224f61f..fe971473123ec1262b96f0db2287136fa84127cb 100644
--- a/src/tg/types/size.hh
+++ b/src/tg/types/size.hh
@@ -41,11 +41,6 @@ using usize4 = size<4, u32>;
 
 // ======== IMPLEMENTATION ========
 
-template <int D, class ScalarT>
-struct size
-{
-};
-
 template <class ScalarT>
 struct size<1, ScalarT>
 {
diff --git a/src/tg/types/vec.hh b/src/tg/types/vec.hh
index 98399cf98df706590951ad4e16ffcc9a7f7c4f16..f5755bba06e91dc75a70d598a83c7a60b5248952 100644
--- a/src/tg/types/vec.hh
+++ b/src/tg/types/vec.hh
@@ -115,11 +115,6 @@ using f64vec4 = vec<4, f64>;
 
 // ======== IMPLEMENTATION ========
 
-template <int D, class ScalarT>
-struct vec
-{
-};
-
 template <class ScalarT>
 struct vec<1, ScalarT>
 {