Thrive Game Development

Development of the evolution game Thrive.
 
HomeHome  PortalPortal  CalendarCalendar  FAQFAQ  SearchSearch  MemberlistMemberlist  UsergroupsUsergroups  RegisterRegister  Log inLog in  
Welcome new and returning members!
If you're new, read around a bit before you post: the odds are we've already covered your suggestion.
If you want to join the development team, sign up and tell us why.
ADMIN is pleased to note that this marquee has finally been updated.
ADMIN reminds you that the Devblog is REQUIRED reading.
Currently: The Microbe Stage GUI is under heavy development
Log in
Username:
Password:
Log in automatically: 
:: I forgot my password
Quick Links
Website
/r/thrive
GitHub
FAQs
Wiki
New Posts
Search
 
 

Display results as :
 
Rechercher Advanced Search
Statistics
We have 1675 registered users
The newest registered user is dejo123

Our users have posted a total of 30851 messages in 1411 subjects
Who is online?
In total there are 2 users online :: 0 Registered, 0 Hidden and 2 Guests

None

Most users ever online was 443 on Sun Mar 17, 2013 5:41 pm
Latest topics
» THIS FORUM IS NOW OBSOLETE
by NickTheNick Sat Sep 26, 2015 10:26 pm

» To all the people who come here looking for thrive.
by NickTheNick Sat Sep 26, 2015 10:22 pm

» Build Error Code::Blocks / CMake
by crovea Tue Jul 28, 2015 5:28 pm

» Hello! I can translate in japanese
by tjwhale Thu Jul 02, 2015 7:23 pm

» On Leave (Offline thread)
by NickTheNick Wed Jul 01, 2015 12:20 am

» Devblog #14: A Brave New Forum
by NickTheNick Mon Jun 29, 2015 4:49 am

» Application for Programmer
by crovea Fri Jun 26, 2015 11:14 am

» Re-Reapplication
by The Creator Thu Jun 25, 2015 10:57 pm

» Application (programming)
by crovea Tue Jun 23, 2015 8:00 am

» Achieving Sapience
by MitochondriaBox Sun Jun 21, 2015 7:03 pm

» Microbe Stage GDD
by tjwhale Sat Jun 20, 2015 3:44 pm

» Application for Programmer/ Theorist
by tjwhale Wed Jun 17, 2015 9:56 am

» Application for a 3D Modeler.
by Kaiju4u Wed Jun 10, 2015 11:16 am

» Translator to Serbian here
by Simeartherist Sun Jun 07, 2015 6:36 am

» Presentation
by Othithu Tue Jun 02, 2015 10:38 am

» Application of Sorts
by crovea Sun May 31, 2015 5:06 pm

» want to contribute
by Renzope Sun May 31, 2015 12:58 pm

» Music List Thread (Post New Themes Here)
by Oliveriver Thu May 28, 2015 1:06 pm

» Application: English-Spanish translator
by Renzope Tue May 26, 2015 1:53 pm

» Want to be promoter or project manager
by TheBudderBros Sun May 24, 2015 9:00 pm


Share | 
 

 Cell Membrane

View previous topic View next topic Go down 
AuthorMessage
tjwhale
Theorist


Posts : 87
Reputation : 26
Join date : 2014-09-07

PostSubject: Cell Membrane   Tue Jan 13, 2015 12:23 pm

Hi,

I'm not really a programmer but I wanted to see if I could help. Sorry if this is a useless distraction!

So what we want is a membrane around a cell that looks quite "cell-like" which the player can click and drag to move around? Right?

This is what I could do.



So basically it just starts with a random set of points and then smooths them out. The longer you run the smoothing for the smoother it gets. I think it looks quite cell like.

The membrane goes green when it is very stretched which is useful in game.

I guess you could generate the set of points with some randomness. Run the smoothing (as much as you wanted). Then that is the shape of your cell.

If you get attacked in game your cell get's deformed and either tears (causing spillage) or doesn't. Then you run the smoothing again for a bit to get the new, deformed, shape.

It would be kind of cool to carry your battle scars with you.

I think this should work fine in 3D, it's just a case of using a 2D laplacian term.

To generate the intital set of points you could either

A) find the organelle furthest from the centre and pick some random points 2x as far as this or

B) project from the centre though some points on the outside of the hjex grid and put a point at double this distance.

Like this

Spoiler:
 

What do you guys think? It's just a suggestion, I have no idea how ogre works so sorry I can't implement this myself. Here is the code I was using (apologies for the quality)

Code:

import pygame
import math
import sys
import random
from pygame.locals import *

background_colour = (255,255,255)
(width, height) = (1000, 1000)

screen = pygame.display.set_mode((width, height))
screen.fill(background_colour)
pygame.display.set_caption('Membrane')
pygame.font.init()

class point:
    def __init__(self, angle):
        self.r = random.randint(200,300)
        self.angle = angle
        self.colour = (255,0,0)

    def display(self):
        pygame.draw.circle(screen, self.colour,
                           (int(500 + self.r*math.cos(self.angle)),int(500 + self.r*math.sin(self.angle))),
                           5)

    def smooth(self, number):
        laplacian = points[number-1].r - 2*points[number].r + points[(number + 1) % len(points)].r
        self.r += 0.01*laplacian
        if laplacian >= 5:
            self.colour = (0,255,0)
        elif laplacian <= 5:
            self.colour = (255,0,0)
        

points = []

max = 2*math.pi
count = 0

while count <= max:
    points.append(point(count))
    count += 0.01



running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == KEYDOWN:
               if event.key == K_ESCAPE:
                   running = False
               if event.key == K_SPACE:
                   for i in range(10):
                        points[50 + i].r += 100


    screen.fill(background_colour)


    for i in range(len(points)):
        points[i].display()
        points[i].smooth(i)      

    

    pygame.display.flip()


pygame.quit()
    
Back to top Go down
View user profile
tjwhale
Theorist


Posts : 87
Reputation : 26
Join date : 2014-09-07

PostSubject: Re: Cell Membrane   Wed Jan 14, 2015 2:17 pm

Three examples of a dynamic membrane encapsulating control points.



Code:


import pygame
import math
import sys
import random
from pygame.locals import *

background_colour = (255,255,255)
(width, height) = (1000, 1000)

screen = pygame.display.set_mode((width, height))
screen.fill(background_colour)
pygame.display.set_caption('Membrane')
pygame.font.init()

class point:
    def __init__(self, angle):
        self.r = random.randint(200,300)
        self.angle = angle
        self.colour = (255,0,0)

    def display(self):
        pygame.draw.circle(screen, self.colour,
                           (int(500 + self.r*math.cos(self.angle)),int(500 + self.r*math.sin(self.angle))),
                           5)

    def smooth(self, number):
        laplacian = points[number-1].r - 2*points[number].r + points[(number + 1) % len(points)].r
        self.r += 0.1*laplacian
        if laplacian >= 5:
            self.colour = (0,255,0)
        elif laplacian <= 5:
            self.colour = (255,0,0)

class controlpoint:
    def __init__(self):
        self.r = random.randint(100,400)
        self.angle = random.uniform(0, 2*math.pi - 0.2)
        self.colour = (0,0,255)

    def display(self):
        pygame.draw.circle(screen, (0,0,255),
                       (int(500 + self.r*math.cos(self.angle)),
                        int(500 + self.r*math.sin(self.angle))),
                           5)
        
        

