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:
$$z^2\; =\; 0.86^2\; \backslash cdot\; (1\; \; r^2)\; \backslash cdot\; (c\_0\; +\; c\_1r^2\; +\; c\_2r^4)$$ $$x\; =\; r\; \backslash cdot\; \backslash cos\; \backslash ,\; \backslash phi,\; \backslash ,\; y\; =\; r\; \backslash cdot\; \backslash sin\; \backslash ,\; \backslash phi\; \backslash quad\; r\; \backslash in\; [1,1],\; \backslash ,\; \backslash phi\; \backslash in\; [0,\; 2\backslash pi]$$ $$(X,\; Y,\; Z)\; =\; \backslash frac\{\backslash text\{diameter\}\}\{2\}\; \backslash cdot\; (x,y,z)$$The values of $c\_0$, $c\_1$ and $c\_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:

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

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.

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)$ in 3D space. The SDF for a sphere is actually quite simple:
For a point $P(x,y,z)$ in space, the SDF $\backslash phi$ of the sphere with radius $r$ is:
$$\backslash phi(P)\; =\; \backslash sqrt\{x^2\; +\; y^2\; +\; z^2\}\; \; r$$What does this function tell us?

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

If $P$ lies on the surface of the sphere, $\backslash phi(P)$ will be zero.

If $P$ is inside the sphere, $\backslash phi(P)$ will be negative, indicating how deep inside the sphere the point is.
To visualize this, consider a point $P(4,0,0)$ and a sphere of radius $r\; =\; 3$. The SDF would be $\backslash phi(P)\; =\; \backslash 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:
 Performance: Calculating a precise SDF might be computationally expensive, especially for complex models.
 Simplicity: For some shapes, deriving an exact SDF might be nontrivial, 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 tradeoffs. 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.
Advantages of Polygonal Discretization:

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.

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

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 tradeoff between precision and performance.

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.
2. Crafting the 2D SDF using the Polygon Method:
I started with creating the 2D SDF for the erythrocyte's profile:

Defining the Xaxis Values: I employed
np.linspace
to obtain a set ofsteps
values in the interval [0, 1] which served as the xvalues for our 2D profile.x = np.linspace(1, 1, steps)

Calculating the Zvalues: For each xvalue, I computed the corresponding positive zvalue, giving us half of the erythrocyte's 2D profile.
z2 = 0.86**2 * (1  x**2) * (c0 + c1*x**2 + c2*x**4) z = np.sqrt(z2)

Exploiting XY Symmetry: The erythrocyte also has symmetry across the XY plane. This allowed me to mirror the computed points, essentially using
x
andz
to obtain the complete 2D profile.points = np.concatenate((np.stack((x,z), axis=1), np.stack((x,z), axis=1)[2:0:1]))
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 inbuilt method revolve
in the library to revolve this 2D SDF around the zaxis. 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 mathematicallyrepresented 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
.

Detail: This parameter determines the sampling resolution when converting our 3D SDF representation into the STL format. By increasing the value of
detail
, we essentially increase the number of samples taken from the SDF, leading to a higher resolution model. It's worth noting that while a higherdetail
results in a more precise model, it also increases the computational overhead. 
Steps: When defining the 2D profile of the erythrocyte using the polygon method, the
steps
variable comes into play. It determines the number of segments or "steps" used to create the polygon, with a greater number of steps producing a smoother, more accurate shape.
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 builtin 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 highresolution, precise representations, all tailored to the requirements of the task at hand.
By following this workflow, we have a readytouse STL model of the erythrocyte based on the Skalak model, which can be further used for visualizations, 3D printing, or any other relevant applications.