Creating a 3D Model of an Erythrocyte using Python

Lets get started...

When we talk about cells flowing through our veins, the first ones that usually come to mind are erythrocytes, or red blood cells. In this post, we'll dive deep into how I modeled them in 3D using Python.

What is an Erythrocyte?

Erythrocytes, commonly referred to as red blood cells (RBCs), are responsible for delivering oxygen throughout our bodies. They have a distinctive biconcave disc shape which maximizes their surface area and aids in their function. The average diameter of a human erythrocyte is about 6.2–8.2 µm (we often use 7.5 µm), and the thickness at the thickest point is about 2.5 µm, while the thickness at the thinnest point (the center) is about 0.8 µm.

Introducing the Skalak Model

To create a 3D representation of an erythrocyte, we'll be implementing the Skalak model. This model provides a mathematical description that closely matches the biconcave shape of RBCs. The Skalak model was proposed in a paper by R. Skalak et al. [1].

Based on this model, the erythrocyte is defined as follows:

z2=0.862(1r2)(c0+c1r2+c2r4)z^2 = 0.86^2 \cdot (1 - r^2) \cdot (c_0 + c_1r^2 + c_2r^4) x=rcosϕ,y=rsinϕr[1,1],ϕ[0,2π]x = r \cdot \cos \, \phi, \, y = r \cdot \sin \, \phi \quad r \in [-1,1], \, \phi \in [0, 2\pi] (X,Y,Z)=diameter2(x,y,z)(X, Y, Z) = \frac{\text{diameter}}{2} \cdot (x,y,z)

The values of c0c_0, c1c_1 and c2c_2 are 0.01384083, 0.2842917 and 0.01306932 respectivly.

While there are multiple models and techniques available for modeling erythrocytes, I found the Skalak model to be particularly fascinating due to its precision and mathematical elegance. I hope this deep dive gives you a unique perspective on the intersection of biology, mathematics, and programming.

The Power of the 3D Modeling Library: SDFs at its Core

While creating the 3D model of the erythrocyte, I utilized a Python library [2] that's quite different from many traditional 3D modeling libraries. This library's core concept revolves around signed distance functions (SDFs).

What are Signed Distance Functions (SDFs)?

At a high level, a signed distance function is a function which, for any given point in space, returns the shortest distance between that point and the surface of an object. The 'signed' part means that the function returns a negative value if the point is inside the object, positive if it's outside and zero if it lies on the surface of the object.

Why is this concept so powerful for 3D modeling? With SDFs:

  1. Complexity is not an issue: Regardless of how intricate the shape is, the computational cost remains relatively consistent. This makes it scalable and efficient.

  2. Combining shapes is seamless: By performing simple mathematical operations, multiple SDFs can be combined to generate compound shapes. For instance, you can subtract one shape from another, or find the intersection between two shapes, with ease.

  3. High precision: Given that SDFs are mathematical in nature, the models they produce can be incredibly precise, avoiding some of the meshing artifacts seen in traditional 3D modeling.

By leveraging a library based on SDFs, I was able to craft a highly accurate and efficient 3D representation of an erythrocyte, grounded in the principles of the Skalak model.

Now let's illustrate the concept of SDFs with the most basic and intuitive example: the sphere.

Example: SDF of a Sphere

To further understand the concept of signed distance functions, consider a sphere centered at the origin (0,0,0)(0,0,0) in 3D space. The SDF for a sphere is actually quite simple:

For a point P(x,y,z)P(x,y,z) in space, the SDF ϕ\phi of the sphere with radius rr is:

ϕ(P)=x2+y2+z2r\phi(P) = \sqrt{x^2 + y^2 + z^2} - r

What does this function tell us?

  1. If PP lies outside the sphere, ϕ(P)\phi(P) will be positive and represents the shortest distance from the point to the sphere's surface.

  2. If PP lies on the surface of the sphere, ϕ(P)\phi(P) will be zero.

  3. If PP is inside the sphere, ϕ(P)\phi(P) will be negative, indicating how deep inside the sphere the point is.

To visualize this, consider a point P(4,0,0)P(4,0,0) and a sphere of radius r=3r = 3. The SDF would be ϕ(P)=42+02+023=1\phi(P) = \sqrt{4^2 + 0^2 + 0^2} - 3 = 1. This means the point is 1 unit outside the sphere.

This simple example showcases the elegance and power of SDFs. By extending this principle to more complex shapes and combining multiple SDFs, we can craft intricate 3D models efficiently.

A Note on Precision and Approximation in SDFs

While signed distance functions provide a mathematically elegant way to define shapes, not all SDFs are created equal. One might be tempted to use an SDF that's "good enough" – mathematically incorrect but seemingly precise for a given task.

Why might someone use an approximate SDF?

There could be various reasons:

  1. Performance: Calculating a precise SDF might be computationally expensive, especially for complex models.
  2. Simplicity: For some shapes, deriving an exact SDF might be non-trivial, making approximations a tempting alternative.

However, it's essential to be cautious:

