Page 1 of 1

[solved] help with internal pressure simulation

Posted: Fri May 15, 2020 11:32 am
by AndrewMicallef
WIP code:
membrane.love
(394.21 KiB) Downloaded 172 times
Hey there,

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

While this feels like a step on the right direction relative to an internal skeleton, it still doesn't quite work the way I had hoped. If you have any ideas for how I might improve, please feel free to share.

Regards
Andrew

Re: help with internal pressure simulation

Posted: Sat May 16, 2020 10:55 am
by AndrewMicallef
Okay,
So after a bit of messing around I think I am getting close to something I can be happy with.
So I made some debug visualisations to work out what was happening with the forces, and made a consolidated table of forces on each vertex, which sum together on each vertex's update call to provide a linear impulse.
Part of the problem had been that I had used explicit names to index the force table, (ie descriptive strings like 'pressure' and 'spring') and then was iterating using ipairs...which meant that my additional net force was 0.

Additionally I sat down and had a think about what pressure actually is, and came up with a different strategy to model it. In my current implementation each vertex exerts a repellent force on every other vertex, proportional to the distance between them.

The below gif shows what this looks like, in the absence of any surface forces binding verticies together.

Unfortunately that is where I have to leave it for tonight, with surface broken.
membrane_1.love
(395.85 KiB) Downloaded 168 times
Edit: It wasn't that hard to put it back together. See the love file, right click on any vertex to select it, left click to drag it around, left clicking away from selected a vertex deselects it. The lines that appear on a selected vertex show the forces acting on that vertex (except those from the joint, not sure how to access those in a sensible way)
fluidmembrane.gif
fluidmembrane.gif (3.25 MiB) Viewed 4018 times

Re: [solved] help with internal pressure simulation

Posted: Sat May 16, 2020 2:30 pm
by pgimeno
Looks cool! Note however that pressure does not exert force towards the centre, but normal to the surface. You can approximate the normal by calculating the perpendicular to the line that joins the two neighbouring vertices. But then there's surface tension too, which makes your approach look a bit more plausible, but under a low enough pressure, the circular shape can't be held.

I had trouble with the .love file because of letter case problems in some library filenames. Maybe you can reproduce it if you try to run the .love file in a folder other than your app's.

Re: [solved] help with internal pressure simulation

Posted: Sat May 16, 2020 9:38 pm
by AndrewMicallef
Letter case problems huh... that sounds like the sort of thing I should fix now before it gets too out of hand.
I had thought require statements were not case sensitive. Im also using backslashes as path seperators in my requires. I guess that is probably bad practise too.

Re: [solved] help with internal pressure simulation

Posted: Sat May 16, 2020 10:47 pm
by zorg
yep, require expects module names and those are separated by dots... as for the casing, blame windows.

Re: [solved] help with internal pressure simulation

Posted: Sat May 16, 2020 11:15 pm
by AndrewMicallef
I'm over blaming windows for everything :P
I'm too old for that, easier just to accept it's flaws. Or how I learned to stop whining about windows and love Microsoft.

Does this fix the issue?

Re: [solved] help with internal pressure simulation

Posted: Sun May 17, 2020 9:35 am
by pgimeno
Now it works for me out of the box at least :)

Case sensitivity is not something to blame anyone for, it's a characteristic of the filesystem and it made sense in the times of CP/M that DOS then Windows inherit from. Linux supports case-insensitive filesystems too, for example. It's just that the default filesystems on Linux, BSD and others, as well as the PhysFS zip filesystem implementation, are case sensitive.
AndrewMicallef wrote: Sat May 16, 2020 9:38 pmIm also using backslashes as path seperators in my requires. I guess that is probably bad practise too.
If that were the case, it would not have worked at all :) They were forward slashes, which 'require' somehow gets away with.