diff --git a/measure.py b/measure.py
new file mode 100644
index 0000000000000000000000000000000000000000..94b79a48f70a24ec938c09d275db6ff9d023b77f
--- /dev/null
+++ b/measure.py
@@ -0,0 +1,126 @@
+import numpy as np
+import overlap
+
+from polygon_triangulate import polygon_triangulate, PlotTriangulation
+from tetrahedralize_triangles import tetrahedralize
+
+def measure_volume(V_tet, T, origin, r):
+
+    sphere = overlap.Sphere(origin, r)
+    volume = 0.0
+
+    for tet in T:
+        vertices = V_tet[tet]
+        overlap_tet = overlap.Tetrahedron(vertices)
+        overlap_volume = overlap.overlap(sphere, overlap_tet)
+        volume += overlap_volume
+
+    return volume
+
+def same_point(x, y):
+    return np.linalg.norm(x - y) < 1e-10
+
+def remove_duplicate_contour_points(P):
+    n = len(P)
+    for i in range(n):
+        j = (i + 1) % n
+        if same_point(P[i], P[j]):
+            return remove_duplicate_contour_points(np.delete(P, i, axis=0))
+    return P
+
+def measure_cumulative_volume(z, P, thickness, origin, R):
+    '''
+    Integration of the intersecting volume of a thick polygon and a sphere of increasing radius.
+
+    Parameters
+        ----------
+        z : float
+            The z depth-wise coordinate of the thick polygon.
+        P : (N, 2) array_like
+            A real array of ordered contour points of the polygon.
+        thickness : float
+            The thickness of the polygon.
+        origin : (3,) array_like
+            The center of the sphere from which to integrate from.
+        R : (M,) array_like
+            The discrete sphere radii from which to measure the polygon in.
+        
+        Returns
+        -------
+        mu: (M,) array_like
+            The intersecting volumes of the sphere and the thick polygon at radii determined by R.
+    '''
+
+    P = np.array(P)
+    origin = np.array(origin)
+
+    P = remove_duplicate_contour_points(P)
+
+    z0 = z - thickness / 2
+    z1 = z + thickness / 2
+    _, V_tri, F = polygon_triangulate(P)
+
+    V_tet, T = tetrahedralize(V_tri, F, z0, z1)
+
+    mu = np.zeros(len(R))
+    for i, r in enumerate(R):
+
+        if r <= 0:
+            continue
+
+        mu[i] = measure_volume(V_tet, T, origin, r)
+
+    return mu
+
+def measure_cumulative_points(z, X, thickness, origin, R):
+    '''
+    Integration of the points placed in a thick section integrated intersecting with a sphere of increasing size.
+
+    Parameters
+        ----------
+        z : float
+            The z depth-wise coordinate of the section with the points.
+        X : (N, 2) array_like
+            A real array of points inside the section.
+        thickness : float
+            The thickness of the section of which the points are in.
+        origin : (3,) array_like
+            The center of the sphere from which to integrate from.
+        R : (M,) array_like
+            The discrete sphere radii from which to measure the points inside.
+        
+        Returns
+        -------
+        mu: (M,) array_like
+            The integrated point masses inside the sphere of radii determined by R.
+    '''
+
+    oz = origin[2]
+    oxy = origin[:2]
+
+    dxy = np.array([np.linalg.norm(x - oxy) for x in X])
+
+    mu = np.zeros(len(R))
+
+    if z == oz:
+        for i, r in enumerate(R):
+            s = np.sqrt(np.clip(r**2 - dxy**2, 0, None))
+            mu[i] = np.sum(np.clip(2*s/thickness, 0, 1))
+
+        return mu
+
+    # plane is below, mirror it above to simplify calculations
+    if z < oz:
+        z = z + 2*(oz - z)
+
+    z0 = z - thickness / 2
+    z1 = z + thickness / 2
+
+    d0 = z0 - oz
+
+    for i, r in enumerate(R):
+        h = np.sqrt(np.clip(r**2 - dxy**2, 0, None))
+        s = h - d0
+        mu[i] = np.sum(np.clip(s/thickness, 0, 1))
+
+    return mu
\ No newline at end of file