points = []
controlpoints = []

maximum = 2*math.pi
count = 0

while count <= maximum:
    points.append(point(count))
    count += 0.01

for i in range(random.randint(5,10)):
    controlpoints.append(controlpoint())
    for j in range(len(points)):
        if points[j].angle >= (controlpoints[i].angle - 0.2) and points[j].angle <= (controlpoints[i].angle + 0.2):
            distance = controlpoints[i].r*1.5
            points[j].r = max(200,distance)



Hold = True
while Hold:
    for event in pygame.event.get():
            if event.type == KEYDOWN:
                   if event.key == K_SPACE:
                       Hold = False



running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == KEYDOWN:
               if event.key == K_ESCAPE:
                   running = False
               if event.key == K_SPACE:
                   for i in range(10):
                        points[50 + i].r += 100


    screen.fill(background_colour)


    for i in range(len(points)):
        points[i].display()
        points[i].smooth(i)

    for i in range(len(controlpoints)):
        controlpoints[i].display()

    

    pygame.display.flip()


pygame.quit()
    
Back to top Go down
View user profile
MitochondriaBox
Learner


Posts : 188
Reputation : 7
Join date : 2013-01-29
Age : 17
Location : Houston, Texas

PostSubject: Re: Cell Membrane   Wed Jan 14, 2015 6:42 pm

Is this intended to work alongside the idea of the cell's shape being based on the "body tiles" by being tightened a bit, or is it meant for amoeboid cells with looser membranes?
Back to top Go down
View user profile
tjwhale
Theorist


Posts : 87
Reputation : 26
Join date : 2014-09-07

PostSubject: Re: Cell Membrane   Wed Jan 14, 2015 7:58 pm

It's an attempt to generate a membrane that looks relatively biological around a set of points (which would be the organelles).

What do you think? Does it look "celley"?

This is the kind of thing you get for a more regular pattern of control nodes (organelles). This is how I would imagine a player microbe might look.

Spoiler:
 
Back to top Go down
View user profile
MitochondriaBox
Learner


Posts : 188
Reputation : 7
Join date : 2013-01-29
Age : 17
Location : Houston, Texas

PostSubject: Re: Cell Membrane   Wed Jan 14, 2015 9:57 pm

tjwhale wrote:
It's an attempt to generate a membrane that looks relatively biological around a set of points (which would be the organelles).

What do you think? Does it look "celley"?

This is the kind of thing you get for a more regular pattern of control nodes (organelles). This is how I would imagine a player microbe might look.

Spoiler:
 

Looks "celley" to me. Just wondering how it fits into the other developments in progress.

Other Developments:
 

Not sure if it's still being worked on, but, if it is, hopefully it can fit into this... Maybe the cell body hexes can be the set points?
Back to top Go down
View user profile
tjwhale
Theorist


Posts : 87
Reputation : 26
Join date : 2014-09-07

PostSubject: Re: Cell Membrane   Thu Jan 15, 2015 8:13 am

Basically that's the system the programmers are working on. They know what they are doing and that will probably work better.

I thought I'd try a different approach which is a bit more experimental. It's good programming practice for me and I think it works well enough.

Here is an example of body deformation movement (rippling) and what engulfment / feeding would look like.

Back to top Go down
View user profile
tjwhale
Theorist


Posts : 87
Reputation : 26
Join date : 2014-09-07

PostSubject: Re: Cell Membrane   Thu Jan 15, 2015 5:07 pm

Here's something in 3D, it's not quite perfect (you can see a hillarious number of points mapped to the pole) but it is in 3D and it does work.

Think of it like looking down on a dome. The redder it is the higher it is. The blue blobs are the organelles.



Code:


import pygame
import math
import sys
import random
from pygame.locals import *

background_colour = (255,255,255)
(width, height) = (1000, 1000)

screen = pygame.display.set_mode((width, height))
screen.fill(background_colour)
pygame.display.set_caption('Membrane')
pygame.font.init()

maxdist = 0

hold = True
while hold:
    for event in pygame.event.get():
            if event.type == KEYDOWN:
                   if event.key == K_SPACE:
                       hold = False


class controlpoint:
    def __init__(self):
        self.r = random.randint(200,300)
        self.phi = random.uniform(0, 2*math.pi - 0.2)
        self.psi = random.uniform(0,math.pi)
        self.x = self.r*math.cos(self.phi)*math.sin(self.psi)
        self.y = self.r*math.sin(self.psi)*math.sin(self.phi)
        self.z = self.r*math.sin(self.psi)
        self.colour = (0,0,255)

    def display(self):
        pygame.draw.circle(screen, self.colour,
                       (int(500 + self.x),
                        int(500 + self.y)),
                           10)

class point:
    def __init__(self, phi, psi):
        self.r = random.randint(maxdist-100,maxdist + 100)
        self.phi = phi
        self.psi = psi
        self.x = self.r*math.cos(self.phi)*math.sin(self.psi)
        self.y = self.r*math.sin(self.psi)*math.sin(self.phi)
        self.z = self.r*math.sin(self.psi)
        if self.z <= -0.01:
            self.colour = (0,0,0)
        else:
            self.colour = (255 - int(self.z/2),0,0)

    def display(self):
        pygame.draw.circle(screen, self.colour,
                       (int(500 + self.x),
                        int(500 + self.y)),
                           5)

    def smooth(self, i, j):
        laplacian = (points[(i-1) % sizephi][j].r +
                     points[(i+1) % sizephi][j].r +
                     points[i][(j+1) % sizepsi].r +
                     points[i][(j-1) % sizepsi].r -
                     points[i][j].r*4)
        self.r += 0.1*laplacian
        self.x = self.r*math.cos(self.phi)*math.sin(self.psi)
        self.y = self.r*math.sin(self.psi)*math.sin(self.phi)
        self.z = self.r*math.cos(self.psi)

    def checkdistance(self):
        for i in range(len(controlpoints)):
            if ((self.x - controlpoints[i].x)**2 +
                (self.y - controlpoints[i].y)**2 +
                (self.z - controlpoints[i].z)**2) <= 20000:
                self.r += 10
        

controlpoints = []
points = []

for i in range(0,10):
    controlpoints.append(controlpoint())
    

for i in range(len(controlpoints)):
    if controlpoints[i].r >= maxdist:
        maxdist = controlpoints[i].r

step = 1
a = 20
b = 10
sizephi = int(a*math.pi)
sizepsi = int(b*math.pi)

phic = 0
psic = 0

while phic <= a*math.pi:
    points.append([])
    psic = 0
    while psic <= b*math.pi:
        points[phic].append(point((float(phic)/b), (float(psic)/b)))
        psic += step
    phic += step
        
    
toggle = True
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == KEYDOWN:
               if event.key == K_ESCAPE:
                   running = False
               elif event.key == K_RCTRL:
                   toggle = not toggle

    screen.fill(background_colour)

    for i in range(len(controlpoints)):
        controlpoints[i].display()

    for i in range(len(points)):
        for j in range(len(points[i])):
            points[i][j].display()
            if toggle:
                points[i][j].smooth(i,j)
                points[i][j].checkdistance()


    pygame.display.flip()

pygame.quit()




Now with side view!!!

Spoiler:
 

