I'm working on a little project at the moment which simulates micro-organisms. I'm working on the cell membrane, which I have modeled as a bunch of box2d circles ("Vertex" class in my code) strung together with distanceJoints. So far so good.
However that only gets me part way, I want the internals of the cell to exert a pressure on the cell wall, trying to retain a circular shape. Without the internal force, the cell just falls into a sad one-dimensional heap. I initially used an internal skeleton, but I wasn't too thrilled with how it moved, and like the idea of my creatures having a lot more fluidity to their movements...I'd like to have an implementation of endocytosis, which doesn't play nice with the skeleton model.
I'm uploading my code as it currently stands, with a badly implemented pressure mechanic. I apologise in advance to anyone who takes a look, as I have been working on a whole bunch of features concurrently and haven't had a chance to refactor into a readable code base.
Currently in my code I have a "PolygonBody" class which implements the membrane through linked vertices. The PolygonBody looks something like what I have below, although this is edited to remove most of the misc bloat.
At the moment getting the pressure calculation right is what I care about and that is contained in PolygonBody:update().
That method
1. computes a pressure by taking the difference between the current area and the initial area (self.internal_vol)
2. finds the surface normal of each vertex, that is the direction of the net pull from the two other connected verticies.
3. applies pressure along the surface normal, distributed across each vertex by dividing by the number of vertices (self.res)
Code: Select all
PolygonBody = Class{}
--[[
A PolygonBody is a physical object defined by a collection of vertices,
linked via distance joints
]]
function PolygonBody:init(parent)
self.parent = parent
self.pos = parent.pos
self.world = parent.world
-[[
a bunch of stuff
---]]
self.verticies = {}
self.edges = {} for i=1, self.res do self.edges[i] = {} end
-- generate a ring of verticies
-- need all verticies to exist before we connect them all together
self:initEdges()
self.internal_vol = self:getArea()
end
function PolygonBody:update(dt)
-- pressure is negative when area is smaller than internalvol
local pressure = (self:getArea() - self.internal_vol) * PRESSURE_CONSTANT
self.pos = Vector(self:getPosition())
-- surface normal is in the direction opposite the pull of the two springs
for i, vertex in ipairs(self.verticies) do
local x0, y0 = vertex.x, vertex.y
local edges = {}
local normvec = Vector(0, 0)
for j, edge in pairs(self.edges[i]) do
local x1,y1 = self.verticies[j].x, self.verticies[j].y
normvec = normvec + Vector(x1-x0, y1-y0)
end
vertex.norm = normvec
local pforce = -normvec * pressure / self.res
vertex.body:applyLinearImpulse(pforce.x, pforce.y)
end
for i, vertex in ipairs(self.verticies) do
vertex:update(dt)
end
end
-- drawing function
PolygonBody:render()
-- iteratively links all edges on body creation
PolygonBody:initEdges()
-- adds distance joint between vertex vi and vj (vi and vj are numerical indices to self.verticies)
PolygonBody:linkEdge(vi,vj)
-- https://www.mathopenref.com/coordpolygonarea2.html
-- implementation of polygon area calculation
PolygonBody:getArea()
PolygonBody:getPosition() -- returns mid point, x,y ala b2d
Regards
Andrew