Artifacts in Complex Models: Using an approximated SDF can lead to unintended visual anomalies, especially in intricate models. These "artifacts" manifest as irregularities in the model, such as bumps, gaps, or distortions, which are often not predictable.

The good news is that if you do encounter such artifacts, tools like Microsoft's 3D Builder or Autodesk's Meshmixer can sometimes offer simple fixes. These software solutions are adept at refining and correcting models, smoothing out irregularities, and ensuring the final output is polished.

The principle is akin to rounding numbers in mathematics. A small rounding error might not seem consequential initially, but compound it over complex calculations, and the end result might be significantly off.

For those leveraging this 3D modeling library, always be conscious of the trade-offs. While approximations can save time and computational resources, they might compromise the quality and integrity of the final model. When in doubt, it's always best to test, refine, and validate your SDFs rigorously.

Tapping into Polygonal Precision: Discretizing Smooth Surfaces

One standout feature of this 3D modeling library is its exact implementation for polygons. Rather than relying solely on mathematical approximations for smooth surfaces, we have the option to discretize these surfaces into numerous points, essentially breaking them down into a series of polygons. This technique, while being more accurate, does come with its own set of challenges and considerations. perfect sphereapproximated by5 pointsapproximated by20 points

Advantages of Polygonal Discretization:

  1. High Precision: By discretizing smooth surfaces, we can obtain an incredibly close approximation of the desired shape. This minimizes artifacts and ensures the model adheres closely to the intended design.

  2. Versatility: Even for complex geometries, using polygons allows for more flexibility and control over the model, especially when dealing with intricate details.

Considerations:

  1. Computational Cost: The more we lean into higher precision by increasing the number of polygons, the more computationally intensive the modeling becomes. This is a classic trade-off between precision and performance.

  2. Model Complexity: An increased number of polygons means more data to process, which can impact the rendering speed.

In conclusion, while polygonal discretization offers a pathway to high fidelity in 3D modeling, it's crucial to strike a balance. Depending on the application and the desired level of detail, one must choose an appropriate level of discretization, always keeping an eye on computational constraints.

Great! Now that we have a basic understanding of the topic, we can talk about implementation.

Implementing the Skalak Erythrocyte: Leveraging Symmetry

When tackling the modeling of the erythrocyte, I made full use of the inherent symmetries in the cell's shape. This not only simplified the process but also ensured a more accurate representation.

1. Embracing Rotational Symmetry:

Erythrocytes possess a rotational symmetry, which means their shape can be obtained by rotating a 2D profile around an axis. By focusing on modeling this 2D profile accurately, the 3D representation naturally follows.

Illustration of an erythrocyte slice at y=0y=0. The dotted line at x=0x=0 emphasizes the inherent rotational symmetry of the erythrocyte, showcasing how its unique shape can be obtained by revolving this profile around the central axis.

2. Crafting the 2D SDF using the Polygon Method:

I started with creating the 2D SDF for the erythrocyte's profile:

3. Revolving the 2D SDF to Obtain 3D:

With the 2D profile in hand, the next step was to bring it to life in 3D. I utilized the in-built method revolve in the library to revolve this 2D SDF around the z-axis. This rotation operation leverages the erythrocyte's rotational symmetry, seamlessly generating the full 3D shape.

4. Integrating Components and Exporting the Final Model:

With the foundational steps completed, it's time to piece everything together and translate our mathematically-represented erythrocyte into a tangible 3D model.

Consolidating the SDFs: First, I integrated the 2D SDF of the erythrocyte's profile with the revolved 3D shape, ensuring the complete structure adheres to the Skalak model's parameters.

Final Code Implementation:

def ery(d=7.6, steps=100):
    c0 = 0.01384083
    c1 = 0.2842917
    c2 = 0.01306932
    x = np.linspace(-1, 1, steps)
    z2 = 0.86**2 * (1 - x**2) * (c0 + c1*x**2 + c2*x**4)
    z = np.sqrt(z2)
    points = np.concatenate((np.stack((x,z), axis=-1), np.stack((x,-z), axis=-1)[-2:0:-1])) * d/2
    return polygon(points)

f = ery().revolve()

Exporting as STL:

STL (Stereolithography) is a standard file format popular for 3D printing and various CAD software. The accuracy and resolution of the exported STL model can be influenced by two primary parameters: detail and steps.

By adjusting both detail and steps, one can achieve the desired balance between computational efficiency and model accuracy. After finalizing these parameters, I employed the library's built-in methods to export our 3D SDF representation into an STL file.

detail = 0 # Increasing this will lead to a more accurate model
f.save('ery.stl', samples=2**(22 + detail))

This approach provides flexibility, allowing for models that range from quick drafts to high-resolution, precise representations, all tailored to the requirements of the task at hand.

By following this workflow, we have a ready-to-use STL model of the erythrocyte based on the Skalak model, which can be further used for visualizations, 3D printing, or any other relevant applications.


  1. R. Skalak, A. Tozeren, R. P. Zarda, and S. Chien, "Strain energy function of red blood cell membranes," Biophysical Journal, vol. 13, no. 3, pp. 245–264, 1973. ↩︎

  2. sdf Python library ↩︎