Spoiler:
 

Spoiler:
 
Back to top Go down
View user profile
tjwhale
Theorist


Posts : 87
Reputation : 26
Join date : 2014-09-07

PostSubject: Re: Cell Membrane   Sat Jan 24, 2015 9:17 pm

So here's my next attempt.

This time it calculates the vertices and the triangles between them and fills them in. You can't see the organelles anymore (they are inside the mesh, at least I think it's a mesh now).

Also it starts with the points layed out in a cube which I think is better than the spherical distribution which I was using before (as that mapped too many points to the poles).

The first two runs are general examples. The third has all the organelles in a plane. You can see the outline turn into a puck pretty quickly.

The fourth run has the two green points fixed (as if the player had click and dragged them there) and the rest of the surface shapes around that. (there's a weird jump in the video, don't know what that is, apologies)

Anyway hope you like it.





Code:

I have tried to put comments in the code to hopefully make it easier to understand in case anyone is reading it.

#make a cube around the organelles which is then smoothed into place, compute all triangles in the mesh

#setup

import pygame
import math
import sys
import random
from pygame.locals import *
import time
start_time = time.time()

print 'Time : ' , time.time() - start_time

background_colour = (255,255,255)
(width, height) = (2000, 1000)

screen = pygame.display.set_mode((width, height))
screen.fill(background_colour)
pygame.display.set_caption('Membrane')
pygame.font.init()

#size is approximately how many vertices to use in total

size = 500
sidelength = int(math.sqrt(float(size) / 6))

print sidelength
truesize = 6*(sidelength**2)

print truesize

#useful vector functions

def distance(x,y,z,i,j,k):
    return math.sqrt((x-i)**2 + (y-j)**2 + (z-k)**2)

def cross(a, b):
    c = [a[1]*b[2] - a[2]*b[1],
         a[2]*b[0] - a[0]*b[2],
         a[0]*b[1] - a[1]*b[0]]

    return c

#define control points (which are the organelles)

class controlpoint:
    def __init__(self):
        self.r = random.randint(200,300)
        self.theta = random.uniform(0, 2*math.pi)
        self.phi = random.uniform(0, math.pi)
        self.x = self.r*math.sin(self.theta)*math.cos(self.phi)
        self.y = self.r*math.sin(self.theta)*math.sin(self.phi)
        self.z = self.r*math.cos(self.theta)
        self.colour = (0,0,255)

    def display(self):
        pygame.draw.circle(screen, self.colour,
                       (int(500 + self.x),
                        int(500 + self.y)),
                           10)

        pygame.draw.circle(screen, self.colour,
                       (int(1500 + self.x),
                        int(500 + self.z)),
                           10)
    

#define vertices

class vertex:
    def __init__(self, x,y,z):
        self.x = x
        self.y = y
        self.z = z
        self.r = distance(self.x,self.y,self.z,0,0,0)
        self.theta = math.acos(float(self.z)/self.r)
        self.phi = math.atan2(self.y,self.x)
        self.colour = (255,0,0)
        self.nbrs = []

    def display(self):
        pygame.draw.circle(screen, self.colour,
                       (int(500 + self.x),
                        int(500 + self.y)),
                           5)
        pygame.draw.circle(screen, self.colour,
                       (int(1500 + self.x),
                        int(500 + self.z)),
                           5)

# this is where the vertices get smoothed. They try sum the distances from their neighbours to the origin.
# then they subtract their own distance multiplied by the number of neighbours.
# this gives a measure of if they are above or below average. They then move to become more average

    def smooth(self):
        laplacian = 0
        for i in range(len(self.nbrs)):
            laplacian += self.nbrs[i].r
        laplacian -= len(self.nbrs)*self.r
        self.r += 0.1*laplacian
        self.x = self.r*math.sin(self.theta)*math.cos(self.phi)
        self.y = self.r*math.sin(self.theta)*math.sin(self.phi)
        self.z = self.r*math.cos(self.theta)

#if a vertx gets too close to an organelle is moves further away

    def checkdistance(self):
        for i in range(len(controlpoints)):
            if distance(self.x,self.y,self.z,controlpoints[i].x,controlpoints[i].y,controlpoints[i].z) <= 150:
                self.r += 5



vertices = []
controlpoints = []
triangles = []

maxdist = 0

#place some random organelles

for i in range(0,10):
    controlpoints.append(controlpoint())
controlpoints[0].r = 0
    

for i in range(len(controlpoints)):
    if controlpoints[i].r >= maxdist:
        maxdist = controlpoints[i].r

maxdist += 30


#compute the size of gaps between vertices

print 'maxdist', maxdist
gap = float(2*maxdist)/sidelength
print 'gap', gap

#put vertices on the top and bottom of the cube

for i in range(sidelength + 1):
    for j in range(sidelength + 1):
        vertices.append(vertex(-maxdist + i*gap, -maxdist + j*gap, -maxdist))
        vertices.append(vertex(-maxdist + i*gap, -maxdist + j*gap, +maxdist))

print 'top and bottom complete'
print 'Time : ' , time.time() - start_time

#put vertices on the sides of the cube

for i in range(sidelength):
    for j in range(sidelength - 1):
        vertices.append(vertex(-maxdist + i*gap , -maxdist, -maxdist + gap + j*gap))
        vertices.append(vertex(+maxdist - i*gap, +maxdist, -maxdist + gap + j*gap))
        vertices.append(vertex(+maxdist, -maxdist + i*gap , -maxdist + gap + j*gap))
        vertices.append(vertex(-maxdist, +maxdist - i*gap , -maxdist + gap + j*gap))

print 'sides complete'
print 'Time : ' , time.time() - start_time

#compute a list of neighbours for each vertex

for i in range(len(vertices)):
    for j in range(len(vertices)):
        if (distance(vertices[i].x,vertices[i].y, vertices[i].z, vertices[j].x, vertices[j].y, vertices[j].z)
            <= 1.1*gap):
            if (distance(vertices[i].x,vertices[i].y, vertices[i].z, vertices[j].x, vertices[j].y, vertices[j].z)
                >= 0.5*gap):
                vertices[i].nbrs.append(vertices[j])

print 'neighbours complete'
print 'Time : ' , time.time() - start_time

#compute a list of triangles which connect the vertices

for i in range(len(vertices)):
    for j in range(len(vertices[i].nbrs)):
        for k in range(j, len(vertices[i].nbrs)):
            vector1 = [vertices[i].nbrs[j].x - vertices[i].x,
                       vertices[i].nbrs[j].y - vertices[i].y,
                       vertices[i].nbrs[j].z - vertices[i].z]
            vector2 = [vertices[i].nbrs[k].x - vertices[i].x,
                       vertices[i].nbrs[k].y - vertices[i].y,
                       vertices[i].nbrs[k].z - vertices[i].z]
            crossproduct = cross(vector1, vector2)
            if crossproduct >= 1:
                triangles.append((vertices[i],vertices[i].nbrs[j],vertices[i].nbrs[k]))

print 'triangles complete'
print 'Time : ' , time.time() - start_time

#remove duplicates from the list of triangles

triangles = list(set(triangles))

print 'triangle filter complete'
print 'Time : ' , time.time() - start_time


#main loop        

toggle = False
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == KEYDOWN:
               if event.key == K_ESCAPE:
                   running = False
               elif event.key == K_RCTRL:
                   toggle = not toggle

    screen.fill(background_colour)

#draw the control points and the triangles

    for i in range(len(controlpoints)):
        controlpoints[i].display()

    for i in range(len(triangles)):
        pygame.draw.polygon(screen, (0,0,100),
                       [[int(500 + triangles[i][0].x),int(500 + triangles[i][0].y)],
                        [int(500 + triangles[i][1].x),int(500 + triangles[i][1].y)],
                        [int(500 + triangles[i][2].x),int(500 + triangles[i][2].y)]],
                           0)
        pygame.draw.polygon(screen, (0,0,100),
                       [[int(1500 + triangles[i][0].x),int(500 + triangles[i][0].z)],
                        [int(1500 + triangles[i][1].x),int(500 + triangles[i][1].z)],
                        [int(1500 + triangles[i][2].x),int(500 + triangles[i][2].z)]],
                           0)

# draw and update the vertices
        

    for i in range(len(vertices)):
        vertices[i].display()
        if toggle:
            vertices[i].smooth()
            vertices[i].r = vertices[i].r*0.99
            vertices[i].checkdistance()


# lock two of the vertices in place

    vertices[27].r = 600
    vertices[27].colour = (0,255,0)
    vertices[29].r = 600
    vertices[29].colour = (0,255,0)      


    pygame.display.flip()

pygame.quit()
            
        



Back to top Go down
View user profile
moopli
Developer


Posts : 318
Reputation : 56
Join date : 2013-09-30
Age : 21
Location : hanging from the chandelier

PostSubject: Re: Cell Membrane   Sun Jan 25, 2015 12:17 am

Hey, this is all really, really great work. I managed to get your latest prototype working (the previous ones I couldn't for some reason), and I fiddled around with it and added some more controls. Mouse can drag organelles, lshift disables vertex rendering leaving just the mesh, enter will randomize organelle positions (it actually replaces them with new ones, but same diff), and rshift will toggle the pinned constraint vertices.

Code:


#I have tried to put comments in the code to hopefully make it easier to understand in case anyone is reading it.

#make a cube around the organelles which is then smoothed into place, compute all triangles in the mesh

#setup

import pygame
import math
import sys
import random
from pygame.locals import *
import time
from pygame.mouse import get_pos
start_time = time.time()

print 'Time : ' , time.time() - start_time

# scale scales the canvas, essentially
scale = 0.5
(width, height) = (int(2000 * scale), int(1000 * scale))

# alpha values for making rendering smoother yay
pointalpha = 130
linealpha = 130
bgalpha = 130

background_colour = (255,255,255, bgalpha)
bg = pygame.Surface((width, height), flags = pygame.SRCALPHA)

#size is approximately how many vertices to use in total

size = 500
sidelength = int(math.sqrt(float(size) / 6))

print sidelength
truesize = 6*(sidelength**2)

print truesize

def within(a,b,lim=5):
    return abs(a-b) < lim

#useful vector functions

def distance(x,y,z,i,j,k):
    return math.sqrt((x-i)**2 + (y-j)**2 + (z-k)**2)

def cross(a, b):
    c = [a[1]*b[2] - a[2]*b[1],
         a[2]*b[0] - a[0]*b[2],
         a[0]*b[1] - a[1]*b[0]]

    return c

#define control points (which are the organelles)

class controlpoint:
    def __init__(self):
        self.r = random.randint(200,300)
        self.theta = random.uniform(0, 2*math.pi)
        self.phi = random.uniform(0, math.pi)
        self.x = self.r*math.sin(self.theta)*math.cos(self.phi)
        self.y = self.r*math.sin(self.theta)*math.sin(self.phi)
        self.z = self.r*math.cos(self.theta)
        self.colour = (0,0,255,pointalpha)
        self.clicked = False

    def display(self):
        pygame.draw.circle(bg, self.colour,
                       (int(scale * (500 + self.x)),
                        int(scale * (500 + self.y))),
                           int(scale * 10))

        pygame.draw.circle(bg, self.colour,
                       (int(scale * (1500 + self.x)),
                        int(scale * (500 + self.z))),
                           int(scale * 10))

    def handleMouse(self, x, y, clicking_edge = False):
        # convert to internal render coords
        x /= scale
        y /= scale
        y -= 500
        mouseray = [None, None, None]
        # convert to effective 3D coordinates
        if x > 1000: # if click was on side view (ie, on xz plane)
            x -= 1500
            mouseray[0] = x
            mouseray[2] = y
        else: # click was on xy plane
            x -= 500
            mouseray[0] = x
            mouseray[1] = y
        # so mouseray holds a line, where the None coordinate is allowed to vary
        if clicking_edge: # runs if this was called on mouse-click-down
            if within(mouseray[0], self.x):
                if mouseray[1] is not None and within(mouseray[1], self.y):
                    self.clicked = True
                elif mouseray[2] is not None and within(mouseray[2], self.z):
                    self.clicked = True
        if self.clicked:
            self.x = mouseray[0]
            if mouseray[1]: self.y = mouseray[1]
            elif mouseray[2]: self.z = mouseray[2]


#define vertices

class vertex:
    def __init__(self, x,y,z):
        self.x = x
        self.y = y
        self.z = z
        self.r = distance(self.x,self.y,self.z,0,0,0)
        self.theta = math.acos(float(self.z)/self.r)
        self.phi = math.atan2(self.y,self.x)
        self.colour = (255,0,0, pointalpha)
        self.nbrs = []

    def display(self):
        pygame.draw.circle(bg, self.colour,
                       (int(scale * (500 + self.x)),
                        int(scale * (500 + self.y))),
                           int(scale * 5))
        pygame.draw.circle(bg, self.colour,
                       (int(scale * (1500 + self.x)),
                        int(scale * (500 + self.z))),
                           int(scale * 5))

# this is where the vertices get smoothed. They try sum the distances from their neighbours to the origin.
# then they subtract their own distance multiplied by the number of neighbours.
# this gives a measure of if they are above or below average. They then move to become more average

    def smooth(self):
        laplacian = 0
        for i in range(len(self.nbrs)):
            laplacian += self.nbrs[i].r
        laplacian -= len(self.nbrs)*self.r
        self.r += 0.1*laplacian
        self.x = self.r*math.sin(self.theta)*math.cos(self.phi)
        self.y = self.r*math.sin(self.theta)*math.sin(self.phi)
        self.z = self.r*math.cos(self.theta)

#if a vertx gets too close to an organelle is moves further away

    def checkdistance(self):
        for pt in controlpoints:
            if distance(self.x,self.y,self.z,pt.x,pt.y,pt.z) <= 150:
                self.r += 5

vertices = []
controlpoints = []
triangles = []

maxdist = 0

#place some random organelles

def makeOrganelles(n):
    controlpoints[:] = [] # clear contents of the list
    for i in range(0, n):
        controlpoints.append(controlpoint())
    controlpoints[0].r = 0

makeOrganelles(10)

for i in range(len(controlpoints)):
    if controlpoints[i].r >= maxdist:
        maxdist = controlpoints[i].r

maxdist += 30

#compute the size of gaps between vertices

print 'maxdist', maxdist
gap = float(2*maxdist)/sidelength
print 'gap', gap

#put vertices on the top and bottom of the cube

for i in range(sidelength + 1):
    for j in range(sidelength + 1):
        vertices.append(vertex(-maxdist + i*gap, -maxdist + j*gap, -maxdist))
        vertices.append(vertex(-maxdist + i*gap, -maxdist + j*gap, +maxdist))

print 'top and bottom complete'
print 'Time : ' , time.time() - start_time

#put vertices on the sides of the cube

for i in range(sidelength):
    for j in range(sidelength - 1):
        vertices.append(vertex(-maxdist + i*gap , -maxdist, -maxdist + gap + j*gap))
        vertices.append(vertex(+maxdist - i*gap, +maxdist, -maxdist + gap + j*gap))
        vertices.append(vertex(+maxdist, -maxdist + i*gap , -maxdist + gap + j*gap))
        vertices.append(vertex(-maxdist, +maxdist - i*gap , -maxdist + gap + j*gap))

print 'sides complete'
print 'Time : ' , time.time() - start_time

#compute a list of neighbours for each vertex

for i in range(len(vertices)):
    for j in range(len(vertices)):
        if (distance(vertices[i].x,vertices[i].y, vertices[i].z, vertices[j].x, vertices[j].y, vertices[j].z)
            <= 1.1*gap):
            if (distance(vertices[i].x,vertices[i].y, vertices[i].z, vertices[j].x, vertices[j].y, vertices[j].z)
                >= 0.5*gap):
                vertices[i].nbrs.append(vertices[j])

print 'neighbours complete'
print 'Time : ' , time.time() - start_time

#compute a list of triangles which connect the vertices

for i in range(len(vertices)):
    for j in range(len(vertices[i].nbrs)):
        for k in range(j, len(vertices[i].nbrs)):
            vector1 = [vertices[i].nbrs[j].x - vertices[i].x,
                       vertices[i].nbrs[j].y - vertices[i].y,
                       vertices[i].nbrs[j].z - vertices[i].z]
            vector2 = [vertices[i].nbrs[k].x - vertices[i].x,
                       vertices[i].nbrs[k].y - vertices[i].y,
                       vertices[i].nbrs[k].z - vertices[i].z]
            crossproduct = cross(vector1, vector2)
            if crossproduct >= 1:
                triangles.append((vertices[i],vertices[i].nbrs[j],vertices[i].nbrs[k]))

print 'triangles complete'
print 'Time : ' , time.time() - start_time

#remove duplicates from the list of triangles

triangles = list(set(triangles))

print 'triangle filter complete'
print 'Time : ' , time.time() - start_time

screen = pygame.display.set_mode((width, height))
screen.fill(background_colour)
pygame.display.set_caption('Membrane')
pygame.font.init()

def drawTriangle(colour, triangle, xoff=0, yoff=0):
    pygame.draw.polygon(bg, colour,
                       [[int(scale * (xoff + triangle[0].x)),int(scale * (yoff + triangle[0].y))],
                        [int(scale * (xoff + triangle[1].x)),int(scale * (yoff + triangle[1].y))],
                        [int(scale * (xoff + triangle[2].x)),int(scale * (yoff + triangle[2].y))]],
                           1)

def drawTriangle2(colour, triangle, xoff=0, yoff=0):
    pygame.draw.polygon(bg, colour,
                       [[int(scale * (xoff + triangle[0].x)),int(scale * (yoff + triangle[0].z))],
                        [int(scale * (xoff + triangle[1].x)),int(scale * (yoff + triangle[1].z))],
                        [int(scale * (xoff + triangle[2].x)),int(scale * (yoff + triangle[2].z))]],
                           1)

#main loop        

toggle = False
lock = True
showVertices = True

running = True

while running:
    mousepos = get_pos()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                running = False
            elif event.key == K_RCTRL:
                toggle = not toggle
            elif event.key == K_RSHIFT:
                lock = not lock
            elif event.key == K_RETURN:
                makeOrganelles(10)
            elif event.key == K_LSHIFT:
                showVertices = not showVertices
            elif event.key == K_LCTRL:
                for pt in controlpoints:
                    pt.clicked = False
        elif event.type == MOUSEBUTTONDOWN:
            for pt in controlpoints:
                pt.handleMouse(*mousepos, clicking_edge = True)
        elif event.type == MOUSEBUTTONUP:
            for pt in controlpoints:
                pt.clicked = False

    bg.fill(background_colour)

    #draw the control points and the triangles

    for pt in controlpoints:
        pt.handleMouse(*mousepos)
        pt.display()

    for i in range(len(triangles)):
        drawTriangle((0,0,100,linealpha), triangles[i], xoff=500, yoff=500)
        drawTriangle2((0,0,100,linealpha), triangles[i], xoff=1500, yoff=500)

    # draw and update the vertices
        

    for i in range(len(vertices)):
        if showVertices: vertices[i].display()
        if toggle:
            vertices[i].smooth()
            vertices[i].r = vertices[i].r*0.99
            vertices[i].checkdistance()


    if lock:
        # lock two of the vertices in place
        vertices[27].r = 600
        vertices[27].colour = (0,255,0)
        vertices[29].r = 600
        vertices[29].colour = (0,255,0)      

    screen.blit(bg, (0,0))
    pygame.display.flip()
    time.sleep(0.030)

pygame.quit()

Anyway, you're worried this is all unappreciated. It isn't, it's a great prototype, and you're showing awesome initiative with all this work. It's also really useful, cuz the current method you're using to mesh is what we'll likely be doing. Even if we use a different set of control parameters (for example, a user-defined spline that gets converted to 3D somehow), we'll still be doing the mesh-morphing thing you've figured out, so it's good to see that it will work. What other ideas do you have for what you could do with this? Is there any way you can force the neighborhoods of constrained points to be smooth?
Back to top Go down
View user profile
tjwhale
Theorist


Posts : 87
Reputation : 26
Join date : 2014-09-07

PostSubject: Re: Cell Membrane   Sun Jan 25, 2015 8:01 am

Great! That's awesome. Thanks for all the encouragement I really appreciate it.

The stuff you've done to the program is really cool too, it's works really well now and looks beautiful!

I'll have a think about smoothing constrained points. It's not really a problem if the constraints are close to where the surface wants to be. It only looks sharp if you drag them really far out. I suppose it would be partially solved by having a "volume" control as well as a click and drag. That way if you wanted it bigger you would just pump it up a bit and only if you want specific shapes would you click and drag vertices around.

Another way would be to have the user grab a bigger chunk. So if a vertex is set to 600 then all it's neighbours are constrained to 575-625.

I tried it, it's a bit better

Code:


if lock:
        # lock two of the vertices in place       
        vertices[27].r = 600
        vertices[27].colour = (0,255,0)
        for i in range(len(vertices[27].nbrs)):
            vertices[27].nbrs[i].r = 575


Another option is to let the user put in "shadow organelles" which morph the surface more naturally and smoothly. That way if you want it sharp you can move the vertices, if you want it soft you put in a shadow oragnelle.
Back to top Go down
View user profile
tjwhale
Theorist


Posts : 87
Reputation : 26
Join date : 2014-09-07

PostSubject: Re: Cell Membrane   Sun Jan 25, 2015 1:43 pm

Taking this video as inspiration



I tried adding movement and some lunging behavior. I think it looks quite good, it's not as dynamic as the real cell. I'd like a more focussed / pointy lunge aswell but that is hard to get right (with the system I am currently using).





Code:


#This code is a tiny bit different from what I used to make the video but basically the same.

#I have tried to put comments in the code to hopefully make it easier to understand in case anyone is reading it.

#make a cube around the organelles which is then smoothed into place, compute all triangles in the mesh

#setup

import pygame
import math
import sys
import random
from pygame.locals import *
import time
from pygame.mouse import get_pos
start_time = time.time()

print 'Time : ' , time.time() - start_time

# scale scales the canvas, essentially
scale = 0.5
(width, height) = (int(2000 * scale), int(1000 * scale))

centre = [500,500]
movevector = [0,0,0]
normalisedmovevector = [0,0,0]

# alpha values for making rendering smoother yay
pointalpha = 130
linealpha = 130
bgalpha = 130

background_colour = (255,255,255, bgalpha)
bg = pygame.Surface((width, height), flags = pygame.SRCALPHA)

#size is approximately how many vertices to use in total

size = 500
sidelength = int(math.sqrt(float(size) / 6))

print sidelength
truesize = 6*(sidelength**2)

print truesize

def within(a,b,lim=5):
    return abs(a-b) < lim

#useful vector functions

def distance(x,y,z,i,j,k):
    return math.sqrt((x-i)**2 + (y-j)**2 + (z-k)**2)

def cross(a, b):
    c = [a[1]*b[2] - a[2]*b[1],
         a[2]*b[0] - a[0]*b[2],
         a[0]*b[1] - a[1]*b[0]]

    return c

def dot(a, b):
    c = a[0]*b[0] + a[1]*b[1] + a[2]*b[2]
    return c

#define control points (which are the organelles)

class controlpoint:
    def __init__(self):
        self.r = random.randint(200,300)
        self.theta = random.uniform(0, 2*math.pi)
        self.phi = random.uniform(0, math.pi)
        self.x = self.r*math.sin(self.theta)*math.cos(self.phi)
        self.y = self.r*math.sin(self.theta)*math.sin(self.phi)
        self.z = self.r*math.cos(self.theta)
        self.colour = (0,0,255,pointalpha)
        self.clicked = False

    def display(self):
        pygame.draw.circle(bg, self.colour,
                       (int(scale * (centre[0] + self.x)),
                        int(scale * (centre[1] + self.y))),
                           int(scale * 10))




#define vertices

class vertex:
    def __init__(self, x,y,z):
        self.x = x
        self.y = y
        self.z = z
        self.r = distance(self.x,self.y,self.z,0,0,0)
        self.theta = math.acos(float(self.z)/self.r)
        self.phi = math.atan2(self.y,self.x)
        self.colour = (255,0,0, pointalpha)
        self.nbrs = []

    def display(self):
        pygame.draw.circle(bg, self.colour,
                       (int(scale * (centre[0] + self.x)),
                        int(scale * (centre[1] + self.y))),
                           int(scale * 5))

# this is where the vertices get smoothed. They try sum the distances from their neighbours to the origin.
# then they subtract their own distance multiplied by the number of neighbours.
# this gives a measure of if they are above or below average. They then move to become more average

    def smooth(self):
        laplacian = 0
        for i in range(len(self.nbrs)):
            laplacian += self.nbrs[i].r
        laplacian -= len(self.nbrs)*self.r
        self.r += 0.1*laplacian
        self.x = self.r*math.sin(self.theta)*math.cos(self.phi)
        self.y = self.r*math.sin(self.theta)*math.sin(self.phi)
        self.z = self.r*math.cos(self.theta)
        self.norm = distance(self.x,self.y,self.z,0,0,0)

#if a vertx gets too close to an organelle is moves further away

    def checkdistance(self):
        for pt in controlpoints:
            if distance(self.x,self.y,self.z,pt.x,pt.y,pt.z) <= 150:
                self.r += 20

    def lunge(self):
        self.r += 20*(dot([self.x, self.y, self.z], normalisedmovevector)/self.norm)**5

        

vertices = []
controlpoints = []
triangles = []

maxdist = 0

#place some random organelles

def makeOrganelles(n):
    controlpoints[:] = [] # clear contents of the list
    for i in range(0, n):
        controlpoints.append(controlpoint())
    controlpoints[0].r = 0

makeOrganelles(10)

for i in range(len(controlpoints)):
    if controlpoints[i].r >= maxdist:
        maxdist = controlpoints[i].r

maxdist += 30

#compute the size of gaps between vertices

print 'maxdist', maxdist
gap = float(2*maxdist)/sidelength
print 'gap', gap

#put vertices on the top and bottom of the cube

for i in range(sidelength + 1):
    for j in range(sidelength + 1):
        vertices.append(vertex(-maxdist + i*gap, -maxdist + j*gap, -maxdist))
        vertices.append(vertex(-maxdist + i*gap, -maxdist + j*gap, +maxdist))

print 'top and bottom complete'
print 'Time : ' , time.time() - start_time

#put vertices on the sides of the cube

for i in range(sidelength):
    for j in range(sidelength - 1):
        vertices.append(vertex(-maxdist + i*gap , -maxdist, -maxdist + gap + j*gap))
        vertices.append(vertex(+maxdist - i*gap, +maxdist, -maxdist + gap + j*gap))
        vertices.append(vertex(+maxdist, -maxdist + i*gap , -maxdist + gap + j*gap))
        vertices.append(vertex(-maxdist, +maxdist - i*gap , -maxdist + gap + j*gap))

print 'sides complete'
print 'Time : ' , time.time() - start_time

#compute a list of neighbours for each vertex

for i in range(len(vertices)):
    for j in range(len(vertices)):
        if (distance(vertices[i].x,vertices[i].y, vertices[i].z, vertices[j].x, vertices[j].y, vertices[j].z)
            <= 1.1*gap):
            if (distance(vertices[i].x,vertices[i].y, vertices[i].z, vertices[j].x, vertices[j].y, vertices[j].z)
                >= 0.5*gap):
                vertices[i].nbrs.append(vertices[j])

print 'neighbours complete'
print 'Time : ' , time.time() - start_time

#compute a list of triangles which connect the vertices

for i in range(len(vertices)):
    for j in range(len(vertices[i].nbrs)):
        for k in range(j, len(vertices[i].nbrs)):
            vector1 = [vertices[i].nbrs[j].x - vertices[i].x,
                       vertices[i].nbrs[j].y - vertices[i].y,
                       vertices[i].nbrs[j].z - vertices[i].z]
            vector2 = [vertices[i].nbrs[k].x - vertices[i].x,
                       vertices[i].nbrs[k].y - vertices[i].y,
                       vertices[i].nbrs[k].z - vertices[i].z]
            crossproduct = cross(vector1, vector2)
            if crossproduct >= 1:
                triangles.append((vertices[i],vertices[i].nbrs[j],vertices[i].nbrs[k]))

print 'triangles complete'
print 'Time : ' , time.time() - start_time

#remove duplicates from the list of triangles

triangles = list(set(triangles))

print 'triangle filter complete'
print 'Time : ' , time.time() - start_time

screen = pygame.display.set_mode((width, height))
screen.fill(background_colour)
pygame.display.set_caption('Membrane')
pygame.font.init()

def drawTriangle(colour, triangle, xoff=0, yoff=0):
    pygame.draw.polygon(bg, colour,
                       [[int(scale * (xoff + triangle[0].x)),int(scale * (yoff + triangle[0].y))],
                        [int(scale * (xoff + triangle[1].x)),int(scale * (yoff + triangle[1].y))],
                        [int(scale * (xoff + triangle[2].x)),int(scale * (yoff + triangle[2].y))]],
                           1)


#main loop        

toggle = False
lock = True
showVertices = True
counter = 0
off = 7000
on = 11000

running = True

while running:
    mousepos = get_pos()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                running = False
            elif event.key == K_RCTRL:
                toggle = not toggle
            elif event.key == K_RSHIFT:
                lock = not lock
            elif event.key == K_RETURN:
                makeOrganelles(10)
            elif event.key == K_LSHIFT:
                showVertices = not showVertices
            elif event.key == K_LCTRL:
                for pt in controlpoints:
                    pt.clicked = False


    bg.fill(background_colour)

    mousepos = pygame.mouse.get_pos()
    movevector[0] = (1/scale)*mousepos[0] - centre[0]
    movevector[1] = (1/scale)*mousepos[1] - centre[1]
    movevector[2] = 0
    normmovevector = max(0.1,distance(movevector[0], movevector[1], movevector[2], 0, 0, 0))
    normalisedmovevector[0] = movevector[0]/normmovevector
    normalisedmovevector[1] = movevector[1]/normmovevector
    normalisedmovevector[2] = 0
    if counter <= off:
        centre[0] += 0.01*movevector[0]
        centre[1] += 0.01*movevector[1]
    else:
        centre[0] += 0.002*movevector[0]
        centre[1] += 0.002*movevector[1]
        

    #draw the control points and the triangles

    for pt in controlpoints:
        pt.display()

    for i in range(len(triangles)):
        drawTriangle((0,0,100,linealpha), triangles[i], xoff=centre[0], yoff=centre[1])

    # draw and update the vertices
        

    for i in range(len(vertices)):
        if showVertices: vertices[i].display()
        if toggle:
            vertices[i].smooth()
            vertices[i].r = vertices[i].r*0.99
            vertices[i].checkdistance()
            if counter <= off:
                vertices[i].lunge()
            counter += 1
            if counter >= on:
                counter = 0




    screen.blit(bg, (0,0))
    pygame.display.flip()
    time.sleep(0.030)

pygame.quit()


Back to top Go down
View user profile
moopli
Developer


Posts : 318
Reputation : 56
Join date : 2013-09-30
Age : 21
Location : hanging from the chandelier

PostSubject: Re: Cell Membrane   Sun Jan 25, 2015 8:38 pm

So after playing around with it a bit, i see too much pinching going on in the trailing edge. I think, to get the lunging behaviour you want, you should move the organelles instead, and let the membrane naturally flow towards the changing equilibrium position, instead of making the membrane itself do the lunging.

Maybe set the organelles up as their own dynamical system, using a linear attraction term and inverse-square repulsion (or similar), which should keep them constantly tumbling past each other, but never too close. Then, have a time-varying attraction-to-mouse term, which falls off with distance. This is the tricky bit I suspect, since you want the microbe to not just shift towards the attractant, but lunge, which requires one or two control points to move substantially faster than the rest at times.
Back to top Go down
View user profile
tjwhale
Theorist


Posts : 87
Reputation : 26
Join date : 2014-09-07

PostSubject: Re: Cell Membrane   Mon Jan 26, 2015 6:38 am

That sounds cool.

One way I guess is to give the organelles different masses. So the nucleus can be big and then that is the center from which the mesh is measured. Then there are other smaller organelles which move around a lot quicker. They would lunge first towards something and then the bulky big organelles would be dragged along behind them.

I'll have a go later at getting it working.
Back to top Go down
View user profile
tjwhale
Theorist


Posts : 87
Reputation : 26
Join date : 2014-09-07

PostSubject: Re: Cell Membrane   Mon Jan 26, 2015 3:11 pm

Moopli your suggestion worked pretty well. I rewrote a lot of the code to work with vectors (as that made thinking about the formulas a lot easier).

This verison makes half as many triangles, which is weird. Also I haven't got the attraction effect decreasing if the mouse is further away, in fact it increases!

You can see all the little organelles move around quickly and they drag the big nucleus behind them. The mesh is always centered on the nucleus.



Code:




import pygame
import math
import sys
import random
from pygame.locals import *
import time
from pygame.mouse import get_pos
start_time = time.time()

# scale scales the canvas, essentially
scale = 0.5
(width, height) = (int(2000 * scale), int(1000 * scale))

# alpha values for making rendering smoother yay
pointalpha = 130
linealpha = 130
bgalpha = 130

background_colour = (255,255,255, bgalpha)
bg = pygame.Surface((width, height), flags = pygame.SRCALPHA)

screen = pygame.display.set_mode((width, height))
screen.fill(background_colour)
pygame.display.set_caption('Membrane')
pygame.font.init()

# I wrote a vector mathematics class just to make things easier

class Vector:
    'Represents a 2D vector.'
    def __init__(self, x = 0, y = 0, z = 0):
        self.x = float(x)
        self.y = float(y)
        self.z = float(z)
        
    def __add__(self, val):
        return Vector(self.x + val.x, self.y + val.y, self.z + val.z )
    
    def __sub__(self,val):
        return Vector(self.x - val.x, self.y - val.y, self.z - val.z )

    def __div__(self, val):
        return Vector(self.x / val, self.y / val, self.z / val)
    
    def __mul__(self, val):
        return Vector( self.x * val, self.y * val, self.z*val)

    def normalise(self):
        norma = norm(self)
        self = self/norma
        

def dot(a,b):
    return a.x*b.x + a.y*b.y + a.z*b.z

def cross(a,b):
    return Vector(a.y*b.z - a.z*b.y, a.x*b.z - a.z*b.x, a.x*b.y - a.y*b.x)

def norm(a):
    value = math.sqrt(a.x**2 + a.y**2 + a.z**2)
    if abs(value) >= 0.01:
        return value
    else:
        return 0.01

def distance(a,b):
    return math.sqrt((a.x - b.x)**2 + (a.y - b.y)**2 + (a.z - b.z)**2)

def tocartesian(a):
    return Vector(a.x*math.sin(a.y)*math.cos(a.z),
                a.x*math.sin(a.y)*math.sin(a.z),
                a.x*math.cos(a.y))

def tospherical(a):
    return Vector(norm(a), math.acos(a.z/norm(a)), math.atan2(a.y,a.x))

def direction(a,b):
    return Vector(b.x - a.x, b.y - a.y, b.z - a.z)

def drawTriangle(colour, triangle, xoff=0, yoff=0):
    pygame.draw.polygon(bg, colour,
                       [[int(scale * (xoff + triangle[0].pos.x)),int(scale * (yoff + triangle[0].pos.y))],
                        [int(scale * (xoff + triangle[1].pos.x)),int(scale * (yoff + triangle[1].pos.y))],
                        [int(scale * (xoff + triangle[2].pos.x)),int(scale * (yoff + triangle[2].pos.y))]],
                           1)



#Setup

size = 500
sidelength = int(math.sqrt(float(size) / 6))

print sidelength, 'sidelength'
truesize = 6*(sidelength**2)

print truesize. 'truesize'

def within(a,b,lim=5):
    return abs(a-b) < lim

#organelles

class controlpoint:
    def __init__(self):
        self.pos = Vector(random.randint(300,700),random.randint(300,700),random.randint(-200,200))
        self.mass = random.randint(5,10)
        self.spos = tospherical(self.pos)
        self.colour = (0,0,255)
        self.norm = norm(self.pos)

    def display(self):
        pygame.draw.circle(bg, self.colour,
                    (int(scale * (self.pos.x)),
                    int(scale * (self.pos.y))),
                    int(scale * self.mass))

    def move(self):
        threshold = 150
        movement = Vector(0,0,0)
        for i in range(len(controlpoints)):
            towards = direction(self.pos, controlpoints[i].pos)
            movement += (towards*(1 - (1 / norm(towards / threshold))**2))*controlpoints[i].mass
        self.pos += movement*0.001
        self.norm = norm(self.pos)


#define vertices

class vertex:
    def __init__(self, x,y,z):
        self.pos = Vector(x,y,z)
        self.r = tospherical(self.pos).x
        self.colour = (255,0,0, pointalpha)
        self.nbrs = []

    def display(self):
        pygame.draw.circle(bg, self.colour,
                       (int(scale * (centre[0] + self.pos.x)),
                        int(scale * (centre[1] + self.pos.y))),
                           int(scale * 5))

# this is where the vertices get smoothed. They try sum the distances from their neighbours to the origin.
# then they subtract their own distance multiplied by the number of neighbours.
# this gives a measure of if they are above or below average. They then move to become more average

    def smooth(self):
        laplacian = 0
        spherical = tospherical(self.pos)
        for i in range(len(self.nbrs)):
            laplacian += self.nbrs[i].r
        laplacian -= len(self.nbrs)*self.r
        spherical.x += 0.1*laplacian
        self.r = spherical.x
        self.pos = tocartesian(spherical)
        self.norm = norm(self.pos)


#if a vertx gets too close to an organelle is moves further away

    def checkdistance(self):
        truelocation = self.pos + Vector(centre[0],centre[1],centre[2])
        for pt in controlpoints:
            if distance(truelocation, pt.pos) <= 100:
                spherical = tospherical(self.pos)
                spherical.x += 30
                self.pos = tocartesian(spherical)


#initialise
        

vertices = []
controlpoints = []
triangles = []

#place some random organelles

def makeOrganelles(n):
    controlpoints[:] = [] # clear contents of the list
    for i in range(0, n):
        controlpoints.append(controlpoint())
        controlpoints[0].pos = Vector(500,500,0)
        controlpoints[0].mass = 20

makeOrganelles(10)

maxdist = 0

for i in range(len(controlpoints)):
    if distance(controlpoints[i].pos, controlpoints[0].pos) >= maxdist:
        maxdist = distance(controlpoints[i].pos, controlpoints[0].pos)

maxdist += 30

#compute the size of gaps between vertices

print 'maxdist', maxdist
gap = float(2*maxdist)/sidelength
print 'gap', gap

#put vertices on the top and bottom of the cube

for i in range(sidelength + 1):
    for j in range(sidelength + 1):
        vertices.append(vertex(-maxdist + i*gap, -maxdist + j*gap, -maxdist))
        vertices.append(vertex(-maxdist + i*gap, -maxdist + j*gap, +maxdist))

print 'top and bottom complete'
print 'Time : ' , time.time() - start_time

#put vertices on the sides of the cube

for i in range(sidelength):
    for j in range(sidelength - 1):
        vertices.append(vertex(-maxdist + i*gap , -maxdist, -maxdist + gap + j*gap))
        vertices.append(vertex(+maxdist - i*gap, +maxdist, -maxdist + gap + j*gap))
        vertices.append(vertex(+maxdist, -maxdist + i*gap , -maxdist + gap + j*gap))
        vertices.append(vertex(-maxdist, +maxdist - i*gap , -maxdist + gap + j*gap))

print 'sides complete'
print 'Time : ' , time.time() - start_time

#compute a list of neighbours for each vertex

for i in range(len(vertices)):
    for j in range(len(vertices)):
        if (distance(vertices[i].pos,vertices[j].pos)
            <= 1.1*gap):
            if (distance(vertices[i].pos, vertices[j].pos)
                >= 0.5*gap):
                vertices[i].nbrs.append(vertices[j])


print 'neighbours complete'
print 'Time : ' , time.time() - start_time

#compute a list of triangles which connect the vertices

for i in range(len(vertices)):
    for j in range(len(vertices[i].nbrs)):
        for k in range(j, len(vertices[i].nbrs)):
            vector1 = direction( vertices[i].pos, vertices[i].nbrs[j].pos )
            vector2 = direction( vertices[i].pos, vertices[i].nbrs[k].pos )
            crossproduct = cross(vector1, vector2)
            if norm(crossproduct) >= 1:
                triangles.append((vertices[i],vertices[i].nbrs[j],vertices[i].nbrs[k]))

print 'triangles complete'
print 'Time : ' , time.time() - start_time

#remove duplicates from the list of triangles

triangles = list(set(triangles))

print 'triangle filter complete'
print 'Time : ' , time.time() - start_time

print len(triangles), 'triangles'

    
counter = 0
running = True
toggle = False
lock = True
showVertices = True
while running:
    mousepos = get_pos()
    vecmousepos = Vector(mousepos[0], mousepos[1], 0)
    waytogo = vecmousepos - (controlpoints[0].pos * scale)
    centre = [controlpoints[0].pos.x, controlpoints[0].pos.y, controlpoints[0].pos.z]

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                running = False
            elif event.key == K_RCTRL:
                toggle = not toggle
            elif event.key == K_RSHIFT:
                lock = not lock
            elif event.key == K_RETURN:
                makeOrganelles(10)
            elif event.key == K_LSHIFT:
                showVertices = not showVertices
            elif event.key == K_LCTRL:
                for pt in controlpoints:
                    pt.clicked = False

    bg.fill(background_colour)

    for i in range(len(controlpoints)):
        controlpoints[i].display()
        if not lock:
            controlpoints[i].move()
            if counter <= 10:
                controlpoints[i].pos += (waytogo*0.8)/controlpoints[i].mass
            else:
                controlpoints[i].pos += (waytogo*0.3)/controlpoints[i].mass

    for i in range(len(triangles)):
        drawTriangle((0,0,100,linealpha), triangles[i], xoff=centre[0], yoff=centre[1])

    for i in range(len(vertices)):
        if showVertices: vertices[i].display()
        if toggle:
            vertices[i].smooth()
            vertices[i].pos = vertices[i].pos*0.99
            vertices[i].checkdistance()


    counter += 1            
    if counter >= 15:
        counter = 0
    
    
    screen.blit(bg, (0,0))
    pygame.display.flip()
    time.sleep(0.030)

pygame.quit()





Back to top Go down
View user profile
Sponsored content




PostSubject: Re: Cell Membrane   Today at 11:01 pm

Back to top Go down
 
Cell Membrane
View previous topic View next topic Back to top 
Page 1 of 1

Permissions in this forum:You cannot reply to topics in this forum
Thrive Game Development :: Development :: Design :: Prototypes-
Jump to: