diff --git a/CellModeller/gui/renderers.py b/CellModeller/gui/renderers.py deleted file mode 100644 index 7935e57e..00000000 --- a/CellModeller/gui/renderers.py +++ /dev/null @@ -1,1692 +0,0 @@ -from OpenGL.GL import * -from OpenGL.GLU import * -from OpenGL.arrays import vbo -import math -import numpy -import random -#import pygame # helper module for text rendering - turned off to see if it suppresses segfaults -#from pyopencl.array import vec - -class GLSphereRenderer: - def __init__(self, sim, properties=None, scales = None): - self.ncells_list = 0 - self.ncells_names_list = 0 - self.dlist = None - self.dlist_names = None - self.cellcol = [1, 1, 1] - self.sim = sim - self.quad = gluNewQuadric() - self.properties = properties - self.scales = scales - - def init_gl(self): - pass - - def build_list(self, cells): - if self.dlist: - glDeleteLists(self.dlist, 1) - index = glGenLists(1) - glNewList(index, GL_COMPILE) - self.render_cells() - glEndList() - self.dlist = index - - def build_list_names(self, cells): - if self.dlist_names: - glDeleteLists(self.dlist_names, 1) - index = glGenLists(1) - glNewList(index, GL_COMPILE) - self.render_cell_names() - glEndList() - self.dlist_names = index - - def render_gl(self, selection=None): - cells = self.sim.cellStates.values() - states = self.sim.cellStates.items() - # FIXED ============================================================================= - # Before, the renderer would only draw cells when the number of cells changed. - # Now it draws them whenever render_gl is called (by paintGL in PyGLCMViewer.py) - #if len(cells)!=self.ncells_list or len(cells)<500: - # self.build_list(cells) - # self.ncells_list = len(cells) - - #if len(cells)!=self.ncells_list: - # self.build_list(cells) - # self.ncells_list = len(cells) - #==================================================================================== - #glCallList(self.dlist) - self.render_cells(selection=selection) - - - def renderNames_gl(self, selection=None): - #cells = self.sim.cellStates.values() - #if len(cells)!=self.ncells_names_list or len(cells)<500: - # self.build_list_names(cells) - # self.ncells_names_list = len(cells) - #glCallList(self.dlist_names) - #for cell in cells: self.render_cell_name(cell, selection) - self.render_cell_names() - - def render_cell_names(self): - # glDisable(GL_DEPTH_TEST) - glDisable(GL_LIGHTING) - for cell in self.sim.cellStates.values(): - l = cell.length - p = cell.pos - - cid = cell.id - glPushName(cid) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glTranslatef(p[0],p[1],p[2]) - gluSphere(self.quad, r, 8, 8) - glPopMatrix() - - glPopName() - - glEnable(GL_LIGHTING) - - def render_cell(self, cell, selection): - r = cell.radius - p = cell.pos - cid = cell.id - if selection==cid: - cellcol = [1,0,0] - else: - cellcol = cell.color - - glColor3fv(cellcol) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glTranslatef(p[0],p[1],p[2]) - gluSphere(self.quad, r, 8, 8) - glPopMatrix() - - def render_cells(self, selection=None): - - # PLACEHOLDER - - #glDisable(GL_DEPTH_TEST) - glDisable(GL_LIGHTING) - cells = self.sim.cellStates.values() - for cell in cells: - self.render_cell(cell, selection) - -class GLGridRenderer: - def __init__(self, sig, integ, rng=None, alpha=1.): - self.sig = sig - self.integ = integ - self.rng = rng - self.alpha = alpha - self.size = self.sig.gridSize - self.dim = self.sig.gridDim[1:] - self.len = self.dim[0]*self.dim[1] - self.orig = [self.sig.gridOrig[i] - self.sig.gridSize[i]*0.5 for i in range(3)] - self.texture = glGenTextures(1) - self.imageData = numpy.zeros(self.dim) - - # Create byte array for texture data: OpenGL requires 2^n size and - # square? - self.texDim = max([2**math.ceil(math.log(d,2)) for d in self.dim[0:2]]) - # The texture coordinates to fit the actual data - self.tyMax = self.dim[0]/self.texDim - self.txMax = self.dim[1]/self.texDim - # The texture data, 3 bytes per (x,y,z) - self.byteImageData = numpy.zeros((self.texDim,self.texDim)+(3,)).astype(numpy.uint8) - - def init_gl(self): - pass - - def renderNames_gl(self): - pass - - def render_gl(self, selection=None): - signalLevel = self.integ.signalLevel.reshape(self.sig.gridDim) - self.imageData = signalLevel[:,:,:,math.floor(self.dim[2]*0.5)] #numpy.log(self.sig.level[:,:,:,0]+1e-8) - if self.rng: - mx = self.rng[1] - mn = self.rng[0] - else: - mx = numpy.max(self.imageData) - mn = numpy.min(self.imageData) - if mx>mn: - scale = 255/(mx-mn) - else: - scale = 1 - self.imageData = (self.imageData - mn)*scale - #print "Signal grid range = %f to %f"%(mn,mx) - for s in range(self.sig.nSignals): - self.byteImageData[0:self.dim[0],0:self.dim[1],s] = self.imageData[s,:,:].astype(numpy.uint8) - - - glEnable(GL_TEXTURE_2D) - glDisable(GL_LIGHTING) - #glDisable(GL_DEPTH_TEST) - glBindTexture(GL_TEXTURE_2D, self.texture) - #Load the Data into the Texture - glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, self.texDim, self.texDim, 0, GL_RGB, GL_UNSIGNED_BYTE, self.byteImageData ) -# glTexSubImage2Dub( GL_TEXTURE_2D, 0, 0,0, GL_RED, GL_UNSIGNED_BYTE, self.imageData ) - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) - #Define some Parameters for this Texture - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA ,GL_ONE_MINUS_SRC_ALPHA) - glColor4f(1.0,1.0,1.0,self.alpha) - glBegin(GL_QUADS) - glTexCoord2f(0.0, 0.0) - glVertex3f(self.orig[0], self.orig[1], 0.0) # Bottom Left Of The Texture and Quad - glTexCoord2f(0.0, self.tyMax) - glVertex3f(self.orig[0]+self.dim[0]*self.size[0], self.orig[1], 0.0)# Bottom Right Of The Texture and Quad - glTexCoord2f(self.txMax, self.tyMax) - glVertex3f( self.orig[0]+self.dim[0]*self.size[0], self.orig[1]+self.dim[1]*self.size[1], 0.0) # Top Right Of The Texture and Quad - glTexCoord2f(self.txMax, 0.0) - glVertex3f(self.orig[0] , self.orig[1]+self.dim[1]*self.size[1], 0.0) - glEnd() - glEnable(GL_LIGHTING) - #glEnable(GL_DEPTH_TEST) - glDisable(GL_BLEND) - glDisable(GL_TEXTURE_2D) - -class GLPlantSignalRenderer: - # Render signals as channels rgb - def __init__(self, sim, chanIdx): - self.sim = sim - self.wallcol = [0, 0, 0] - self.nodecol = [0, 0, 0] - self.cellcol = [1, 1, 1] - self.chanIdx = chanIdx - - def init_gl(self): - pass - - def renderNames_gl(self): - pass - - def render_gl(self, selection=None): - # Render signals[chanIdx] as rgb - # - maxSig = [0,0,0] - for i in range(len(self.chanIdx)): - for (cid,cellState) in self.sim.cellStates.items(): - s = cellState.signals[self.chanIdx[i]] - if s > maxSig[i]: - maxSig[i] = s - if maxSig[i]==0: - maxSig[i]=1.0 - - for (cid,cellState) in self.sim.cellStates.items(): - col = [0,0,0] - for i in range(len(self.chanIdx)): - col[i] = cellState.signals[self.chanIdx[i]]/maxSig[i] - glColor3fv(col) - glBegin(GL_POLYGON) - for p in cellState.nodep: - glVertex2d(p[0], p[1]) - glEnd() - -class GLPlantRenderer: - def __init__(self, sim): - self.sim = sim - self.wallcol = [0, 0, 0] - self.nodecol = [0, 0, 0] - self.cellcol = [1, 1, 1] - - def init_gl(self): - pass - - def renderNames_gl(self): - # Render cell_ids for selection - # - for (cid,cellState) in self.sim.cellStates.items(): - glColor3fv(self.cellcol) - glPushName(cid) - glBegin(GL_POLYGON) - for p in cellState.nodep: - glVertex2d(p[0], p[1]) - glEnd() - glPopName() - - def render_gl(self, selection=None): - # Render model using PyOpenGL - # - # principle axes - #scale=10.0 - #c=cell.get_centre() - #glLineWidth(1) - #glColor3f(1.0,0.0,0.0) #red - #glBegin(GL_LINES) - #glVertex2d(c[0],c[1]) - #glVertex2d(c[0]+cell.pa1[0]*scale,c[1]+cell.pa1[1]*scale) - #glColor3f(0.0,0.0,1.0) #blue - #glVertex2d(c[0],c[1]) - #glVertex2d(c[0]+cell.pa2[0]*scale,c[1]+cell.pa2[1]*scale) - #glEnd() - glDisable(GL_LIGHTING) - glDisable(GL_DEPTH_TEST) - cellStates = self.sim.cellStates - - #for (cid,cellState) in cellStates.items(): - # cellcol = cellState.color - # glColor3fv(cellcol) - # glBegin(GL_POLYGON) - # for p in cellState.nodep: - # glVertex2d(p[0], p[1]) - # glEnd() - for (cid,cellState) in cellStates.items(): - col = self.wallcol - for (wp1,wp2) in cellState.wallp: - glColor3fv(col) - glLineWidth(2) - glBegin(GL_LINES) - glVertex2d(wp1[0], wp1[1]) - glVertex2d(wp2[0], wp2[1]) - glEnd() - - if selection>0 and cellStates.has_key(selection): - scell = cellStates[selection] - glColor3fv([1,0,0]) - for (wp1,wp2) in scell.wallp: - glLineWidth(2) - glBegin(GL_LINES) - glVertex2d(wp1[0], wp1[1]) - glVertex2d(wp2[0], wp2[1]) - glEnd() - - # Draw nodes as points - #glColor3fv(self.nodecol) - #glPointSize(3) - - #for node in self.model.nodes: - # p = node.get_pos() - # glBegin(GL_POINTS) - # glVertex2d(p[0], p[1]) - # glEnd() - # r = node.get_rot() - # glBegin(GL_LINES) - # glVertex2d(p[0], p[1]) - # glVertex2d(p[0] + math.cos(r), p[1] + math.sin(r)) - # glEnd() - - glEnable(GL_LIGHTING) - - - - -class GLBacteriumRenderer: - def __init__(self, sim, properties=None, scales = None): - self.ncells_list = 0 - self.ncells_names_list = 0 - self.dlist = None - self.dlist_names = None - self.cellcol = [1, 1, 1] - self.sim = sim - self.quad = gluNewQuadric() - self.properties = properties - self.scales = scales - self.last_rendered_step = -1 - - def init_gl(self): - pass - - #def render_text(self, position, textString, fontSize): - # pygame.font.init() - # font = pygame.font.Font (None, fontSize) - # textSurface = font.render(textString, True, (0,0,0,225), (255,255,255,255)) - # textData = pygame.image.tostring(textSurface, "RGBA", True) - # glRasterPos3d(*position) - # glDrawPixels(textSurface.get_width(), textSurface.get_height(), GL_RGBA, GL_UNSIGNED_BYTE, textData) - - def build_list(self, cells): - if self.dlist: - glDeleteLists(self.dlist, 1) - index = glGenLists(1) - glNewList(index, GL_COMPILE) - self.render_cells() - glEndList() - self.dlist = index - - def build_list_names(self, cells): - if self.dlist_names: - glDeleteLists(self.dlist_names, 1) - index = glGenLists(1) - glNewList(index, GL_COMPILE) - self.render_cell_names() - glEndList() - self.dlist_names = index - - def render_gl(self, selection=None): - cells = self.sim.cellStates.values() - states = self.sim.cellStates.items() - # FIXED ============================================================================= - # Before, the renderer would only draw cells when the number of cells changed. - # Now it draws them whenever render_gl is called (by paintGL in PyGLCMViewer.py) - #if len(cells)!=self.ncells_list or len(cells)<500: - # self.build_list(cells) - # self.ncells_list = len(cells) - - #if len(cells)!=self.ncells_list: - if self.last_rendered_step0: - glColor4f(s.signals[0],0,0,1) - else: - glColor4fv(c.color) #c.color[0:3]) - else: - glColor4f(1,0,0,1) - glDrawArrays(GL_TRIANGLES, self.vboOffsets[cid], len(c.tris)*3) - - glDisable(GL_POLYGON_OFFSET_FILL); - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - glColor4f(0,0,0,0.5); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - glDrawArrays(GL_TRIANGLES, 0, self.nverts) - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - - self.vbo.unbind() - glDisableClientState(GL_VERTEX_ARRAY); - #glDisableClientState(GL_NORMAL_ARRAY); - - # glEnable(GL_LIGHTING) - #glDisable(GL_BLEND) #DEPTH_TEST) - # glDisable(GL_DEPTH_TEST) - - def renderNames_gl(self): - if not self.vbo: - self.init_gl() - - glDisable(GL_LIGHTING) - glDisable(GL_DEPTH_TEST) - #glCullFace(GL_FRONT) - glDisable(GL_CULL_FACE) - - self.vbo.bind() - glEnableClientState(GL_VERTEX_ARRAY); - - glVertexPointer(3, GL_FLOAT, 32, self.vbo ) - for c in self.mesh.getCells(): - cid = c.getId() - glPushName(cid) - glDrawArrays(GL_TRIANGLES, self.vboOffsets[cid], len(c.tris)*3) - glPopName() - - self.vbo.unbind() - glDisableClientState(GL_VERTEX_ARRAY); - glEnable(GL_LIGHTING) - - - - -class GLCelBacteriumRenderer: - def __init__(self, sim, properties=None, scales=None): - self.cellcolor = [0.4, 0.6, 0.5] - self.sim = sim - self.properties = properties - self.scales = scales - self.init_gl() - - def init_gl(self): - # display lists for drawing cells - self.cylinder = self.build_cylinder(16) - self.hemisphere = self.build_hemisphere(16, 16) - # set up the 1d texture used for cel-shading 'lighting' - self.texture = glGenTextures(1) - glBindTexture(GL_TEXTURE_1D, self.texture) - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) - self.tex_img = [0.2]*(7*3) + [0.6]*(10*3) + [0.9]*(15*3) - glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, 32, 0, GL_RGB, GL_FLOAT, self.tex_img) - glEnable(GL_TEXTURE_1D) - glBindTexture(GL_TEXTURE_1D, self.texture) - # we'll do our own 'lighting' - glDisable(GL_LIGHTING) - - def build_cylinder(self, n_phis): - """Return the id of a display list that draws a cylinder. - - The cylinder is along the x axis from -1/2 to 1/2. It has a - circular cross-section with radius 1. - - n_phis -- The number of rectangular panels with which to - approximate the cylinder. - """ - index = glGenLists(1) - phis = [float(i)*2.0*numpy.pi/float(n_phis) for i in range(n_phis+1)] - phi_pairs = zip(phis, phis[1:]) - glNewList(index, GL_COMPILE) - glBegin(GL_QUADS) - for phi1,phi2 in phi_pairs: - dot1 = min(max(numpy.cos(phi1), 0.0), 1.0) - dot2 = min(max(numpy.cos(phi2), 0.0), 1.0) - glTexCoord1f(dot1) - glVertex3f(-0.5, numpy.sin(phi1), numpy.cos(phi1)) - glTexCoord1f(dot1) - glVertex3f(0.5, numpy.sin(phi1), numpy.cos(phi1)) - glTexCoord1f(dot2) - glVertex3f(0.5, numpy.sin(phi2), numpy.cos(phi2)) - glTexCoord1f(dot2) - glVertex3f(-0.5, numpy.sin(phi2), numpy.cos(phi2)) - glEnd() - glEndList() - return index - - def build_hemisphere(self, n_phis, n_thetas): - """Return the id of a display list that draw a hemisphere. - - The hemisphere is in the +x half of the x axis and opens on the x=0 plane. It has a radius of 1. - - n_phis -- Number of latitudes to use. There are actually n_phis/2+1 latitudes. - n_thetas -- Number of longitudes to use. - """ - index = glGenLists(1) - phis = [float(i)*numpy.pi/float(n_phis/2) for i in range(n_phis/2+1)] - phi_pairs = zip(phis, phis[1:]) - thetas = [float(i)*numpy.pi/float(n_thetas) for i in range(-n_thetas/2, n_thetas/2+1)] - theta_pairs = zip(thetas, thetas[1:]) - glNewList(index, GL_COMPILE) - glBegin(GL_QUADS) - for phi1,phi2 in phi_pairs: - dot1 = min(max(numpy.cos(phi1), 0.0), 1.0) - dot2 = min(max(numpy.cos(phi2), 0.0), 1.0) - for th1,th2 in theta_pairs: - glTexCoord1f(dot1) - glVertex3f(numpy.sin(phi1)*numpy.cos(th1), numpy.sin(phi1)*numpy.sin(th1), numpy.cos(phi1)) - glTexCoord1f(dot2) - glVertex3f(numpy.sin(phi2)*numpy.cos(th1), numpy.sin(phi2)*numpy.sin(th1), numpy.cos(phi2)) - glTexCoord1f(dot2) - glVertex3f(numpy.sin(phi2)*numpy.cos(th2), numpy.sin(phi2)*numpy.sin(th2), numpy.cos(phi2)) - glTexCoord1f(dot1) - glVertex3f(numpy.sin(phi1)*numpy.cos(th2), numpy.sin(phi1)*numpy.sin(th2), numpy.cos(phi1)) - glEnd() - glEndList() - return index - - def render_capsule(self, l, r): - """Draw a capsule at the origin along the x axis. - - l -- length of the capsule. - r -- radius of the capsule. - """ - # draw cylinder - glPushMatrix() - glScalef(l, r, r) - glCallList(self.cylinder) - glPopMatrix() - # draw +x hemisphere - glPushMatrix() - glTranslatef(l/2.0, 0, 0) - glScalef(r, r, r) - glCallList(self.hemisphere) - glPopMatrix() - # draw -x hemisphere - glPushMatrix() - glRotatef(180.0, 0, 0, 1) - glTranslatef(l/2.0, 0, 0) - glScalef(r, r, r) - glCallList(self.hemisphere) - glPopMatrix() - - def render_cell(self, cell, selection=None): - l = cell.length - r = cell.radius - (e1,e2) = map(numpy.array, cell.ends) - ori = (e1+e2)/2.0 # origin in the cell's coordinates - z = numpy.array([0, 0, 1]) - axis = (e2-e1)/l # x axis in the cell's coordinates - rotangle = numpy.arctan2(axis[1], axis[0]) # angle to rotate around the z axis - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - - # move to the cell's coordinates - glTranslatef(ori[0], ori[1], ori[2]) - glRotatef(rotangle*180.0/numpy.pi, z[0], z[1], z[2]) - - # draw the cell in color - color = getattr(cell, 'color', self.cellcolor) - glColor3fv(color) - self.render_capsule(l, r) - - # draw the outlines antialiased in black - glColor3f(0.0, 0.0, 0.0) - glLineWidth(2.0) - if cell.id == selection: - glColor3f(1.0, 0.3, 0.3) - glLineWidth(3.0) - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA ,GL_ONE_MINUS_SRC_ALPHA) - glEnable(GL_LINE_SMOOTH) - # draw wireframe for back facing polygons and cull front-facing ones - glPolygonMode(GL_BACK, GL_LINE) - glEnable(GL_CULL_FACE) - glCullFace(GL_FRONT) - glDepthFunc(GL_LEQUAL) - self.render_capsule(l, r) - glDepthFunc(GL_LESS) - glDisable(GL_CULL_FACE) - glPolygonMode(GL_FRONT, GL_FILL) - glDisable(GL_LINE_SMOOTH) - glDisable(GL_BLEND) - - glPopMatrix() - - def render_cell_name(self, cell): - l = cell.length - r = cell.radius - (e1,e2) = map(numpy.array, cell.ends) - ori = (e1+e2)/2.0 # origin in the cell's coordinates - z = numpy.array([0, 0, 1]) - axis = (e2-e1)/l # x axis in the cell's coordinates - rotangle = numpy.arctan2(axis[1], axis[0]) # angle to rotate around the z axis - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - - # move to the cell's coordinates - glTranslatef(ori[0], ori[1], ori[2]) - glRotatef(rotangle*180.0/numpy.pi, z[0], z[1], z[2]) - - # draw the cell in color - glPushName(cell.id) - self.render_capsule(l, r) - glPopName() - - glPopMatrix() - - - def render_gl(self, selection=None): - cells = self.sim.cellStates.values() - for cell in cells: self.render_cell(cell, selection) - - def renderNames_gl(self, selection=None): - cells = self.sim.cellStates.values() - for cell in cells: self.render_cell_name(cell) - - - - - - - -class GL2DBacteriumRenderer: - - def __init__(self, sim, properties=None, scales=None): - self.ncells_list = 0 - self.ncells_names_list = 0 - self.dlist = None - self.dlist_names = None - self.sim = sim - self.properties = properties - self.scales = scales - self.cellcolor = [0.4, 0.6, 0.5] - self.circ_pts = [(math.cos(math.radians(th)), math.sin(math.radians(th))) for th in range(-80,90,20)] - self.semi = self.build_semicircle() - - - def build_semicircle(self): - index = glGenLists(1) - glNewList(index, GL_COMPILE) - glVertex3f(0.5, -1, 0) - for x,y in self.circ_pts: - glVertex3f(0.5 + x, y, 0.0) - glVertex3f(0.5, 1, 0) - glEndList() - return index - - def draw_cell(self, p, d, l, r): - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - ang = math.atan2(d[1], d[0]) * 360.0 / (2.0*3.141593) - glTranslatef(p[0], p[1], 0.0) - glRotatef(ang, 0.0, 0.0, 1.0) - glBegin(GL_POLYGON) - glVertex3f(-l/2.0, -r, 0) - glVertex3f(l/2.0, -r, 0) - for x,y in self.circ_pts: - glVertex3f(l/2.0 + x*r, y*r, 0.0) - glVertex3f(l/2.0, r, 0) - glVertex3f(-l/2.0, r, 0) - for x,y in self.circ_pts: - glVertex3f(-l/2.0 -x*r, -y*r, 0.0) - glEnd() - glPopMatrix() - - def build_list(self, cells): - if self.dlist: - glDeleteLists(self.dlist, 1) - index = glGenLists(1) - glNewList(index, GL_COMPILE) - for cell in cells: self.render_cell(cell) - glEndList() - self.dlist = index - - def build_list_names(self, cells): - if self.dlist_names: - glDeleteLists(self.dlist_names, 1) - index = glGenLists(1) - glNewList(index, GL_COMPILE) - for cell in cells: self.render_cell_name(cell) - glEndList() - self.dlist_names = index - - def render_cell(self, cell, selection=None): - p,d,l,r = (cell.pos, cell.dir, cell.length, cell.radius) - glMatrixMode(GL_MODELVIEW) - glEnable(GL_DEPTH_TEST) - glPushMatrix() - glPolygonMode(GL_FRONT, GL_FILL) - glColor3f(0.0, 0.0, 0.0) - if selection == cell.id: - glColor3f(0.2, 0.1, 0.8) - glTranslatef(0.0, 0.0, 0.025) - self.draw_cell(p, d, l, r) - color = getattr(cell, 'color', [0.4, 0.4, 0.4]) - glColor3fv(color) - glTranslatef(0.0, 0.0, 0.025) - self.draw_cell(p, d, l, r-0.15) - glPopMatrix() - glDisable(GL_DEPTH_TEST) - - - def render_cell_name(self, cell, selection=None): - p,d,l,r = (cell.pos, cell.dir, cell.length, cell.radius) - glPushName(cell.id) - glEnable(GL_DEPTH_TEST) - self.draw_cell(p, d, l, r) - glPopName() - glDisable(GL_DEPTH_TEST) - - - def render_gl(self, selection=None): - glDisable(GL_LIGHTING) - glEnable(GL_POLYGON_SMOOTH) - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - cells = self.sim.cellStates.values() - #if len(cells)!=self.ncells_list: - self.build_list(cells) - #self.ncells_list = len(cells) - glCallList(self.dlist) - #for cell in cells: self.render_cell(cell, selection) - - - def renderNames_gl(self, selection=None): - cells = self.sim.cellStates.values() - if len(cells)!=self.ncells_names_list: - self.build_list_names(cells) - self.ncells_names_list = len(cells) - glCallList(self.dlist_names) - #for cell in cells: self.render_cell_name(cell, selection) - - - diff --git a/README.md b/README.md index 16cc3e30..6ffe5f65 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # CellModeller -[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](Community/code_of_conduct.md) +[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](community/code_of_conduct.md) ============= Multicellular modelling framework, created by Tim Rudge, PJ Steiner, and Jim Haseloff, University of Cambridge. -- [Contributing](Community/contributing.md) -- [Code of Conduct](Community/code_of_conduct.md) +- [Contributing](community/contributing.md) +- [Code of Conduct](community/code_of_conduct.md) diff --git a/Scripts/batch_iter.py b/Scripts/batch_iter.py deleted file mode 100644 index 05611dab..00000000 --- a/Scripts/batch_iter.py +++ /dev/null @@ -1,23 +0,0 @@ -import os -import sys -import subprocess -import string -import shutil - -from CellModeller.simulator import Simulator - -sys.path.append('../../Models') - -max_cells = 100000 -cell_buffer = 256 - -gammas = [2,5,10,15,30,50,100,500] - -def simulate(mod_name, ingam, fname): - sim = Simulator(mod_name, 0.25, ingam, 25, fname) - while len(sim.cellStates) < max_cells-cell_buffer: - sim.step() - -#here we are varying gamma from 1 to 50, every 2 -for i in range(len(gammas)): - simulate(sys.argv[1],gammas[i],(str(sys.argv[1])+'_gamma='+str(gammas[i]))) diff --git a/Scripts/contact_graph.py b/Scripts/contact_graph.py deleted file mode 100644 index b57f7e3a..00000000 --- a/Scripts/contact_graph.py +++ /dev/null @@ -1,141 +0,0 @@ -import sys -import os -import math -import numpy as np -import pickle -import CellModeller -import subprocess -import string -import shutil -from CellModeller.simulator import Simulator -import networkx -from reportlab.pdfgen.canvas import Canvas -from reportlab.lib import units -from reportlab.lib.colors import Color -from reportlab.graphics.shapes import Circle - -class CellModellerPDFGenerator(Canvas): - # --- - # Class that extends reportlab pdf canvas to draw CellModeller simulations - # --- - def __init__(self, name, network, bg_color): - self.name = name - self.data = data - self.bg_color = bg_color - Canvas.__init__(self, name) - self.network = network - - def setup_canvas(self, name, world, page, page_center): - worldx,worldy = world - pagex,pagey = page - page_centx,page_centy = page_center - self.setPageSize((pagex*units.cm, pagey*units.cm)) - self.setFillColor(self.bg_color) - self.rect(0, 0, pagex*units.cm, pagey*units.cm, fill=1) - self.translate(pagex*units.cm/2.0, pagey*units.cm/2.0) - self.translate(page_centx*units.cm, page_centy*units.cm) - self.scale(float(pagex*units.cm)/worldx, float(pagey*units.cm)/worldy) - - def drawNode(self,pos,col): - Canvas.setFillColor(self,col) - Canvas.circle(self,pos[0],pos[1],0.3, fill = 1) - - def drawEdge(self,pos1,pos2): - path = self.beginPath() - path.moveTo(pos1[0],pos1[1]) - path.lineTo(pos2[0],pos2[1]) - self.drawPath(path, fill=1) - - def draw_frame(self, name, world, page, center): - self.setup_canvas(name, world, page, center) - self.draw_graph() - self.showPage() - self.save() - - def draw_graph(self): - self.saveState() - self.setLineWidth(0.003*units.cm) - self.setStrokeColor((0.0,0.0,0.0)) - positions = list(networkx.get_node_attributes(G,'pos').values()) - colors = list(networkx.get_node_attributes(G,'color').values()) - for u,v,di in self.network.edges_iter(data=True): - self.drawEdge(positions[u],positions[v]) - for n in self.network.nodes_iter(): - self.drawNode(positions[n],colors[n]) - self.restoreState() - -def generate_network(G,num_cells,ct_tos,n_cts,pos,celldata): - for i in range(0, num_cells): #make all nodes - G.add_node(i, pos = pos[i], type=celldata[i], color = (1,0,0) if celldata[i]==0 else (0,1,0)) - for i in range(0, num_cells): #make all edges (when all nodes are present) - for j in range(0, n_cts[i]): #make all edges from contact_tos - G.add_edge(i,ct_tos[i,j], width=8 , color = 'black') - -def get_current_contacts(G, data): - cs = data['cellStates'] - it = iter(cs) - n = len(cs) - cell_type={} - pos_dict={} - for it in cs: - cell_type[cs[it].idx] = cs[it].cellType - pos_dict[cs[it].idx] = cs[it].pos[0:2] - modname = data['moduleName'] - moduleStr = data['moduleStr'] - sim = Simulator(modname, 0.0, moduleStr=moduleStr, saveOutput=False) - sim.loadFromPickle(data) - sim.phys.update_grid() # we assume local cell_centers is current - sim.phys.bin_cells() - sim.phys.cell_sqs = sim.phys.cell_sqs_dev.get() # get updated cell sqs - sim.phys.sort_cells() - sim.phys.sorted_ids_dev.set(sim.phys.sorted_ids) # push changes to the device - sim.phys.sq_inds_dev.set(sim.phys.sq_inds) - sim.phys.find_contacts(predict=False) - sim.phys.get_cts() - ct_pts=sim.phys.ct_pts #these are the points on the cell surface - they can be transformed into the global coordinate system - ct_tos=sim.phys.ct_tos #this is the list of *some* contacts from each cell (only for lower cell_ids) - ct_dists=sim.phys.ct_dists - cell_cts=sim.phys.cell_n_cts #not really all the contacts of the cell, because the tos are only defined between a cell and ones with lower ids - generate_network(G,n,ct_tos,cell_cts,pos_dict,cell_type) - - -bg_color = Color(1.0,1.0,1.0,alpha=1.0) - - -G = networkx.Graph() -fname = sys.argv[1] -data = pickle.load(open(fname,'rb')) -cs = data['cellStates'] -it = iter(cs) -n = len(cs) -oname = fname.replace('.pickle','_graph.pdf') - -print(("num_cells = "+str(n))) - -cell_type={} -pos_dict={} -for it in cs: - cell_type[cs[it].idx] = cs[it].cellType - pos_dict[cs[it].idx] = cs[it].pos[0:2] - -get_current_contacts(G, data) - -print(("num_contacts = " + str(networkx.number_of_edges(G)))) -degrees = list(G.degree().values()) -print(("mean degree = " + str(np.mean(degrees)))) - -if list(networkx.get_edge_attributes(G,'color').items()): - edges,ecolor = list(zip(*list(networkx.get_edge_attributes(G,'color').items()))) -ncolor = list(networkx.get_node_attributes(G,'color').values()) - -pdf = CellModellerPDFGenerator(oname, G, bg_color) -world = (120,120) -page = (50,50) -center = (0,0) - -pdf.draw_frame(oname, world, page, center) - - -#from this, I want the spatial position of every contact, and the graph of the cells that each cell is touching -#This can be all in one data structure - a graph where each vertex has a position, and each node has a cellState - diff --git a/Scripts/length_histogram.py b/Scripts/length_histogram.py deleted file mode 100644 index 7253bcfd..00000000 --- a/Scripts/length_histogram.py +++ /dev/null @@ -1,55 +0,0 @@ -import sys -import os -import math -import numpy as np -sys.path.append('.') -import pickle -import CellModeller -#import matplotlib.pyplot as plt - -file = sys.argv[1] #select pickle file in command line -output_file = open('LengthData.csv','w') - - -def rad_pos(cellstate): - return np.sqrt(cellstate.pos[0]*cellstate.pos[0]+cellstate.pos[1]*cellstate.pos[1]) - -def lengthHist(pickle, bins, file=False): - print(('opening '+ pickle)) - data = pickle.load(open(pickle,'r')) - cs = data['cellStates'] - it = iter(cs) - n = len(cs) - print(('Number of cells = '+str(n))) - lens = [] - r = [] - for it in cs: - radialPosition = rad_pos(cs[it]) - cellLength = cs[it].length+2*cs[it].radius - r.append(radialPosition) - lens.append(cellLength) - if file: - file.write(str(radialPosition)+', '+ str(cellLength)+'\n') - #plt.hist(lens,bins) - #heatmap, xedges, yedges = np.histogram2d(r, lens, bins=100) - #extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]] - #plt.imshow(heatmap,extent=extent,aspect='auto') - #plt.show() - #print r, lens - return r, lens - -def dirLengthHist(dir,bins,file=False): - for f in os.listdir(dir): - if '.pickle' in f: - number = f[5:-7] - if file: - fout = open('LengthData'+number+'.csv') - print(('step number = ', number)) - n, lens = lengthHist(dir + f,bins,file) - if file: - fout.close() - -bin = np.arange(0.0,6.0,0.1) -lengthHist(file,bin,output_file) -output_file.close() - diff --git a/Scripts/print_timing.py b/Scripts/print_timing.py deleted file mode 100644 index f6f3996c..00000000 --- a/Scripts/print_timing.py +++ /dev/null @@ -1,18 +0,0 @@ -import sys -import os -sys.path.append('.') -import pickle -import CellModeller - -dir = os.path.join('data', sys.argv[1]) - -print("No. cells\ttime (s)") -print("-------------------") -for f in os.listdir(dir): - if 'pickle' in f: - ff = os.path.join(dir,f) - (cs,lin) = pickle.load(open(ff,'r')) - n = len(cs) - t = os.path.getmtime(ff) - print(("%i\t%f"%(n,t))) - diff --git a/Scripts/spatial_analyze.py b/Scripts/spatial_analyze.py deleted file mode 100644 index 15b86ecb..00000000 --- a/Scripts/spatial_analyze.py +++ /dev/null @@ -1,44 +0,0 @@ -import sys -import os -import math -import numpy as np -sys.path.append('.') -import pickle -import CellModeller -import matplotlib.pyplot as plt - -pname= sys.argv[1] -print(('opening '+ pname)) -fout=open('spatial.txt', "w") -fout.write("r spec_level \n") - -bin_num=20 -rad_max=95 - -narray=np.zeros(bin_num) -specArray=np.zeros(bin_num) - -data = pickle.load(open(pname,'r')) -cs = data['cellStates'] -it = iter(cs) -n = len(cs) - -rArray = np.multiply(list(range(0,bin_num)),rad_max/bin_num) - -for it in cs: - r = np.sqrt(cs[it].pos[0]*cs[it].pos[0]+cs[it].pos[1]*cs[it].pos[1]) - for x in range(0,bin_num): - if((r>=x*rad_max/bin_num)and(r<(x+1)*rad_max/bin_num)): - specArray[x]=specArray[x]*narray[x]+cs[it].species[0] - narray[x]+=1 - specArray[x]=specArray[x]/narray[x] - -for x in range(0,bin_num): - fout.write(str((x+1)*rad_max/bin_num/2)+" "+str(narray[x])) - if(narray[x]==0): fout.write(" 0.0\n") - else: fout.write(" "+str(specArray[x])+"\n") - -fout.close() - -plt.plot(rArray,specArray) -plt.show() \ No newline at end of file diff --git a/CellModeller/Regulation/__init__.py b/cellmodeller/__init__.py similarity index 100% rename from CellModeller/Regulation/__init__.py rename to cellmodeller/__init__.py diff --git a/CellModeller/cell_state.py b/cellmodeller/cell_state.py similarity index 72% rename from CellModeller/cell_state.py rename to cellmodeller/cell_state.py index 4f155874..d70631c8 100644 --- a/CellModeller/cell_state.py +++ b/cellmodeller/cell_state.py @@ -1,12 +1,11 @@ - class CellState: # Don't show these attributes in gui - excludeAttr = ['id', 'divideFlag', 'ends','cellAdh'] + excludeAttr = ["id", "divideFlag", "ends", "cellAdh"] def __init__(self, cid): self.id = cid self.growthRate = 1.0 - self.color = [0.5,0.5,0.5] + self.color = [0.5, 0.5, 0.5] self.divideFlag = False self.cellAge = 0 self.neighbours = [] diff --git a/CellModeller/__init__.py b/cellmodeller/gui/__init__.py similarity index 100% rename from CellModeller/__init__.py rename to cellmodeller/gui/__init__.py diff --git a/CellModeller/gui/py_gl_cm_viewer.py b/cellmodeller/gui/py_gl_cm_viewer.py similarity index 65% rename from CellModeller/gui/py_gl_cm_viewer.py rename to cellmodeller/gui/py_gl_cm_viewer.py index e6e34eea..ee8b5846 100644 --- a/CellModeller/gui/py_gl_cm_viewer.py +++ b/cellmodeller/gui/py_gl_cm_viewer.py @@ -7,9 +7,8 @@ from OpenGL.GL import * from OpenGL.GLU import * -from CellModeller.Regulation import ModuleRegulator -from CellModeller.simulator import Simulator -from CellModeller.cell_state import CellState +from cellmodeller.simulator import Simulator +from cellmodeller.cell_state import CellState import os import sys import pickle @@ -17,15 +16,16 @@ import importlib import numpy as np + class PyGLCMViewer(PyGLWidget): setSavePicklesToggle = pyqtSignal(bool) - selectedCell = pyqtSignal(str) #emit selected cell info + selectedCell = pyqtSignal(str) # emit selected cell info selectedName = -1 dt = 0.05 - def __init__(self, parent = None): - PyGLWidget.__init__(self,parent) + def __init__(self, parent=None): + PyGLWidget.__init__(self, parent) self.animTimer = QTimer() self.animTimer.timeout.connect(self.animate) self.animTimer.start(0) @@ -34,17 +34,17 @@ def __init__(self, parent = None): self.run = False self.frameNo = 0 self.loadingFromPickle = False - self.clPlatformNum=0 - self.clDeviceNum=0 + self.clPlatformNum = 0 + self.clDeviceNum = 0 # Initial view setup self.set_radius(32) - self.rotate([0,0,1],-45) - self.translate([0,0,20]) - self.rotate([1,0,0],-45) + self.rotate([0, 0, 1], -45) + self.translate([0, 0, 20]) + self.rotate([1, 0, 0], -45) # Assume no pixel scaling unless explicitly set - self.pix_ratio = 1. + self.pix_ratio = 1.0 def help(self): pass @@ -61,7 +61,7 @@ def setSimulator(self, sim): self.frameNo += 1 # Make GUI button match simulator state for saving pickles self.setSavePicklesToggle.emit(sim.saveOutput) - print('saveOutput ', sim.saveOutput) + print("saveOutput ", sim.saveOutput) # Get rid of any selected cell id self.selectedName = -1 @@ -69,21 +69,23 @@ def getOpenCLPlatDev(self): return self.getOpenCLPlatform() and self.getOpenCLDevice() def getOpenCLPlatform(self): - # Pop dialogs to get user to choose OpenCL platform + # Pop dialogs to get user to choose OpenCL platform platforms = cl.get_platforms() platlist = [str(p.name) for p in platforms] platdict = dict(list(zip(platlist, list(range(len(platlist)))))) - if len(platlist)==1: + if len(platlist) == 1: self.clPlatformNum = 0 return True - qsPlatformName, ok = QInputDialog.getItem(self, \ - 'Choose OpenCL platform', \ - 'Available platforms:', \ - platlist, \ - editable=False) + qsPlatformName, ok = QInputDialog.getItem( + self, + "Choose OpenCL platform", + "Available platforms:", + platlist, + editable=False, + ) if not ok: print("You didn't select a OpenCL platform...") return False @@ -98,16 +100,14 @@ def getOpenCLDevice(self): devlist = [str(d.name) for d in devices] devdict = dict(list(zip(devlist, list(range(len(devlist)))))) - - if len(devlist)==1: + + if len(devlist) == 1: self.clDeviceNum = 0 return True - - qsDeviceName, ok = QInputDialog.getItem(self, \ - 'Choose OpenCL device', \ - 'Available devices:', \ - devlist, \ - editable=False) + + qsDeviceName, ok = QInputDialog.getItem( + self, "Choose OpenCL device", "Available devices:", devlist, editable=False + ) if not ok: print("You didn't select a OpenCL device...") return False @@ -115,7 +115,6 @@ def getOpenCLDevice(self): self.clDeviceNum = devdict[qsDeviceName] return True - @pyqtSlot(bool) def toggleRun(self, run): self.run = run @@ -134,20 +133,24 @@ def reset(self): importlib.reload(self.sim.module) if self.loadingFromPickle: - sim = Simulator(self.modName, \ - self.dt, \ - moduleStr=self.moduleStr, \ - clPlatformNum=self.clPlatformNum, \ - clDeviceNum=self.clDeviceNum, \ - is_gui=True) - self.setSimulator(sim) + sim = Simulator( + self.modName, + self.dt, + moduleStr=self.moduleStr, + clPlatformNum=self.clPlatformNum, + clDeviceNum=self.clDeviceNum, + is_gui=True, + ) + self.setSimulator(sim) else: - sim = Simulator(self.modName, \ - self.dt, \ - clPlatformNum=self.clPlatformNum, \ - clDeviceNum=self.clDeviceNum, \ - is_gui=True) - self.setSimulator(sim) + sim = Simulator( + self.modName, + self.dt, + clPlatformNum=self.clPlatformNum, + clDeviceNum=self.clDeviceNum, + is_gui=True, + ) + self.setSimulator(sim) self.frameNo = 0 self.updateGL() @@ -155,11 +158,13 @@ def reset(self): def loadGeometry(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog - qs,_ = QFileDialog.getOpenFileName(self, 'Load geometry from pickle file', '', '*.pickle', options=options) + qs, _ = QFileDialog.getOpenFileName( + self, "Load geometry from pickle file", "", "*.pickle", options=options + ) if qs: filename = str(qs) print(filename) - data = pickle.load(open(filename,'rb')) + data = pickle.load(open(filename, "rb")) if isinstance(data, dict): self.sim.loadGeometryFromPickle(data) self.frameNo = self.sim.stepNum @@ -173,22 +178,26 @@ def loadGeometry(self): def loadPickle(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog - qs,_ = QFileDialog.getOpenFileName(self, 'Load pickle file', '', '*.pickle', options=options) + qs, _ = QFileDialog.getOpenFileName( + self, "Load pickle file", "", "*.pickle", options=options + ) if qs and self.getOpenCLPlatDev(): filename = str(qs) print(filename) - data = pickle.load(open(filename,'rb')) + data = pickle.load(open(filename, "rb")) if isinstance(data, dict): - self.modName = data['moduleName'] - self.moduleStr = data['moduleStr'] - self.frameNo = data['stepNum'] - sim = Simulator(self.modName, \ - self.dt, \ - moduleStr=self.moduleStr, \ - clPlatformNum=self.clPlatformNum, \ - clDeviceNum=self.clDeviceNum, \ - is_gui=True) - + self.modName = data["moduleName"] + self.moduleStr = data["moduleStr"] + self.frameNo = data["stepNum"] + sim = Simulator( + self.modName, + self.dt, + moduleStr=self.moduleStr, + clPlatformNum=self.clPlatformNum, + clDeviceNum=self.clDeviceNum, + is_gui=True, + ) + self.loadingFromPickle = True sim.loadFromPickle(data) self.setSimulator(sim) @@ -205,7 +214,9 @@ def loadPickle(self): def load(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog - qs,_ = QFileDialog.getOpenFileName(self, 'Load Python module', '', '*.py', options=options) + qs, _ = QFileDialog.getOpenFileName( + self, "Load Python module", "", "*.py", options=options + ) if qs: modfile = str(qs) print(modfile) @@ -213,20 +224,21 @@ def load(self): def loadModelFile(self, modname): if self.getOpenCLPlatDev(): - self.loadingFromPickle=False - (path,name) = os.path.split(modname) - self.modName = str(name).split('.')[0] + self.loadingFromPickle = False + path, name = os.path.split(modname) + self.modName = str(name).split(".")[0] sys.path.append(path) - sim = Simulator(self.modName, \ - self.dt, \ - clPlatformNum=self.clPlatformNum, \ - clDeviceNum=self.clDeviceNum, \ - is_gui=True) - + sim = Simulator( + self.modName, + self.dt, + clPlatformNum=self.clPlatformNum, + clDeviceNum=self.clDeviceNum, + is_gui=True, + ) + self.setSimulator(sim) self.updateGL() - def animate(self): if self.sim: if self.frameNo > self.sim.stepNum: @@ -234,25 +246,25 @@ def animate(self): self.updateSelectedCell() if self.run: self.frameNo += 1 - + def updateSelectedCell(self): if self.sim: states = self.sim.cellStates cid = self.selectedName - txt = '' + txt = "" if cid in states: - txt += 'Selected Cell (id = %d)
'%(cid) + txt += "Selected Cell (id = %d)
" % (cid) s = states[cid] - for (name,val) in list(s.__dict__.items()): + for name, val in list(s.__dict__.items()): if name not in CellState.excludeAttr: - txt += '' + name + ':\t' + txt += "" + name + ":\t" if type(val) in [float, np.float32, np.float64]: - txt += '%g'%val + txt += "%g" % val elif type(val) in [list, tuple, np.array]: - txt += ', '.join(['%g'%v for v in val]) + txt += ", ".join(["%g" % v for v in val]) else: txt += str(val) - txt += '
' + txt += "
" self.selectedCell.emit(txt) self.updateGL() @@ -265,7 +277,7 @@ def translate(self, _trans): cid = self.selectedName # Uncomment the condition below to enable movement of cells # currently the translation is not great due to the way PyGLWidget works - if False: #self.sim and self.sim.cellStates.has_key(cid): + if False: # self.sim and self.sim.cellStates.has_key(cid): self.sim.moveCell(cid, _trans) print("Called self.sim.moveCell") self.updateSelectedCell() @@ -285,12 +297,12 @@ def translate(self, _trans): def paintGL(self): PyGLWidget.paintGL(self) - glClearColor(0.5,0.5,0.5,0.0) + glClearColor(0.5, 0.5, 0.5, 0.0) glClear(GL_COLOR_BUFFER_BIT) glMatrixMode(GL_MODELVIEW) glPushMatrix() - #s = self.renderInfo.scale - #glScalef(s,s,s) + # s = self.renderInfo.scale + # glScalef(s,s,s) # Draw a grid in xy plane glEnable(GL_DEPTH_TEST) @@ -300,24 +312,24 @@ def paintGL(self): glLineWidth(1.0) glBegin(GL_LINES) for i in range(25): - glVertex(-120, (i-12)*10, 0) - glVertex(120, (i-12)*10, 0) - glVertex((i-12)*10, -120, 0) - glVertex((i-12)*10, 120, 0) + glVertex(-120, (i - 12) * 10, 0) + glVertex(120, (i - 12) * 10, 0) + glVertex((i - 12) * 10, -120, 0) + glVertex((i - 12) * 10, 120, 0) glEnd() # Draw x,y,z axes glDisable(GL_DEPTH_TEST) glBegin(GL_LINES) - glColor3f(1.0,0.0,0.0) - glVertex(0,0,0) - glVertex(25,0,0) - glColor3f(0.0,1.0,0.0) - glVertex(0,0,0) - glVertex(0,25,0) - glColor3f(0.0,0.0,1.0) - glVertex(0,0,0) - glVertex(0,0,25) + glColor3f(1.0, 0.0, 0.0) + glVertex(0, 0, 0) + glVertex(25, 0, 0) + glColor3f(0.0, 1.0, 0.0) + glVertex(0, 0, 0) + glVertex(0, 25, 0) + glColor3f(0.0, 0.0, 1.0) + glVertex(0, 0, 0) + glVertex(0, 0, 25) glEnd() glEnable(GL_DEPTH_TEST) glEnable(GL_LIGHTING) @@ -332,8 +344,8 @@ def paintGL(self): def drawWithNames(self): glMatrixMode(GL_MODELVIEW) glPushMatrix() - #s = self.renderInfo.scale - #glScalef(s,s,s) + # s = self.renderInfo.scale + # glScalef(s,s,s) if self.sim: for r in self.sim.renderers: if r: @@ -341,16 +353,17 @@ def drawWithNames(self): glPopMatrix() - class RenderInfo: def __init__(self): self.renderers = [] self.scale = 1.0 + def addRenderer(self, renderer): self.renderers.append(renderer) + def reset(self): self.renderers = [] self.scale = 1.0 + def setScale(self, s): self.scale = s - diff --git a/CellModeller/gui/py_gl_gui.ui b/cellmodeller/gui/py_gl_gui.ui similarity index 99% rename from CellModeller/gui/py_gl_gui.ui rename to cellmodeller/gui/py_gl_gui.ui index 24b43045..f9d0d5f4 100644 --- a/CellModeller/gui/py_gl_gui.ui +++ b/cellmodeller/gui/py_gl_gui.ui @@ -158,7 +158,7 @@ PyGLCMViewer QWidget -
CellModeller.gui.py_gl_cm_viewer
+
cellmodeller.gui.py_gl_cm_viewer
setSavePicklesToggle(bool) load() diff --git a/CellModeller/gui/py_gl_widget.py b/cellmodeller/gui/py_gl_widget.py similarity index 57% rename from CellModeller/gui/py_gl_widget.py rename to cellmodeller/gui/py_gl_widget.py index e10c9d11..92b1fb38 100644 --- a/CellModeller/gui/py_gl_widget.py +++ b/cellmodeller/gui/py_gl_widget.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -#=============================================================================== +# =============================================================================== # # PyGLWidget.py # @@ -30,17 +30,19 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # -#=============================================================================== +# =============================================================================== from PyQt5 import QtCore, QtGui, QtOpenGL import math import numpy import numpy.linalg as linalg import OpenGL + OpenGL.ERROR_CHECKING = True from OpenGL.GL import * from OpenGL.GLU import * + class PyGLWidget(QtOpenGL.QGLWidget): # Qt signals @@ -48,30 +50,30 @@ class PyGLWidget(QtOpenGL.QGLWidget): rotationBeginEvent = QtCore.pyqtSignal() rotationEndEvent = QtCore.pyqtSignal() - def __init__(self, parent = None): + def __init__(self, parent=None): format = QtOpenGL.QGLFormat() format.setSampleBuffers(True) QtOpenGL.QGLWidget.__init__(self, format, parent) - #self.setCursor(QtCore.Qt.OpenHandCursor) + # self.setCursor(QtCore.Qt.OpenHandCursor) self.setMouseTracking(True) - self.modelview_matrix_ = [] - self.translate_vector_ = [0.0, 0.0, 0.0] - self.viewport_matrix_ = [] + self.modelview_matrix_ = [] + self.translate_vector_ = [0.0, 0.0, 0.0] + self.viewport_matrix_ = [] self.projection_matrix_ = [] - self.near_ = 0.1 - self.far_ = 100.0 - self.fovy_ = 45.0 + self.near_ = 0.1 + self.far_ = 100.0 + self.fovy_ = 45.0 self.radius_ = 5.0 self.last_point_2D_ = QtCore.QPoint() self.last_point_ok_ = False self.last_point_3D_ = [1.0, 0.0, 0.0] - self.isInRotation_ = False + self.isInRotation_ = False self.pickSize = 18 - self.pix_ratio = 1. + self.pix_ratio = 1.0 # connections - #self.signalGLMatrixChanged.connect(self.printModelViewMatrix) + # self.signalGLMatrixChanged.connect(self.printModelViewMatrix) @QtCore.pyqtSlot() def printModelViewMatrix(self): @@ -84,8 +86,8 @@ def initializeGL(self): self.reset_view() def resizeGL(self, width, height): - glViewport( 0, 0, width, height ); - self.set_projection( self.near_, self.far_, self.fovy_ ); + glViewport(0, 0, width, height) + self.set_projection(self.near_, self.far_, self.fovy_) self.updateGL() def paintGL(self): @@ -99,9 +101,14 @@ def set_projection(self, _near, _far, _fovy): self.far_ = _far self.fovy_ = _fovy self.makeCurrent() - glMatrixMode( GL_PROJECTION ) + glMatrixMode(GL_PROJECTION) glLoadIdentity() - gluPerspective( self.fovy_, float(self.width()) / float(self.height()), self.near_, self.far_ ) + gluPerspective( + self.fovy_, + float(self.width()) / float(self.height()), + self.near_, + self.far_, + ) self.updateGL() def set_pick_projection(self, x, y, _near, _far, _fovy): @@ -109,13 +116,24 @@ def set_pick_projection(self, x, y, _near, _far, _fovy): self.far_ = _far self.fovy_ = _fovy self.makeCurrent() - glMatrixMode( GL_PROJECTION ) + glMatrixMode(GL_PROJECTION) glLoadIdentity() viewport = glGetIntegerv(GL_VIEWPORT) - #gluPickMatrix(x, viewport[3]-y, self.pickSize, self.pickSize, viewport); - gluPickMatrix(x*self.pix_ratio, viewport[3]-y*self.pix_ratio, self.pickSize, self.pickSize, viewport); - gluPerspective( self.fovy_, float(self.width()) / float(self.height()), self.near_, self.far_ ) - + # gluPickMatrix(x, viewport[3]-y, self.pickSize, self.pickSize, viewport); + gluPickMatrix( + x * self.pix_ratio, + viewport[3] - y * self.pix_ratio, + self.pickSize, + self.pickSize, + viewport, + ) + gluPerspective( + self.fovy_, + float(self.width()) / float(self.height()), + self.near_, + self.far_, + ) + def set_center(self, _cog): self.center_ = _cog self.view_all() @@ -130,9 +148,9 @@ def set_radius(self, _radius): def reset_view(self): # scene pos and size - glMatrixMode( GL_MODELVIEW ) - glLoadIdentity(); - self.modelview_matrix_ = glGetDoublev( GL_MODELVIEW_MATRIX ) + glMatrixMode(GL_MODELVIEW) + glLoadIdentity() + self.modelview_matrix_ = glGetDoublev(GL_MODELVIEW_MATRIX) self.set_center([0.0, 0.0, 0.0]) def reset_rotation(self): @@ -142,7 +160,7 @@ def reset_rotation(self): glMatrixMode(GL_MODELVIEW) glLoadMatrixd(self.modelview_matrix_) self.updateGL() - + def translate(self, _trans): # Translate the object by _trans # Update modelview_matrix_ @@ -158,18 +176,20 @@ def translate(self, _trans): self.signalGLMatrixChanged.emit() def rotate(self, _axis, _angle): - t = [self.modelview_matrix_[0][0] * self.center_[0] + - self.modelview_matrix_[1][0] * self.center_[1] + - self.modelview_matrix_[2][0] * self.center_[2] + - self.modelview_matrix_[3][0], - self.modelview_matrix_[0][1] * self.center_[0] + - self.modelview_matrix_[1][1] * self.center_[1] + - self.modelview_matrix_[2][1] * self.center_[2] + - self.modelview_matrix_[3][1], - self.modelview_matrix_[0][2] * self.center_[0] + - self.modelview_matrix_[1][2] * self.center_[1] + - self.modelview_matrix_[2][2] * self.center_[2] + - self.modelview_matrix_[3][2]] + t = [ + self.modelview_matrix_[0][0] * self.center_[0] + + self.modelview_matrix_[1][0] * self.center_[1] + + self.modelview_matrix_[2][0] * self.center_[2] + + self.modelview_matrix_[3][0], + self.modelview_matrix_[0][1] * self.center_[0] + + self.modelview_matrix_[1][1] * self.center_[1] + + self.modelview_matrix_[2][1] * self.center_[2] + + self.modelview_matrix_[3][1], + self.modelview_matrix_[0][2] * self.center_[0] + + self.modelview_matrix_[1][2] * self.center_[1] + + self.modelview_matrix_[2][2] * self.center_[2] + + self.modelview_matrix_[3][2], + ] self.makeCurrent() glLoadIdentity() @@ -181,35 +201,49 @@ def rotate(self, _axis, _angle): self.signalGLMatrixChanged.emit() def view_all(self): - self.translate( [ -( self.modelview_matrix_[0][0] * self.center_[0] + - self.modelview_matrix_[0][1] * self.center_[1] + - self.modelview_matrix_[0][2] * self.center_[2] + - self.modelview_matrix_[0][3]), - -( self.modelview_matrix_[1][0] * self.center_[0] + - self.modelview_matrix_[1][1] * self.center_[1] + - self.modelview_matrix_[1][2] * self.center_[2] + - self.modelview_matrix_[1][3]), - -( self.modelview_matrix_[2][0] * self.center_[0] + - self.modelview_matrix_[2][1] * self.center_[1] + - self.modelview_matrix_[2][2] * self.center_[2] + - self.modelview_matrix_[2][3] + - self.radius_ / 2.0 )]) + self.translate( + [ + -( + self.modelview_matrix_[0][0] * self.center_[0] + + self.modelview_matrix_[0][1] * self.center_[1] + + self.modelview_matrix_[0][2] * self.center_[2] + + self.modelview_matrix_[0][3] + ), + -( + self.modelview_matrix_[1][0] * self.center_[0] + + self.modelview_matrix_[1][1] * self.center_[1] + + self.modelview_matrix_[1][2] * self.center_[2] + + self.modelview_matrix_[1][3] + ), + -( + self.modelview_matrix_[2][0] * self.center_[0] + + self.modelview_matrix_[2][1] * self.center_[1] + + self.modelview_matrix_[2][2] * self.center_[2] + + self.modelview_matrix_[2][3] + + self.radius_ / 2.0 + ), + ] + ) def map_to_sphere(self, _v2D): _v3D = [0.0, 0.0, 0.0] # inside Widget? - if (( _v2D.x() >= 0 ) and ( _v2D.x() <= self.width() ) and - ( _v2D.y() >= 0 ) and ( _v2D.y() <= self.height() ) ): + if ( + (_v2D.x() >= 0) + and (_v2D.x() <= self.width()) + and (_v2D.y() >= 0) + and (_v2D.y() <= self.height()) + ): # map Qt Coordinates to the centered unit square [-0.5..0.5]x[-0.5..0.5] - x = float( _v2D.x() - 0.5 * self.width()) / self.width() - y = float( 0.5 * self.height() - _v2D.y()) / self.height() + x = float(_v2D.x() - 0.5 * self.width()) / self.width() + y = float(0.5 * self.height() - _v2D.y()) / self.height() - _v3D[0] = x; - _v3D[1] = y; + _v3D[0] = x + _v3D[1] = y # use Pythagoras to comp z-coord (the sphere has radius sqrt(2.0*0.5*0.5)) - z2 = 2.0*0.5*0.5-x*x-y*y; + z2 = 2.0 * 0.5 * 0.5 - x * x - y * y # numerical robust sqrt - _v3D[2] = math.sqrt(max( z2, 0.0 )) + _v3D[2] = math.sqrt(max(z2, 0.0)) # normalize direction to unit sphere n = linalg.norm(_v3D) @@ -221,53 +255,63 @@ def map_to_sphere(self, _v2D): def wheelEvent(self, _event): # Use the mouse wheel to zoom in/out - d = - float(_event.angleDelta().y()) / 200.0 * self.radius_ + d = -float(_event.angleDelta().y()) / 200.0 * self.radius_ self.translate([0.0, 0.0, d]) self.updateGL() _event.accept() def selectName(self, point): - glSelectBuffer(500) # allocate a selection buffer of SIZE elements + glSelectBuffer(500) # allocate a selection buffer of SIZE elements glRenderMode(GL_SELECT) - - glMatrixMode( GL_PROJECTION ) + + glMatrixMode(GL_PROJECTION) glPushMatrix() - self.set_pick_projection( point.x(), point.y(), self.near_, self.far_, self.fovy_ ); - - #self.paintGL() + self.set_pick_projection( + point.x(), point.y(), self.near_, self.far_, self.fovy_ + ) + + # self.paintGL() self.drawWithNames() buf = glRenderMode(GL_RENDER) selectedName = -1 closest_z = 1.0 - print('buf ', buf) + print("buf ", buf) for hit_record in buf: min_depth, max_depth, names = hit_record - print('Names ', names) + print("Names ", names) if min_depth < closest_z: closest_z = min_depth for name in names: if name: selectedName = name - print('Selected name ', selectedName) - glMatrixMode( GL_PROJECTION ) + print("Selected name ", selectedName) + glMatrixMode(GL_PROJECTION) glPopMatrix() return selectedName - + def mousePressEvent(self, _event): self.last_point_2D_ = _event.pos() - self.last_point_ok_, self.last_point_3D_ = self.map_to_sphere(self.last_point_2D_) - if (_event.buttons() & QtCore.Qt.LeftButton) and (_event.modifiers() & QtCore.Qt.ShiftModifier): + self.last_point_ok_, self.last_point_3D_ = self.map_to_sphere( + self.last_point_2D_ + ) + if (_event.buttons() & QtCore.Qt.LeftButton) and ( + _event.modifiers() & QtCore.Qt.ShiftModifier + ): name = self.selectName(_event.pos()) self.postSelection(name) def mouseMoveEvent(self, _event): newPoint2D = _event.pos() - if ((newPoint2D.x() < 0) or (newPoint2D.x() > self.width()) or - (newPoint2D.y() < 0) or (newPoint2D.y() > self.height())): + if ( + (newPoint2D.x() < 0) + or (newPoint2D.x() > self.width()) + or (newPoint2D.y() < 0) + or (newPoint2D.y() > self.height()) + ): return - + # Left button: rotate around center_ # Middle button: translate object # Left & middle button: zoom in/out @@ -278,49 +322,60 @@ def mouseMoveEvent(self, _event): dx = float(newPoint2D.x() - self.last_point_2D_.x()) dy = float(newPoint2D.y() - self.last_point_2D_.y()) - w = float(self.width()) - h = float(self.height()) + w = float(self.width()) + h = float(self.height()) # enable GL context self.makeCurrent() # move in z direction - if (((_event.buttons() & QtCore.Qt.LeftButton) and (_event.buttons() & QtCore.Qt.MidButton)) - or (_event.buttons() & QtCore.Qt.LeftButton and _event.modifiers() & QtCore.Qt.ControlModifier)): + if ( + (_event.buttons() & QtCore.Qt.LeftButton) + and (_event.buttons() & QtCore.Qt.MidButton) + ) or ( + _event.buttons() & QtCore.Qt.LeftButton + and _event.modifiers() & QtCore.Qt.ControlModifier + ): print("translating in Z") value_y = self.radius_ * dy * 2.0 / h self.translate([0.0, 0.0, value_y]) # move in x,y direction - elif (_event.buttons() & QtCore.Qt.RightButton): - z = - (self.modelview_matrix_[0][2] * self.center_[0] + - self.modelview_matrix_[1][2] * self.center_[1] + - self.modelview_matrix_[2][2] * self.center_[2] + - self.modelview_matrix_[3][2]) / (self.modelview_matrix_[0][3] * self.center_[0] + - self.modelview_matrix_[1][3] * self.center_[1] + - self.modelview_matrix_[2][3] * self.center_[2] + - self.modelview_matrix_[3][3]) - fovy = 45.0 + elif _event.buttons() & QtCore.Qt.RightButton: + z = -( + self.modelview_matrix_[0][2] * self.center_[0] + + self.modelview_matrix_[1][2] * self.center_[1] + + self.modelview_matrix_[2][2] * self.center_[2] + + self.modelview_matrix_[3][2] + ) / ( + self.modelview_matrix_[0][3] * self.center_[0] + + self.modelview_matrix_[1][3] * self.center_[1] + + self.modelview_matrix_[2][3] * self.center_[2] + + self.modelview_matrix_[3][3] + ) + fovy = 45.0 aspect = w / h - n = 0.01 * self.radius_ - up = math.tan(fovy / 2.0 * math.pi / 180.0) * n - right = aspect * up + n = 0.01 * self.radius_ + up = math.tan(fovy / 2.0 * math.pi / 180.0) * n + right = aspect * up - self.translate( [2.0 * dx / w * right / n * z, - -2.0 * dy / h * up / n * z, - 0.0] ) + self.translate( + [2.0 * dx / w * right / n * z, -2.0 * dy / h * up / n * z, 0.0] + ) # rotate - elif (_event.buttons() & QtCore.Qt.LeftButton and (not _event.modifiers() & QtCore.Qt.ShiftModifier)): - if (not self.isInRotation_): + elif _event.buttons() & QtCore.Qt.LeftButton and ( + not _event.modifiers() & QtCore.Qt.ShiftModifier + ): + if not self.isInRotation_: self.isInRotation_ = True self.rotationBeginEvent.emit() - + axis = [0.0, 0.0, 0.0] angle = 0.0 - if (self.last_point_ok_ and newPoint_hitSphere): + if self.last_point_ok_ and newPoint_hitSphere: axis = numpy.cross(self.last_point_3D_, newPoint3D) cos_angle = numpy.dot(self.last_point_3D_, newPoint3D) - if (abs(cos_angle) < 1.0): + if abs(cos_angle) < 1.0: angle = math.acos(cos_angle) * 180.0 / math.pi angle *= 2.0 self.rotate(axis, angle) @@ -334,16 +389,17 @@ def mouseMoveEvent(self, _event): self.updateGL() def mouseReleaseEvent(self, _event): - if (self.isInRotation_): + if self.isInRotation_: self.isInRotation_ = False self.rotationEndEvent.emit() last_point_ok_ = False -#=============================================================================== + +# =============================================================================== # # Local Variables: # mode: Python # indent-tabs-mode: nil # End: # -#=============================================================================== +# =============================================================================== diff --git a/cellmodeller/gui/renderers.py b/cellmodeller/gui/renderers.py new file mode 100644 index 00000000..fbdb496f --- /dev/null +++ b/cellmodeller/gui/renderers.py @@ -0,0 +1,1746 @@ +from OpenGL.GL import * +from OpenGL.GLU import * +from OpenGL.arrays import vbo +import math +import numpy +import random + +# import pygame # helper module for text rendering - turned off to see if it suppresses segfaults +# from pyopencl.array import vec + + +class GLSphereRenderer: + def __init__(self, sim, properties=None, scales=None): + self.ncells_list = 0 + self.ncells_names_list = 0 + self.dlist = None + self.dlist_names = None + self.cellcol = [1, 1, 1] + self.sim = sim + self.quad = gluNewQuadric() + self.properties = properties + self.scales = scales + + def init_gl(self): + pass + + def build_list(self, cells): + if self.dlist: + glDeleteLists(self.dlist, 1) + index = glGenLists(1) + glNewList(index, GL_COMPILE) + self.render_cells() + glEndList() + self.dlist = index + + def build_list_names(self, cells): + if self.dlist_names: + glDeleteLists(self.dlist_names, 1) + index = glGenLists(1) + glNewList(index, GL_COMPILE) + self.render_cell_names() + glEndList() + self.dlist_names = index + + def render_gl(self, selection=None): + cells = self.sim.cellStates.values() + states = self.sim.cellStates.items() + # FIXED ============================================================================= + # Before, the renderer would only draw cells when the number of cells changed. + # Now it draws them whenever render_gl is called (by paintGL in PyGLCMViewer.py) + # if len(cells)!=self.ncells_list or len(cells)<500: + # self.build_list(cells) + # self.ncells_list = len(cells) + + # if len(cells)!=self.ncells_list: + # self.build_list(cells) + # self.ncells_list = len(cells) + # ==================================================================================== + # glCallList(self.dlist) + self.render_cells(selection=selection) + + def renderNames_gl(self, selection=None): + # cells = self.sim.cellStates.values() + # if len(cells)!=self.ncells_names_list or len(cells)<500: + # self.build_list_names(cells) + # self.ncells_names_list = len(cells) + # glCallList(self.dlist_names) + # for cell in cells: self.render_cell_name(cell, selection) + self.render_cell_names() + + def render_cell_names(self): + # glDisable(GL_DEPTH_TEST) + glDisable(GL_LIGHTING) + for cell in self.sim.cellStates.values(): + l = cell.length + p = cell.pos + + cid = cell.id + glPushName(cid) + + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(p[0], p[1], p[2]) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + + glPopName() + + glEnable(GL_LIGHTING) + + def render_cell(self, cell, selection): + r = cell.radius + p = cell.pos + cid = cell.id + if selection == cid: + cellcol = [1, 0, 0] + else: + cellcol = cell.color + + glColor3fv(cellcol) + + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(p[0], p[1], p[2]) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + + def render_cells(self, selection=None): + + # PLACEHOLDER + + # glDisable(GL_DEPTH_TEST) + glDisable(GL_LIGHTING) + cells = self.sim.cellStates.values() + for cell in cells: + self.render_cell(cell, selection) + + +class GLGridRenderer: + def __init__(self, sig, integ, rng=None, alpha=1.0): + self.sig = sig + self.integ = integ + self.rng = rng + self.alpha = alpha + self.size = self.sig.gridSize + self.dim = self.sig.gridDim[1:] + self.len = self.dim[0] * self.dim[1] + self.orig = [ + self.sig.gridOrig[i] - self.sig.gridSize[i] * 0.5 for i in range(3) + ] + self.texture = glGenTextures(1) + self.imageData = numpy.zeros(self.dim) + + # Create byte array for texture data: OpenGL requires 2^n size and + # square? + self.texDim = max([2 ** math.ceil(math.log(d, 2)) for d in self.dim[0:2]]) + # The texture coordinates to fit the actual data + self.tyMax = self.dim[0] / self.texDim + self.txMax = self.dim[1] / self.texDim + # The texture data, 3 bytes per (x,y,z) + self.byteImageData = numpy.zeros((self.texDim, self.texDim) + (3,)).astype( + numpy.uint8 + ) + + def init_gl(self): + pass + + def renderNames_gl(self): + pass + + def render_gl(self, selection=None): + signalLevel = self.integ.signalLevel.reshape(self.sig.gridDim) + self.imageData = signalLevel[ + :, :, :, math.floor(self.dim[2] * 0.5) + ] # numpy.log(self.sig.level[:,:,:,0]+1e-8) + if self.rng: + mx = self.rng[1] + mn = self.rng[0] + else: + mx = numpy.max(self.imageData) + mn = numpy.min(self.imageData) + if mx > mn: + scale = 255 / (mx - mn) + else: + scale = 1 + self.imageData = (self.imageData - mn) * scale + # print "Signal grid range = %f to %f"%(mn,mx) + for s in range(self.sig.nSignals): + self.byteImageData[0 : self.dim[0], 0 : self.dim[1], s] = self.imageData[ + s, :, : + ].astype(numpy.uint8) + + glEnable(GL_TEXTURE_2D) + glDisable(GL_LIGHTING) + # glDisable(GL_DEPTH_TEST) + glBindTexture(GL_TEXTURE_2D, self.texture) + # Load the Data into the Texture + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGB, + self.texDim, + self.texDim, + 0, + GL_RGB, + GL_UNSIGNED_BYTE, + self.byteImageData, + ) + # glTexSubImage2Dub( GL_TEXTURE_2D, 0, 0,0, GL_RED, GL_UNSIGNED_BYTE, self.imageData ) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) + # Define some Parameters for this Texture + + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glColor4f(1.0, 1.0, 1.0, self.alpha) + glBegin(GL_QUADS) + glTexCoord2f(0.0, 0.0) + glVertex3f( + self.orig[0], self.orig[1], 0.0 + ) # Bottom Left Of The Texture and Quad + glTexCoord2f(0.0, self.tyMax) + glVertex3f( + self.orig[0] + self.dim[0] * self.size[0], self.orig[1], 0.0 + ) # Bottom Right Of The Texture and Quad + glTexCoord2f(self.txMax, self.tyMax) + glVertex3f( + self.orig[0] + self.dim[0] * self.size[0], + self.orig[1] + self.dim[1] * self.size[1], + 0.0, + ) # Top Right Of The Texture and Quad + glTexCoord2f(self.txMax, 0.0) + glVertex3f(self.orig[0], self.orig[1] + self.dim[1] * self.size[1], 0.0) + glEnd() + glEnable(GL_LIGHTING) + # glEnable(GL_DEPTH_TEST) + glDisable(GL_BLEND) + glDisable(GL_TEXTURE_2D) + + +class GLPlantSignalRenderer: + # Render signals as channels rgb + def __init__(self, sim, chanIdx): + self.sim = sim + self.wallcol = [0, 0, 0] + self.nodecol = [0, 0, 0] + self.cellcol = [1, 1, 1] + self.chanIdx = chanIdx + + def init_gl(self): + pass + + def renderNames_gl(self): + pass + + def render_gl(self, selection=None): + # Render signals[chanIdx] as rgb + # + maxSig = [0, 0, 0] + for i in range(len(self.chanIdx)): + for cid, cellState in self.sim.cellStates.items(): + s = cellState.signals[self.chanIdx[i]] + if s > maxSig[i]: + maxSig[i] = s + if maxSig[i] == 0: + maxSig[i] = 1.0 + + for cid, cellState in self.sim.cellStates.items(): + col = [0, 0, 0] + for i in range(len(self.chanIdx)): + col[i] = cellState.signals[self.chanIdx[i]] / maxSig[i] + glColor3fv(col) + glBegin(GL_POLYGON) + for p in cellState.nodep: + glVertex2d(p[0], p[1]) + glEnd() + + +class GLPlantRenderer: + def __init__(self, sim): + self.sim = sim + self.wallcol = [0, 0, 0] + self.nodecol = [0, 0, 0] + self.cellcol = [1, 1, 1] + + def init_gl(self): + pass + + def renderNames_gl(self): + # Render cell_ids for selection + # + for cid, cellState in self.sim.cellStates.items(): + glColor3fv(self.cellcol) + glPushName(cid) + glBegin(GL_POLYGON) + for p in cellState.nodep: + glVertex2d(p[0], p[1]) + glEnd() + glPopName() + + def render_gl(self, selection=None): + # Render model using PyOpenGL + # + # principle axes + # scale=10.0 + # c=cell.get_centre() + # glLineWidth(1) + # glColor3f(1.0,0.0,0.0) #red + # glBegin(GL_LINES) + # glVertex2d(c[0],c[1]) + # glVertex2d(c[0]+cell.pa1[0]*scale,c[1]+cell.pa1[1]*scale) + # glColor3f(0.0,0.0,1.0) #blue + # glVertex2d(c[0],c[1]) + # glVertex2d(c[0]+cell.pa2[0]*scale,c[1]+cell.pa2[1]*scale) + # glEnd() + glDisable(GL_LIGHTING) + glDisable(GL_DEPTH_TEST) + cellStates = self.sim.cellStates + + # for (cid,cellState) in cellStates.items(): + # cellcol = cellState.color + # glColor3fv(cellcol) + # glBegin(GL_POLYGON) + # for p in cellState.nodep: + # glVertex2d(p[0], p[1]) + # glEnd() + for cid, cellState in cellStates.items(): + col = self.wallcol + for wp1, wp2 in cellState.wallp: + glColor3fv(col) + glLineWidth(2) + glBegin(GL_LINES) + glVertex2d(wp1[0], wp1[1]) + glVertex2d(wp2[0], wp2[1]) + glEnd() + + if selection > 0 and cellStates.has_key(selection): + scell = cellStates[selection] + glColor3fv([1, 0, 0]) + for wp1, wp2 in scell.wallp: + glLineWidth(2) + glBegin(GL_LINES) + glVertex2d(wp1[0], wp1[1]) + glVertex2d(wp2[0], wp2[1]) + glEnd() + + # Draw nodes as points + # glColor3fv(self.nodecol) + # glPointSize(3) + + # for node in self.model.nodes: + # p = node.get_pos() + # glBegin(GL_POINTS) + # glVertex2d(p[0], p[1]) + # glEnd() + # r = node.get_rot() + # glBegin(GL_LINES) + # glVertex2d(p[0], p[1]) + # glVertex2d(p[0] + math.cos(r), p[1] + math.sin(r)) + # glEnd() + + glEnable(GL_LIGHTING) + + +class GLBacteriumRenderer: + def __init__(self, sim, properties=None, scales=None): + self.ncells_list = 0 + self.ncells_names_list = 0 + self.dlist = None + self.dlist_names = None + self.cellcol = [1, 1, 1] + self.sim = sim + self.quad = gluNewQuadric() + self.properties = properties + self.scales = scales + self.last_rendered_step = -1 + + def init_gl(self): + pass + + # def render_text(self, position, textString, fontSize): + # pygame.font.init() + # font = pygame.font.Font (None, fontSize) + # textSurface = font.render(textString, True, (0,0,0,225), (255,255,255,255)) + # textData = pygame.image.tostring(textSurface, "RGBA", True) + # glRasterPos3d(*position) + # glDrawPixels(textSurface.get_width(), textSurface.get_height(), GL_RGBA, GL_UNSIGNED_BYTE, textData) + + def build_list(self, cells): + if self.dlist: + glDeleteLists(self.dlist, 1) + index = glGenLists(1) + glNewList(index, GL_COMPILE) + self.render_cells() + glEndList() + self.dlist = index + + def build_list_names(self, cells): + if self.dlist_names: + glDeleteLists(self.dlist_names, 1) + index = glGenLists(1) + glNewList(index, GL_COMPILE) + self.render_cell_names() + glEndList() + self.dlist_names = index + + def render_gl(self, selection=None): + cells = self.sim.cellStates.values() + states = self.sim.cellStates.items() + # FIXED ============================================================================= + # Before, the renderer would only draw cells when the number of cells changed. + # Now it draws them whenever render_gl is called (by paintGL in PyGLCMViewer.py) + # if len(cells)!=self.ncells_list or len(cells)<500: + # self.build_list(cells) + # self.ncells_list = len(cells) + + # if len(cells)!=self.ncells_list: + if self.last_rendered_step < self.sim.stepNum: + self.build_list(cells) + self.ncells_list = len(cells) + self.last_rendered_step = self.sim.stepNum + # ==================================================================================== + glCallList(self.dlist) + # self.render_cells(selection=selection) + + def renderNames_gl(self, selection=None): + # cells = self.sim.cellStates.values() + # if len(cells)!=self.ncells_names_list or len(cells)<500: + # self.build_list_names(cells) + # self.ncells_names_list = len(cells) + # glCallList(self.dlist_names) + # for cell in cells: self.render_cell_name(cell, selection) + self.render_cell_names() + + def render_cell_names(self): + # glDisable(GL_DEPTH_TEST) + glDisable(GL_LIGHTING) + for cell in self.sim.cellStates.values(): + l = cell.length + r = cell.radius + + e1, e2 = cell.ends + ae1 = numpy.array(e1) + ae2 = numpy.array(e2) + zaxis = numpy.array([0, 0, 1]) + caxis = numpy.array(cell.dir) # (ae2-ae1)/l + rotaxis = numpy.cross(caxis, zaxis) + rotangle = numpy.arccos(numpy.dot(caxis, zaxis)) + + cid = cell.id + glPushName(cid) + + """ + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1[0],e1[1],e1[2]) + glRotatef(-rotangle*180.0/numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2]) + gluCylinder(self.quad, r, r , l, 8, 1) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + glPushMatrix() + glTranslatef(e2[0],e2[1],e2[2]) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + """ + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1[0], e1[1], e1[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + # glScalef(1.25,1.0,1.0) + glRotatef(-rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2]) + gluCylinder(self.quad, r, r, l * 1.25, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2[0], e2[1], e2[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + + glPopName() + + glEnable(GL_LIGHTING) + + def render_cell(self, cell, selection): + l = cell.length + # r = cell.radius*2.0 + r = cell.radius + + e1, e2 = cell.ends + ae1 = numpy.array(e1) + ae2 = numpy.array(e2) + zaxis = numpy.array([0, 0, 1]) + caxis = numpy.array(cell.dir) # (ae2-ae1)/l + rotaxis = numpy.cross(caxis, zaxis) + rotangle = numpy.arccos(numpy.dot(caxis, zaxis)) + + cid = cell.id + cidx = cell.idx + if False: + self.render_text(0.5 * (e1 + e2), str(cidx), 24) + + if selection == cid: + linecol = [1, 0, 0] + else: + linecol = [1, 1, 1] + cellcol = cell.color + if self.properties: + cellcol = [] + for p in self.properties: + if hasattr(cell, p): + cellcol.append(getattr(cell, p)) + else: + cellcol.append(0) + for i in range(3): + cellcol[i] *= self.scales[i] + cellcol[i] = min(1, cellcol[i]) + + # draw the outlines antialiased in black + glColor3fv(linecol) + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glEnable(GL_LINE_SMOOTH) + glLineWidth(8.0) + # draw wireframe for back facing polygons and cull front-facing ones + glPolygonMode(GL_BACK, GL_FILL) + glEnable(GL_CULL_FACE) + glCullFace(GL_FRONT) + glDepthFunc(GL_LEQUAL) + + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1[0], e1[1], e1[2]) + gluSphere(self.quad, r, 8, 8) + glRotatef(-rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2]) + gluCylinder(self.quad, r, r, l, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2[0], e2[1], e2[2]) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + + glDepthFunc(GL_LESS) + glDisable(GL_CULL_FACE) + glPolygonMode(GL_FRONT, GL_FILL) + glDisable(GL_LINE_SMOOTH) + glDisable(GL_BLEND) + + glColor3fv(cellcol) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1[0], e1[1], e1[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + # glScalef(1.25,1.0,1.0) + glRotatef(-rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2]) + gluCylinder(self.quad, r, r, l * 1.25, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2[0], e2[1], e2[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + + # glColor3f(68.0 / 256, 81.0 / 256, 44.0 / 256) + # glLineWidth(2) + # glBegin(GL_LINES) + # glVertex3f(e1[0], e1[1], e1[2]) + # glVertex3f(e2[0], e2[1], e2[2]) + # glEnd() + # + # glColor3f(1.0, 1.0, 0.0) + # glPointSize(3) + # glBegin(GL_POINTS) + # glVertex3f(e1[0], e1[1], e1[2]) + # glVertex3f(e2[0], e2[1], e2[2]) + # glEnd() + + # draw contact points + if False: # hasattr(cells[0], 'contacts'): + glDisable(GL_DEPTH_TEST) + glDisable(GL_LIGHTING) + for cell in cells: + contacts = cell.contacts + glBegin(GL_LINES) + for ct in contacts: + glColor3fv(ct[6:9]) + glVertex3fv(ct[0:3]) + glVertex3fv(ct[3:6]) + glEnd() + glBegin(GL_POINTS) + for ct in contacts: + glColor3fv(ct[6:9]) + glVertex3fv(ct[0:3]) + glEnd() + glEnable(GL_DEPTH_TEST) + glEnable(GL_LIGHTING) + + def render_cells(self, selection=None): + + # PLACEHOLDER + + # glDisable(GL_DEPTH_TEST) + glDisable(GL_LIGHTING) + cells = self.sim.cellStates.values() + for cell in cells: + self.render_cell(cell, selection) + + +class GLBacteriumRendererWithPeriodicImages: + def __init__(self, sim, properties=None, scales=None): + self.ncells_list = 0 + self.ncells_names_list = 0 + self.dlist = None + self.dlist_names = None + self.cellcol = [1, 1, 1] + self.sim = sim + self.quad = gluNewQuadric() + self.properties = properties + self.scales = scales + + def init_gl(self): + pass + + def build_list(self, cells): + if self.dlist: + glDeleteLists(self.dlist, 1) + index = glGenLists(1) + glNewList(index, GL_COMPILE) + self.render_cells() + textString = "CellModeller4 Development Version" # self.render_text(position, textString, 40) + position = numpy.array([-40.0, 30.0, 0.0]) + glEndList() + self.dlist = index + + def build_list_names(self, cells): + if self.dlist_names: + glDeleteLists(self.dlist_names, 1) + index = glGenLists(1) + glNewList(index, GL_COMPILE) + self.render_cell_names() + glEndList() + self.dlist_names = index + + def render_gl(self, selection=None): + cells = self.sim.cellStates.values() + states = self.sim.cellStates.items() + # FIXED ============================================================================= + # Before, the renderer would only draw cells when the number of cells changed. + # Now it draws them whenever render_gl is called (by paintGL in PyGLCMViewer.py) + + names = False + if not names: + self.build_list(cells) + self.ncells_list = len(cells) + glCallList(self.dlist) + else: + # added to try and render cell ids + self.build_list_names(cells) + self.ncells_names_list = len(cells) + glCallList(self.dlist_names) + + # if len(cells)!=self.ncells_list: + # self.build_list(cells) + # self.ncells_list = len(cells) + + # ==================================================================================== + # glCallList(self.dlist) + # glCallList(self.dlist_names) + # for cell in cells: self.render_cell(cell, selection) + + def renderNames_gl(self, selection=None): + cells = self.sim.cellStates.values() + if len(cells) != self.ncells_names_list: + self.build_list_names(cells) + self.ncells_names_list = len(cells) + glCallList(self.dlist_names) + # for cell in cells: self.render_cell_name(cell, selection) + + def render_cell_names(self): + # glDisable(GL_DEPTH_TEST) + glDisable(GL_LIGHTING) + for cell in self.sim.cellStates.values(): + l = cell.length + r = cell.radius + + e1, e2 = cell.ends + ae1 = numpy.array(e1) + ae2 = numpy.array(e2) + zaxis = numpy.array([0, 0, 1]) + caxis = numpy.array(cell.dir) # (ae2-ae1)/l + rotaxis = numpy.cross(caxis, zaxis) + rotangle = numpy.arccos(numpy.dot(caxis, zaxis)) + + cid = cell.id + glPushName(cid) + + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1[0], e1[1], e1[2]) + glRotatef(-rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2]) + gluCylinder(self.quad, r, r, l, 8, 1) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + glPushMatrix() + glTranslatef(e2[0], e2[1], e2[2]) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + + glPopName() + + glEnable(GL_LIGHTING) + + # def render_text(self, position, textString, fontSize): + # pygame.font.init() + # font = pygame.font.Font (None, fontSize) + # textSurface = font.render(textString, True, (0,0,0,225), (255,255,255,255)) + # textData = pygame.image.tostring(textSurface, "RGBA", True) + # glRasterPos3d(*position) + # glDrawPixels(textSurface.get_width(), textSurface.get_height(), GL_RGBA, GL_UNSIGNED_BYTE, textData) + # #glClear(GL_DEPTH_BUFFER_BIT) + + def render_cells(self, selection=None): + # glDisable(GL_DEPTH_TEST) + glDisable(GL_LIGHTING) + cells = self.sim.cellStates.values() + for cell in cells: + l = cell.length + # r = cell.radius*2.0 + r = cell.radius + + e1, e2 = cell.ends + + L_x = self.sim.phys.max_x_coord - self.sim.phys.min_x_coord + L_y = self.sim.phys.max_y_coord - self.sim.phys.min_y_coord + offset_x = numpy.array([L_x, 0.0, 0.0]) + offset_y = numpy.array([0.0, L_y, 0.0]) + + # top image + e1_t = e1 + offset_y + e2_t = e2 + offset_y + # bottom image + e1_b = e1 - offset_y + e2_b = e2 - offset_y + # left image + e1_l = e1 - offset_x + e2_l = e2 - offset_x + # right image + e1_r = e1 + offset_x + e2_r = e2 + offset_x + + zaxis = numpy.array([0, 0, 1]) + caxis = numpy.array(cell.dir) # (ae2-ae1)/l + rotaxis = numpy.cross(caxis, zaxis) + rotangle = numpy.arccos(numpy.dot(caxis, zaxis)) + + cid = cell.id + # ======================================================== Adding cell labels + cidx = cell.idx + # if self.sim.render_labels: + if False: + self.render_text(0.5 * (e1 + e2), str(cidx), 24) + # ======================================================== + if selection == cid: + cellcol = [1, 0, 0] + else: + cellcol = ( + cell.color + ) # self.cellcol #[random.uniform(0,1), random.uniform(0,1), random.uniform(0,1)] + if self.properties: + cellcol = [] + for p in self.properties: + if hasattr(cell, p): + cellcol.append(getattr(cell, p)) + else: + cellcol.append(0) + for i in range(3): + cellcol[i] *= self.scales[i] + cellcol[i] = min(1, cellcol[i]) + + # image cells are grey + cellcol2 = numpy.array([0.9, 0.9, 0.9]) + + # main image + # draw the outlines initialized in black + glColor3f(0.0, 0.0, 0.0) + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glEnable(GL_LINE_SMOOTH) + glLineWidth(8.0) + # draw wireframe for back facing polygons and cull front-facing ones + glPolygonMode(GL_BACK, GL_FILL) + glEnable(GL_CULL_FACE) + glCullFace(GL_FRONT) + glDepthFunc(GL_LEQUAL) + + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1[0], e1[1], e1[2]) + gluSphere(self.quad, r, 8, 8) + glRotatef(-rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2]) + gluCylinder(self.quad, r, r, l, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2[0], e2[1], e2[2]) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + # FIXME ========================================= + # () + # ================================================ + + glDepthFunc(GL_LESS) + glDisable(GL_CULL_FACE) + glPolygonMode(GL_FRONT, GL_FILL) + glDisable(GL_LINE_SMOOTH) + glDisable(GL_BLEND) + + glColor3fv(cellcol) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1[0], e1[1], e1[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + # glScalef(1.25,1.0,1.0) + glRotatef(-rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2]) + gluCylinder(self.quad, r, r, l * 1.25, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2[0], e2[1], e2[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + + # draw contact points + # ========================================================= + if hasattr(cells[0], "contacts"): + # ========================================================= used to always be false but it won't work anyway - see below + glDisable(GL_DEPTH_TEST) + glDisable(GL_LIGHTING) + for cell in cells: + contacts = ( + cell.contacts + ) # this won't work because cellStates no longer have a contacts attribute + glBegin(GL_LINES) + for ct in contacts: + glColor3fv(ct[6:9]) + glVertex3fv(ct[0:3]) + glVertex3fv(ct[3:6]) + glEnd() + glBegin(GL_POINTS) + for ct in contacts: + glColor3fv(ct[6:9]) + glVertex3fv(ct[0:3]) + glEnd() + glEnable(GL_DEPTH_TEST) + glEnable(GL_LIGHTING) + + # top image + # ========================================== + # draw the outlines initialized in black + glColor3f(0.0, 0.0, 0.0) + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glEnable(GL_LINE_SMOOTH) + glLineWidth(8.0) + + # draw wireframe for back facing polygons and cull front-facing ones + glPolygonMode(GL_BACK, GL_FILL) + glEnable(GL_CULL_FACE) + glCullFace(GL_FRONT) + glDepthFunc(GL_LEQUAL) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1_t[0], e1_t[1], e1_t[2]) + gluSphere(self.quad, r, 8, 8) + glRotatef(-rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2]) + gluCylinder(self.quad, r, r, l, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2_t[0], e2_t[1], e2_t[2]) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + + glDepthFunc(GL_LESS) + glDisable(GL_CULL_FACE) + glPolygonMode(GL_FRONT, GL_FILL) + glDisable(GL_LINE_SMOOTH) + glDisable(GL_BLEND) + + glColor3fv(cellcol2) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1_t[0], e1_t[1], e1_t[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + # glScalef(1.25,1.0,1.0) + glRotatef(-rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2]) + gluCylinder(self.quad, r, r, l * 1.25, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2_t[0], e2_t[1], e2_t[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + # ============================================ + + # bottom image + # ========================================== + # draw the outlines initialized in black + glColor3f(0.0, 0.0, 0.0) + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glEnable(GL_LINE_SMOOTH) + glLineWidth(8.0) + + # draw wireframe for back facing polygons and cull front-facing ones + glPolygonMode(GL_BACK, GL_FILL) + glEnable(GL_CULL_FACE) + glCullFace(GL_FRONT) + glDepthFunc(GL_LEQUAL) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1_b[0], e1_b[1], e1_b[2]) + gluSphere(self.quad, r, 8, 8) + glRotatef(-rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2]) + gluCylinder(self.quad, r, r, l, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2_b[0], e2_b[1], e2_b[2]) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + + glDepthFunc(GL_LESS) + glDisable(GL_CULL_FACE) + glPolygonMode(GL_FRONT, GL_FILL) + glDisable(GL_LINE_SMOOTH) + glDisable(GL_BLEND) + + glColor3fv(cellcol2) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1_b[0], e1_b[1], e1_b[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + # glScalef(1.25,1.0,1.0) + glRotatef(-rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2]) + gluCylinder(self.quad, r, r, l * 1.25, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2_b[0], e2_b[1], e2_b[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + # ============================================ + + # left image + # ========================================== + # draw the outlines initialized in black + glColor3f(0.0, 0.0, 0.0) + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glEnable(GL_LINE_SMOOTH) + glLineWidth(8.0) + + # draw wireframe for back facing polygons and cull front-facing ones + glPolygonMode(GL_BACK, GL_FILL) + glEnable(GL_CULL_FACE) + glCullFace(GL_FRONT) + glDepthFunc(GL_LEQUAL) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1_l[0], e1_l[1], e1_l[2]) + gluSphere(self.quad, r, 8, 8) + glRotatef(-rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2]) + gluCylinder(self.quad, r, r, l, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2_l[0], e2_l[1], e2_l[2]) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + + glDepthFunc(GL_LESS) + glDisable(GL_CULL_FACE) + glPolygonMode(GL_FRONT, GL_FILL) + glDisable(GL_LINE_SMOOTH) + glDisable(GL_BLEND) + + glColor3fv(cellcol2) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1_l[0], e1_l[1], e1_l[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + # glScalef(1.25,1.0,1.0) + glRotatef(-rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2]) + gluCylinder(self.quad, r, r, l * 1.25, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2_l[0], e2_l[1], e2_l[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + # ============================================ + # right image + # ========================================== + # draw the outlines initialized in black + glColor3f(0.0, 0.0, 0.0) + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glEnable(GL_LINE_SMOOTH) + glLineWidth(8.0) + + # draw wireframe for back facing polygons and cull front-facing ones + glPolygonMode(GL_BACK, GL_FILL) + glEnable(GL_CULL_FACE) + glCullFace(GL_FRONT) + glDepthFunc(GL_LEQUAL) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1_r[0], e1_r[1], e1_r[2]) + gluSphere(self.quad, r, 8, 8) + glRotatef(-rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2]) + gluCylinder(self.quad, r, r, l, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2_r[0], e2_r[1], e2_r[2]) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + + glDepthFunc(GL_LESS) + glDisable(GL_CULL_FACE) + glPolygonMode(GL_FRONT, GL_FILL) + glDisable(GL_LINE_SMOOTH) + glDisable(GL_BLEND) + + glColor3fv(cellcol2) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1_r[0], e1_r[1], e1_r[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + # glScalef(1.25,1.0,1.0) + glRotatef(-rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2]) + gluCylinder(self.quad, r, r, l * 1.25, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2_r[0], e2_r[1], e2_r[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + # ============================================ + if True: + # prepare diagonal images + # top right image + e1_tr = e1 + offset_y + offset_x + e2_tr = e2 + offset_y + offset_x + # bottom right image + e1_br = e1 - offset_y + offset_x + e2_br = e2 - offset_y + offset_x + # top left image + e1_tl = e1 - offset_x + offset_y + e2_tl = e2 - offset_x + offset_y + # bottom left image + e1_bl = e1 - offset_x - offset_y + e2_bl = e2 - offset_x - offset_y + # top-right image + # ========================================== + # draw the outlines initialized in black + glColor3f(0.0, 0.0, 0.0) + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glEnable(GL_LINE_SMOOTH) + glLineWidth(8.0) + + # draw wireframe for back facing polygons and cull front-facing ones + glPolygonMode(GL_BACK, GL_FILL) + glEnable(GL_CULL_FACE) + glCullFace(GL_FRONT) + glDepthFunc(GL_LEQUAL) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1_tr[0], e1_tr[1], e1_tr[2]) + gluSphere(self.quad, r, 8, 8) + glRotatef( + -rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2] + ) + gluCylinder(self.quad, r, r, l, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2_tr[0], e2_tr[1], e2_tr[2]) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + + glDepthFunc(GL_LESS) + glDisable(GL_CULL_FACE) + glPolygonMode(GL_FRONT, GL_FILL) + glDisable(GL_LINE_SMOOTH) + glDisable(GL_BLEND) + + glColor3fv(cellcol2) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1_tr[0], e1_tr[1], e1_tr[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + # glScalef(1.25,1.0,1.0) + glRotatef( + -rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2] + ) + gluCylinder(self.quad, r, r, l * 1.25, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2_tr[0], e2_tr[1], e2_tr[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + # ============================================ + + # bottom right image + # ========================================== + # draw the outlines initialized in black + glColor3f(0.0, 0.0, 0.0) + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glEnable(GL_LINE_SMOOTH) + glLineWidth(8.0) + + # draw wireframe for back facing polygons and cull front-facing ones + glPolygonMode(GL_BACK, GL_FILL) + glEnable(GL_CULL_FACE) + glCullFace(GL_FRONT) + glDepthFunc(GL_LEQUAL) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1_br[0], e1_br[1], e1_br[2]) + gluSphere(self.quad, r, 8, 8) + glRotatef( + -rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2] + ) + gluCylinder(self.quad, r, r, l, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2_br[0], e2_br[1], e2_br[2]) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + + glDepthFunc(GL_LESS) + glDisable(GL_CULL_FACE) + glPolygonMode(GL_FRONT, GL_FILL) + glDisable(GL_LINE_SMOOTH) + glDisable(GL_BLEND) + + glColor3fv(cellcol2) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1_br[0], e1_br[1], e1_br[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + # glScalef(1.25,1.0,1.0) + glRotatef( + -rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2] + ) + gluCylinder(self.quad, r, r, l * 1.25, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2_br[0], e2_br[1], e2_br[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + # ============================================ + + # top left image + # ========================================== + # draw the outlines initialized in black + glColor3f(0.0, 0.0, 0.0) + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glEnable(GL_LINE_SMOOTH) + glLineWidth(8.0) + + # draw wireframe for back facing polygons and cull front-facing ones + glPolygonMode(GL_BACK, GL_FILL) + glEnable(GL_CULL_FACE) + glCullFace(GL_FRONT) + glDepthFunc(GL_LEQUAL) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1_tl[0], e1_tl[1], e1_tl[2]) + gluSphere(self.quad, r, 8, 8) + glRotatef( + -rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2] + ) + gluCylinder(self.quad, r, r, l, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2_tl[0], e2_tl[1], e2_tl[2]) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + + glDepthFunc(GL_LESS) + glDisable(GL_CULL_FACE) + glPolygonMode(GL_FRONT, GL_FILL) + glDisable(GL_LINE_SMOOTH) + glDisable(GL_BLEND) + + glColor3fv(cellcol2) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1_tl[0], e1_tl[1], e1_tl[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + # glScalef(1.25,1.0,1.0) + glRotatef( + -rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2] + ) + gluCylinder(self.quad, r, r, l * 1.25, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2_tl[0], e2_tl[1], e2_tl[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + # ============================================ + # bottom left image + # ========================================== + # draw the outlines initialized in black + glColor3f(0.0, 0.0, 0.0) + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glEnable(GL_LINE_SMOOTH) + glLineWidth(8.0) + + # draw wireframe for back facing polygons and cull front-facing ones + glPolygonMode(GL_BACK, GL_FILL) + glEnable(GL_CULL_FACE) + glCullFace(GL_FRONT) + glDepthFunc(GL_LEQUAL) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1_bl[0], e1_bl[1], e1_bl[2]) + gluSphere(self.quad, r, 8, 8) + glRotatef( + -rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2] + ) + gluCylinder(self.quad, r, r, l, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2_bl[0], e2_bl[1], e2_bl[2]) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + + glDepthFunc(GL_LESS) + glDisable(GL_CULL_FACE) + glPolygonMode(GL_FRONT, GL_FILL) + glDisable(GL_LINE_SMOOTH) + glDisable(GL_BLEND) + + glColor3fv(cellcol2) + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + glTranslatef(e1_bl[0], e1_bl[1], e1_bl[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + # glScalef(1.25,1.0,1.0) + glRotatef( + -rotangle * 180.0 / numpy.pi, rotaxis[0], rotaxis[1], rotaxis[2] + ) + gluCylinder(self.quad, r, r, l * 1.25, 8, 1) + glPopMatrix() + glPushMatrix() + glTranslatef(e2_bl[0], e2_bl[1], e2_bl[2]) + glScalef(0.8, 0.8, 0.8) + gluSphere(self.quad, r, 8, 8) + glPopMatrix() + # ============================================ + # glColor3f(68.0 / 256, 81.0 / 256, 44.0 / 256) + # glLineWidth(2) + # glBegin(GL_LINES) + # glVertex3f(e1[0], e1[1], e1[2]) + # glVertex3f(e2[0], e2[1], e2[2]) + # glEnd() + # + # glColor3f(1.0, 1.0, 0.0) + # glPointSize(3) + # glBegin(GL_POINTS) + # glVertex3f(e1[0], e1[1], e1[2]) + # glVertex3f(e2[0], e2[1], e2[2]) + # glEnd() + + +class GLWillsMeshRenderer: + def __init__(self, sim): + self.sim = sim + + def render_gl(self, selection=None): # is it necessary to have this method? YES + self.render_mesh() + + def render_mesh(self): + + # choose mesh color + glColor3f(0, 1, 0) # green + + # get domain info + min_x = self.sim.phys.min_x_coord + min_y = self.sim.phys.min_y_coord + max_x = self.sim.phys.max_x_coord + max_y = self.sim.phys.max_y_coord + nx = self.sim.phys.grid_x_max - self.sim.phys.grid_x_min + ny = self.sim.phys.grid_y_max - self.sim.phys.grid_y_min + z_offset = -0.1 + h = self.sim.phys.grid_spacing + glLineWidth(2) + + # render lines in x direction + for i in range(0, ny + 1): + glBegin(GL_LINES) + glVertex3f(min_x, min_y + i * h, z_offset) + glVertex3f(max_x, min_y + i * h, z_offset) + glEnd() + + # render lines in y direction + for j in range(0, nx + 1): + glBegin(GL_LINES) + glVertex3f(min_x + j * h, min_y, z_offset) + glVertex3f(min_x + j * h, max_y, z_offset) + glEnd() + + +class GLStaticMeshRenderer: + def __init__(self, mesh, regul): + self.mesh = mesh + self.regul = regul + self.vbo = None + self.vboOffsets = {} + self.nverts = 0 + + def init_gl(self): + self.makeVBOs() + + def makeVBOs(self): + # print "Making VBO for triangle mesh..." + cells = self.mesh.getCells() + self.nverts = 0 + for c in cells: + self.nverts += len(c.tris) * 3 + + self.normals = numpy.zeros((self.nverts, 3)) + # ni = 0 + # for c in cells: + # for t in c.tris: + # v1,v2,v3 = self.mesh.verts[t[0]], self.mesh.verts[t[1]], self.mesh.verts[t[2]] + # v1 = numpy.array(v1) + # v2 = numpy.array(v2) + # v3 = numpy.array(v3) + # n = -numpy.cross((v2-v1),(v3-v1)) + # n /= math.sqrt(numpy.dot(n,n)) + # self.normals[ni,:] = n + # self.normals[ni+1,:] = n + # self.normals[ni+2,:] = n + # ni += 3 + + varr = [] + voff = 0 + for c in cells: + self.vboOffsets[c.getId()] = voff + for t in c.tris: + for ti in t: + for i in xrange(3): + varr.append(self.mesh.verts[ti][i]) + for i in xrange(3): + varr.append(self.normals[voff][i]) + for i in xrange(2): + varr.append(0.0) # pad to align to 32byte blocks + voff += 1 + self.vbo = vbo.VBO(numpy.array(varr, "f")) + + def render_gl(self, selection=None): + if not self.vbo: + self.init_gl() + + states = self.regul.cellStates + + glDisable(GL_LIGHTING) + glEnable(GL_DEPTH_TEST) + glShadeModel(GL_FLAT) + glCullFace(GL_FRONT) + glEnable(GL_CULL_FACE) + + self.vbo.bind() + glEnableClientState(GL_VERTEX_ARRAY) + # glEnableClientState(GL_NORMAL_ARRAY); + + glEnable(GL_POLYGON_OFFSET_FILL) + glPolygonOffset(1.0, 1.0) + + glVertexPointer(3, GL_FLOAT, 32, self.vbo) + # glNormalPointer(GL_FLOAT, 32, self.vbo+12 ) + for c in self.mesh.getCells(): + cid = c.getId() + if selection != cid: + s = states[cid] + if len(s.signals) > 0: + glColor4f(s.signals[0], 0, 0, 1) + else: + glColor4fv(c.color) # c.color[0:3]) + else: + glColor4f(1, 0, 0, 1) + glDrawArrays(GL_TRIANGLES, self.vboOffsets[cid], len(c.tris) * 3) + + glDisable(GL_POLYGON_OFFSET_FILL) + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glColor4f(0, 0, 0, 0.5) + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) + glDrawArrays(GL_TRIANGLES, 0, self.nverts) + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) + + self.vbo.unbind() + glDisableClientState(GL_VERTEX_ARRAY) + # glDisableClientState(GL_NORMAL_ARRAY); + + # glEnable(GL_LIGHTING) + # glDisable(GL_BLEND) #DEPTH_TEST) + # glDisable(GL_DEPTH_TEST) + + def renderNames_gl(self): + if not self.vbo: + self.init_gl() + + glDisable(GL_LIGHTING) + glDisable(GL_DEPTH_TEST) + # glCullFace(GL_FRONT) + glDisable(GL_CULL_FACE) + + self.vbo.bind() + glEnableClientState(GL_VERTEX_ARRAY) + + glVertexPointer(3, GL_FLOAT, 32, self.vbo) + for c in self.mesh.getCells(): + cid = c.getId() + glPushName(cid) + glDrawArrays(GL_TRIANGLES, self.vboOffsets[cid], len(c.tris) * 3) + glPopName() + + self.vbo.unbind() + glDisableClientState(GL_VERTEX_ARRAY) + glEnable(GL_LIGHTING) + + +class GLCelBacteriumRenderer: + def __init__(self, sim, properties=None, scales=None): + self.cellcolor = [0.4, 0.6, 0.5] + self.sim = sim + self.properties = properties + self.scales = scales + self.init_gl() + + def init_gl(self): + # display lists for drawing cells + self.cylinder = self.build_cylinder(16) + self.hemisphere = self.build_hemisphere(16, 16) + # set up the 1d texture used for cel-shading 'lighting' + self.texture = glGenTextures(1) + glBindTexture(GL_TEXTURE_1D, self.texture) + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) + self.tex_img = [0.2] * (7 * 3) + [0.6] * (10 * 3) + [0.9] * (15 * 3) + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, 32, 0, GL_RGB, GL_FLOAT, self.tex_img) + glEnable(GL_TEXTURE_1D) + glBindTexture(GL_TEXTURE_1D, self.texture) + # we'll do our own 'lighting' + glDisable(GL_LIGHTING) + + def build_cylinder(self, n_phis): + """Return the id of a display list that draws a cylinder. + + The cylinder is along the x axis from -1/2 to 1/2. It has a + circular cross-section with radius 1. + + n_phis -- The number of rectangular panels with which to + approximate the cylinder. + """ + index = glGenLists(1) + phis = [float(i) * 2.0 * numpy.pi / float(n_phis) for i in range(n_phis + 1)] + phi_pairs = zip(phis, phis[1:]) + glNewList(index, GL_COMPILE) + glBegin(GL_QUADS) + for phi1, phi2 in phi_pairs: + dot1 = min(max(numpy.cos(phi1), 0.0), 1.0) + dot2 = min(max(numpy.cos(phi2), 0.0), 1.0) + glTexCoord1f(dot1) + glVertex3f(-0.5, numpy.sin(phi1), numpy.cos(phi1)) + glTexCoord1f(dot1) + glVertex3f(0.5, numpy.sin(phi1), numpy.cos(phi1)) + glTexCoord1f(dot2) + glVertex3f(0.5, numpy.sin(phi2), numpy.cos(phi2)) + glTexCoord1f(dot2) + glVertex3f(-0.5, numpy.sin(phi2), numpy.cos(phi2)) + glEnd() + glEndList() + return index + + def build_hemisphere(self, n_phis, n_thetas): + """Return the id of a display list that draw a hemisphere. + + The hemisphere is in the +x half of the x axis and opens on the x=0 plane. It has a radius of 1. + + n_phis -- Number of latitudes to use. There are actually n_phis/2+1 latitudes. + n_thetas -- Number of longitudes to use. + """ + index = glGenLists(1) + phis = [float(i) * numpy.pi / float(n_phis / 2) for i in range(n_phis / 2 + 1)] + phi_pairs = zip(phis, phis[1:]) + thetas = [ + float(i) * numpy.pi / float(n_thetas) + for i in range(-n_thetas / 2, n_thetas / 2 + 1) + ] + theta_pairs = zip(thetas, thetas[1:]) + glNewList(index, GL_COMPILE) + glBegin(GL_QUADS) + for phi1, phi2 in phi_pairs: + dot1 = min(max(numpy.cos(phi1), 0.0), 1.0) + dot2 = min(max(numpy.cos(phi2), 0.0), 1.0) + for th1, th2 in theta_pairs: + glTexCoord1f(dot1) + glVertex3f( + numpy.sin(phi1) * numpy.cos(th1), + numpy.sin(phi1) * numpy.sin(th1), + numpy.cos(phi1), + ) + glTexCoord1f(dot2) + glVertex3f( + numpy.sin(phi2) * numpy.cos(th1), + numpy.sin(phi2) * numpy.sin(th1), + numpy.cos(phi2), + ) + glTexCoord1f(dot2) + glVertex3f( + numpy.sin(phi2) * numpy.cos(th2), + numpy.sin(phi2) * numpy.sin(th2), + numpy.cos(phi2), + ) + glTexCoord1f(dot1) + glVertex3f( + numpy.sin(phi1) * numpy.cos(th2), + numpy.sin(phi1) * numpy.sin(th2), + numpy.cos(phi1), + ) + glEnd() + glEndList() + return index + + def render_capsule(self, l, r): + """Draw a capsule at the origin along the x axis. + + l -- length of the capsule. + r -- radius of the capsule. + """ + # draw cylinder + glPushMatrix() + glScalef(l, r, r) + glCallList(self.cylinder) + glPopMatrix() + # draw +x hemisphere + glPushMatrix() + glTranslatef(l / 2.0, 0, 0) + glScalef(r, r, r) + glCallList(self.hemisphere) + glPopMatrix() + # draw -x hemisphere + glPushMatrix() + glRotatef(180.0, 0, 0, 1) + glTranslatef(l / 2.0, 0, 0) + glScalef(r, r, r) + glCallList(self.hemisphere) + glPopMatrix() + + def render_cell(self, cell, selection=None): + l = cell.length + r = cell.radius + e1, e2 = map(numpy.array, cell.ends) + ori = (e1 + e2) / 2.0 # origin in the cell's coordinates + z = numpy.array([0, 0, 1]) + axis = (e2 - e1) / l # x axis in the cell's coordinates + rotangle = numpy.arctan2(axis[1], axis[0]) # angle to rotate around the z axis + + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + + # move to the cell's coordinates + glTranslatef(ori[0], ori[1], ori[2]) + glRotatef(rotangle * 180.0 / numpy.pi, z[0], z[1], z[2]) + + # draw the cell in color + color = getattr(cell, "color", self.cellcolor) + glColor3fv(color) + self.render_capsule(l, r) + + # draw the outlines antialiased in black + glColor3f(0.0, 0.0, 0.0) + glLineWidth(2.0) + if cell.id == selection: + glColor3f(1.0, 0.3, 0.3) + glLineWidth(3.0) + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glEnable(GL_LINE_SMOOTH) + # draw wireframe for back facing polygons and cull front-facing ones + glPolygonMode(GL_BACK, GL_LINE) + glEnable(GL_CULL_FACE) + glCullFace(GL_FRONT) + glDepthFunc(GL_LEQUAL) + self.render_capsule(l, r) + glDepthFunc(GL_LESS) + glDisable(GL_CULL_FACE) + glPolygonMode(GL_FRONT, GL_FILL) + glDisable(GL_LINE_SMOOTH) + glDisable(GL_BLEND) + + glPopMatrix() + + def render_cell_name(self, cell): + l = cell.length + r = cell.radius + e1, e2 = map(numpy.array, cell.ends) + ori = (e1 + e2) / 2.0 # origin in the cell's coordinates + z = numpy.array([0, 0, 1]) + axis = (e2 - e1) / l # x axis in the cell's coordinates + rotangle = numpy.arctan2(axis[1], axis[0]) # angle to rotate around the z axis + + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + + # move to the cell's coordinates + glTranslatef(ori[0], ori[1], ori[2]) + glRotatef(rotangle * 180.0 / numpy.pi, z[0], z[1], z[2]) + + # draw the cell in color + glPushName(cell.id) + self.render_capsule(l, r) + glPopName() + + glPopMatrix() + + def render_gl(self, selection=None): + cells = self.sim.cellStates.values() + for cell in cells: + self.render_cell(cell, selection) + + def renderNames_gl(self, selection=None): + cells = self.sim.cellStates.values() + for cell in cells: + self.render_cell_name(cell) + + +class GL2DBacteriumRenderer: + + def __init__(self, sim, properties=None, scales=None): + self.ncells_list = 0 + self.ncells_names_list = 0 + self.dlist = None + self.dlist_names = None + self.sim = sim + self.properties = properties + self.scales = scales + self.cellcolor = [0.4, 0.6, 0.5] + self.circ_pts = [ + (math.cos(math.radians(th)), math.sin(math.radians(th))) + for th in range(-80, 90, 20) + ] + self.semi = self.build_semicircle() + + def build_semicircle(self): + index = glGenLists(1) + glNewList(index, GL_COMPILE) + glVertex3f(0.5, -1, 0) + for x, y in self.circ_pts: + glVertex3f(0.5 + x, y, 0.0) + glVertex3f(0.5, 1, 0) + glEndList() + return index + + def draw_cell(self, p, d, l, r): + glMatrixMode(GL_MODELVIEW) + glPushMatrix() + ang = math.atan2(d[1], d[0]) * 360.0 / (2.0 * 3.141593) + glTranslatef(p[0], p[1], 0.0) + glRotatef(ang, 0.0, 0.0, 1.0) + glBegin(GL_POLYGON) + glVertex3f(-l / 2.0, -r, 0) + glVertex3f(l / 2.0, -r, 0) + for x, y in self.circ_pts: + glVertex3f(l / 2.0 + x * r, y * r, 0.0) + glVertex3f(l / 2.0, r, 0) + glVertex3f(-l / 2.0, r, 0) + for x, y in self.circ_pts: + glVertex3f(-l / 2.0 - x * r, -y * r, 0.0) + glEnd() + glPopMatrix() + + def build_list(self, cells): + if self.dlist: + glDeleteLists(self.dlist, 1) + index = glGenLists(1) + glNewList(index, GL_COMPILE) + for cell in cells: + self.render_cell(cell) + glEndList() + self.dlist = index + + def build_list_names(self, cells): + if self.dlist_names: + glDeleteLists(self.dlist_names, 1) + index = glGenLists(1) + glNewList(index, GL_COMPILE) + for cell in cells: + self.render_cell_name(cell) + glEndList() + self.dlist_names = index + + def render_cell(self, cell, selection=None): + p, d, l, r = (cell.pos, cell.dir, cell.length, cell.radius) + glMatrixMode(GL_MODELVIEW) + glEnable(GL_DEPTH_TEST) + glPushMatrix() + glPolygonMode(GL_FRONT, GL_FILL) + glColor3f(0.0, 0.0, 0.0) + if selection == cell.id: + glColor3f(0.2, 0.1, 0.8) + glTranslatef(0.0, 0.0, 0.025) + self.draw_cell(p, d, l, r) + color = getattr(cell, "color", [0.4, 0.4, 0.4]) + glColor3fv(color) + glTranslatef(0.0, 0.0, 0.025) + self.draw_cell(p, d, l, r - 0.15) + glPopMatrix() + glDisable(GL_DEPTH_TEST) + + def render_cell_name(self, cell, selection=None): + p, d, l, r = (cell.pos, cell.dir, cell.length, cell.radius) + glPushName(cell.id) + glEnable(GL_DEPTH_TEST) + self.draw_cell(p, d, l, r) + glPopName() + glDisable(GL_DEPTH_TEST) + + def render_gl(self, selection=None): + glDisable(GL_LIGHTING) + glEnable(GL_POLYGON_SMOOTH) + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + + cells = self.sim.cellStates.values() + # if len(cells)!=self.ncells_list: + self.build_list(cells) + # self.ncells_list = len(cells) + glCallList(self.dlist) + # for cell in cells: self.render_cell(cell, selection) + + def renderNames_gl(self, selection=None): + cells = self.sim.cellStates.values() + if len(cells) != self.ncells_names_list: + self.build_list_names(cells) + self.ncells_names_list = len(cells) + glCallList(self.dlist_names) + # for cell in cells: self.render_cell_name(cell, selection) diff --git a/CellModeller/Regulation/SBMLImport.py b/cellmodeller/regulation/SBMLImport.py similarity index 62% rename from CellModeller/Regulation/SBMLImport.py rename to cellmodeller/regulation/SBMLImport.py index c0a10a3f..4f5ed85a 100644 --- a/CellModeller/Regulation/SBMLImport.py +++ b/cellmodeller/regulation/SBMLImport.py @@ -1,22 +1,21 @@ -from libsbml import * +from libsbml import * import math import new import urllib.request, urllib.error, urllib.parse - # indicates whether we should perform SBML document consistency check. -# suppressed by default because the MSR SBML library does not produce valid SBML. +# suppressed by default because the MSR SBML library does not produce valid SBML. suppressConsistencyCheck = True # get the array index of given species ID: def getSpecIndex(specIdToArrayIndexDict, specId): if specId not in specIdToArrayIndexDict: - specIdToArrayIndexDict[specId] = len(list(specIdToArrayIndexDict.keys())) - return specIdToArrayIndexDict[specId] + specIdToArrayIndexDict[specId] = len(list(specIdToArrayIndexDict.keys())) + return specIdToArrayIndexDict[specId] -# get python variable name for the ODE of given species ID: +# get python variable name for the ODE of given species ID: def specOdeNameFromId(specId): return "d_" + specId @@ -26,20 +25,26 @@ def specOdeNameFromId(specId): def checkSBMLConsistency(document): if suppressConsistencyCheck: return - + numFailures = document.checkInternalConsistency() if numFailures > 0: failureMsg = "" for failureNum in range(numFailures): - print("Failure " + str(failureNum) + ": " + document.getError(failureNum).getShortMessage() + "\n") + print( + "Failure " + + str(failureNum) + + ": " + + document.getError(failureNum).getShortMessage() + + "\n" + ) raise Exception(failureMsg) - - + + # load SBML model from given file. -def SBMLModelFromSBMLFile(sbmlFile): - reader = SBMLReader() +def SBMLModelFromSBMLFile(sbmlFile): + reader = SBMLReader() document = reader.readSBML(sbmlFile) - if document.getNumErrors()>0: + if document.getNumErrors() > 0: print("Errors in reading SBML file...") checkSBMLConsistency(document) model = document.getModel() @@ -50,20 +55,20 @@ def SBMLModelFromSBMLFile(sbmlFile): # load SBML model form given string. def SBMLModelFromSBMLStr(sbmlStr): - - # would be nice if SBMLReader.readSBMLFromStr worked - but it does not, + + # would be nice if SBMLReader.readSBMLFromStr worked - but it does not, # there seems to be some SWIG fuck-up. so we are forced to first write - # to temporary file: - file = open("tmp.sbml", 'w') - file.write(sbmlStr) - file.close() - + # to temporary file: + file = open("tmp.sbml", "w") + file.write(sbmlStr) + file.close() + # then read from file: - reader = SBMLReader() + reader = SBMLReader() document = reader.readSBML("tmp.sbml") - checkSBMLConsistency(document) + checkSBMLConsistency(document) model = document.getModel() - + return model @@ -76,82 +81,96 @@ def splitBinary(astNode, operand, kineticLaw, model): # constructs a python string representing the math expresson in an AST node. -# currently only covers basic cases, and not all the ASTNode types; an +# currently only covers basic cases, and not all the ASTNode types; an # exception is thrown if an unsupported type is encountered. # more types can easily be added. def pythonMathFromASTNode(astNode, kineticLaw, model): - if (astNode.getType() == AST_PLUS): + if astNode.getType() == AST_PLUS: return splitBinary(astNode, "+", kineticLaw, model) - - elif (astNode.getType() == AST_MINUS): + + elif astNode.getType() == AST_MINUS: return splitBinary(astNode, "-", kineticLaw, model) - elif (astNode.getType() == AST_DIVIDE): + elif astNode.getType() == AST_DIVIDE: return splitBinary(astNode, "/", kineticLaw, model) - - elif (astNode.getType() == AST_TIMES): - return splitBinary(astNode, "*", kineticLaw, model) - - elif (astNode.getType() == AST_FUNCTION_POWER): - return splitBinary(astNode, "**", kineticLaw, model) + + elif astNode.getType() == AST_TIMES: + return splitBinary(astNode, "*", kineticLaw, model) + + elif astNode.getType() == AST_FUNCTION_POWER: + return splitBinary(astNode, "**", kineticLaw, model) elif astNode.isNumber(): if astNode.isReal(): return str(astNode.getReal()) elif astNode.isInteger(): - return str(astNode.getInteger()) - + return str(astNode.getInteger()) + elif astNode.isName(): # check if this name is a defined parameter. can't see any way of distinguishing between # parameters and species identifiers, other than checking for definition this way? nodeName = astNode.getName() parameterLocal = kineticLaw.getParameter(nodeName) - parameterGlobal = model.getParameter(nodeName) + parameterGlobal = model.getParameter(nodeName) if parameterLocal != None: - val = str(parameterLocal.getValue()) + val = str(parameterLocal.getValue()) return val elif parameterGlobal != None: val = str(parameterGlobal.getValue()) return val - - # if not parameter, then check if this is a species id: - elif model.getSpecies(nodeName) != None: + + # if not parameter, then check if this is a species id: + elif model.getSpecies(nodeName) != None: return nodeName - + # something weird -- return 1 and issue warning: else: - print("WARNING: unknown identifier " + nodeName + ", defaulting to 1. Globals: " + str(model.getNumParameters()) + ". Locals: " + str(kineticLaw.getNumParameters())) + print( + "WARNING: unknown identifier " + + nodeName + + ", defaulting to 1. Globals: " + + str(model.getNumParameters()) + + ". Locals: " + + str(kineticLaw.getNumParameters()) + ) return "1" - - else: - raise Exception("Un-supported AST node type: " + str(astNode.isName()) + ", node: " + str(astNode.getType())) - + + else: + raise Exception( + "Un-supported AST node type: " + + str(astNode.isName()) + + ", node: " + + str(astNode.getType()) + ) + # constructs a python program string from given SBML model. def pythonStrFromSBMLModel(model): - # initialise dictionaries and python code string: - specIdToArrayIndexDict = {} + # initialise dictionaries and python code string: + specIdToArrayIndexDict = {} specIdToExpDict = {} - + # define indentation for python code: indent = " " - indent2 = indent + indent - + indent2 = indent + indent + # iterate over reactions: for reactionNum in range(model.getNumReactions()): - + reaction = model.getReaction(reactionNum) - - # compute rate expression. + + # compute rate expression. # UPDATE: first tried to use the formulaToString libSBML function, # which should have worked in most cases. but it doesn't substitute - # in defined parameters. hence we use our own ASTNode parsing function. - #mathAstNode = reaction.getKineticLaw().getMath() - #pythonRateExp = "(" + formulaToString(mathAstNode) + ")" - #pythonRateExp = reaction.getKineticLaw().getFormula() - pythonRateExp = pythonMathFromASTNode(reaction.getKineticLaw().getMath(), reaction.getKineticLaw(), model) - + # in defined parameters. hence we use our own ASTNode parsing function. + # mathAstNode = reaction.getKineticLaw().getMath() + # pythonRateExp = "(" + formulaToString(mathAstNode) + ")" + # pythonRateExp = reaction.getKineticLaw().getFormula() + pythonRateExp = pythonMathFromASTNode( + reaction.getKineticLaw().getMath(), reaction.getKineticLaw(), model + ) + """ # iterate over reactants to create mass-action rate expression: # UPDATE: don't assume mass-action, just read the rate expression literally. @@ -171,95 +190,101 @@ def pythonStrFromSBMLModel(model): else: pythonRateExp += "*" + reactantSpecId + "^" + str(stoich) """ - - # iterate over reactants and update ODE for each: + + # iterate over reactants and update ODE for each: for reactantNum in range(reaction.getNumReactants()): reactantSpecRef = reaction.getReactant(reactantNum) stoich = reactantSpecRef.getStoichiometry() - + # for some reason stoichiometry is sometimes NaN: if math.isnan(stoich): stoich = 1 - - reactantSpecId = reactantSpecRef.getSpecies() - - # add key to hash table if it doesn't exist: - if reactantSpecId not in specIdToExpDict: + + reactantSpecId = reactantSpecRef.getSpecies() + + # add key to hash table if it doesn't exist: + if reactantSpecId not in specIdToExpDict: specIdToExpDict[reactantSpecId] = "" - + if stoich == 1: specIdToExpDict[reactantSpecId] += " - " + pythonRateExp else: - specIdToExpDict[reactantSpecId] += " - " + str(stoich) + "*" + pythonRateExp - - # do the same for products: + specIdToExpDict[reactantSpecId] += ( + " - " + str(stoich) + "*" + pythonRateExp + ) + + # do the same for products: for productNum in range(reaction.getNumProducts()): productSpecRef = reaction.getProduct(productNum) stoich = productSpecRef.getStoichiometry() - + # for some reason stoichiometry is sometimes NaN: if math.isnan(stoich): stoich = 1 - - productSpecId = productSpecRef.getSpecies() - - # add key to hash table if it doesn't exist: - if productSpecId not in specIdToExpDict: - specIdToExpDict[productSpecId] = "" - + + productSpecId = productSpecRef.getSpecies() + + # add key to hash table if it doesn't exist: + if productSpecId not in specIdToExpDict: + specIdToExpDict[productSpecId] = "" + if stoich == 1: - specIdToExpDict[productSpecId] += " + " + pythonRateExp + specIdToExpDict[productSpecId] += " + " + pythonRateExp else: - specIdToExpDict[productSpecId] += " + " + str(stoich) + "*" + pythonRateExp - + specIdToExpDict[productSpecId] += ( + " + " + str(stoich) + "*" + pythonRateExp + ) + # create python variable definitions, python updated rate definitions, # and a list of species IDs in the relevant order. pythonVarDefs = "" - pythonRateUpdateExps = "" - specIdLst = [0]*len(specIdToExpDict) + pythonRateUpdateExps = "" + specIdLst = [0] * len(specIdToExpDict) for specId in list(specIdToExpDict.keys()): # get index into species array: index = getSpecIndex(specIdToArrayIndexDict, specId) - + # create python variable definition: - pythonVarDefs += indent + indent + specId + " = cell.species[" + str(index) + "]\n" + pythonVarDefs += ( + indent + indent + specId + " = cell.species[" + str(index) + "]\n" + ) # create python rate update expression: exp = specIdToExpDict[specId] varname = specOdeNameFromId(specId) pythonRateUpdateExps += indent2 + varname + " = " + exp + "\n" - + # insert spec id into correct position in list: - specIdLst[index] = specId #.insert(index, specId) - + specIdLst[index] = specId # .insert(index, specId) + # create a python list with updated rate expressions, - # and python lists with with species IDs and names in the relevant order. - pythonRateUpdateLst = "" + # and python lists with with species IDs and names in the relevant order. + pythonRateUpdateLst = "" pythonSpecIDLst = "" pythonSpecNameLst = "" for specId in specIdLst: - - # get species name from model: + + # get species name from model: specName = model.getSpecies(specId).getName() if specName == None: - specName = "Undefined" - + specName = "Undefined" + if pythonRateUpdateLst == "": pythonRateUpdateLst += "[" + specOdeNameFromId(specId) - pythonSpecIDLst += "[\"" + specId + "\"" - pythonSpecNameLst += "[\"" + specName + "\"" + pythonSpecIDLst += '["' + specId + '"' + pythonSpecNameLst += '["' + specName + '"' else: pythonRateUpdateLst += ", " + specOdeNameFromId(specId) - pythonSpecIDLst += ", \"" + specId + "\"" - pythonSpecNameLst += ", \"" + specName + "\"" - + pythonSpecIDLst += ', "' + specId + '"' + pythonSpecNameLst += ', "' + specName + '"' + pythonRateUpdateLst += "]" pythonSpecIDLst += "]" - pythonSpecNameLst += "]" - + pythonSpecNameLst += "]" + # build initial population list: - initPopLst = [0]*model.getNumSpecies() - for specId in specIdLst: + initPopLst = [0] * model.getNumSpecies() + for specId in specIdLst: sbmlSpec = model.getSpecies(specId) initAmount = sbmlSpec.getInitialAmount() initConc = sbmlSpec.getInitialConcentration() @@ -268,10 +293,10 @@ def pythonStrFromSBMLModel(model): init = initAmount elif not math.isnan(initConc) and initConc > 0: init = initConc - - index = getSpecIndex(specIdToArrayIndexDict, specId) + + index = getSpecIndex(specIdToArrayIndexDict, specId) initPopLst[index] = init - + # build initial population python string: pythonInitPopLst = "" for init in initPopLst: @@ -279,44 +304,47 @@ def pythonStrFromSBMLModel(model): pythonInitPopLst += "[" + str(init) else: pythonInitPopLst += "," + str(init) - - pythonInitPopLst += "]" - - # compose the complete python program string. + + pythonInitPopLst += "]" + + # compose the complete python program string. pythonStr = "" pythonStr += "def getRates(cells):\n" pythonStr += indent + "df = []\n" - pythonStr += indent + "for cell in cells:\n" + pythonStr += indent + "for cell in cells:\n" pythonStr += pythonVarDefs + "\n" pythonStr += pythonRateUpdateExps + "\n" - pythonStr += indent2 + "df.append(" + pythonRateUpdateLst + ")\n" - pythonStr += indent + "return df" + pythonStr += indent2 + "df.append(" + pythonRateUpdateLst + ")\n" + pythonStr += indent + "return df" pythonStr += "\n\n" pythonStr += "specIdLst = " + pythonSpecIDLst + "\n\n" pythonStr += "specNameLst = " + pythonSpecNameLst + "\n\n" pythonStr += "specInitPopLst = " + pythonInitPopLst + "\n\n" - + return pythonStr - -# create python module from code dynamically. + +# create python module from code dynamically. def pythonModuleFromPythonStr(pythonStr): module = new.module("sbmlPythonEncoding") exec(pythonStr, module.__dict__) - return module + return module -# get text from given channel. +# get text from given channel. def TextFromChannel(channelName): - f = urllib.request.urlopen('http://async-message-passer.appspot.com/?content_only=1&channel_name=' + channelName) - return f.read() + f = urllib.request.urlopen( + "http://async-message-passer.appspot.com/?content_only=1&channel_name=" + + channelName + ) + return f.read() def pythonModuleFromFile(fileName): - reader = SBMLReader() + reader = SBMLReader() document = reader.readSBML(fileName) - checkSBMLConsistency(document) - model = document.getModel() + checkSBMLConsistency(document) + model = document.getModel() pstr = pythonStrFromSBMLModel(model) print("Got python str: " + pstr) pythonModule = pythonModuleFromPythonStr(pstr) @@ -327,29 +355,28 @@ def pythonModuleFromFile(fileName): def pythonModuleFromChannel(channelName): sbmlStr = TextFromChannel(channelName) - #print "Got channel text:\n" + sbmlStr - + # print "Got channel text:\n" + sbmlStr + # 1) an apparent segmentation issue in libsbml means that we cannot # invoke SBMLModelFromSBMLStr() defined above. great. so we inline it below: - # 2) would be nice if SBMLReader.readSBMLFromStr worked - but it does not, + # 2) would be nice if SBMLReader.readSBMLFromStr worked - but it does not, # there seems to be some SWIG fuck-up. so we are forced to first write # to temporary file: - file = open("tmp.sbml", 'w') - file.write(sbmlStr) - file.close() - reader = SBMLReader() + file = open("tmp.sbml", "w") + file.write(sbmlStr) + file.close() + reader = SBMLReader() document = reader.readSBML("tmp.sbml") - checkSBMLConsistency(document) - model = document.getModel() - - + checkSBMLConsistency(document) + model = document.getModel() + pythonStr = pythonStrFromSBMLModel(model) print("Got python str: " + pythonStr) pythonModule = pythonModuleFromPythonStr(pythonStr) return pythonModule -# example usage: -#sbmlFile = "/Users/michaeldavidpedersen/tmp/testsbml.sbml" -#print sbmlToPython(sbmlFile) -#module = pythonModuleFromChannel("DEFAULT_CHANNEL") +# example usage: +# sbmlFile = "/Users/michaeldavidpedersen/tmp/testsbml.sbml" +# print sbmlToPython(sbmlFile) +# module = pythonModuleFromChannel("DEFAULT_CHANNEL") diff --git a/CellModeller/gui/__init__.py b/cellmodeller/regulation/__init__.py similarity index 100% rename from CellModeller/gui/__init__.py rename to cellmodeller/regulation/__init__.py diff --git a/CellModeller/Regulation/ModuleRegulator.py b/cellmodeller/regulation/module_regulator.py similarity index 95% rename from CellModeller/Regulation/ModuleRegulator.py rename to cellmodeller/regulation/module_regulator.py index fe483924..bb976454 100644 --- a/CellModeller/Regulation/ModuleRegulator.py +++ b/cellmodeller/regulation/module_regulator.py @@ -2,6 +2,7 @@ import os.path import sys + class ModuleRegulator: def __init__(self, sim, biophys=None, signalling=None): self.modName = sim.moduleName @@ -12,7 +13,7 @@ def __init__(self, sim, biophys=None, signalling=None): self.signal = signalling # Simulator is responsible for loading the model as a python module # This class uses the module imported by Simulator - self.module = sim.module + self.module = sim.module def addCell(self, cellState, **kwargs): self.module.init(cellState, **kwargs) @@ -31,7 +32,7 @@ def sigRateCL(self): def specRateCL(self): return self.module.specRateCL() - + def signalRates(self, cstate, speciesLevels, signalLevels): return self.module.signalRates(cstate, speciesLevels, signalLevels) @@ -42,7 +43,7 @@ def initSpeciesLevels(self, levels): csv = list(self.cellStates.values()) nCells = len(csv) for i in range(nCells): - levels[i,:] = csv[i].species + levels[i, :] = csv[i].species def step(self, dt=0): try: @@ -56,5 +57,3 @@ def divide(self, pState, d1State, d2State): divfunc = getattr(self.module, "divide", None) if callable(divfunc): divfunc(pState, d1State, d2State) - - diff --git a/CellModeller/simulator.py b/cellmodeller/simulator.py similarity index 65% rename from CellModeller/simulator.py rename to cellmodeller/simulator.py index 57ca5ac4..41b13ad7 100644 --- a/CellModeller/simulator.py +++ b/cellmodeller/simulator.py @@ -10,36 +10,39 @@ import configparser import importlib + class Simulator: """ -This class is in charge of running the simulation, creating the various models -and stepping them forward in time. It is the control interface for the gui -or script that is running the simulation. - -Stores a map from cell_id to CellState, which stores the current simulation -state of each cell. - -Constructed on a user-defined python file. This file implements a -function setup(Simulator, Gui) that constructs the requiredx modules -(Regulator, Signalling, Integrator), and calls Simulator.init(). It -can also create Renderers and add them by calling -Simulator.addRenderer(renderer) so that the simulation can be -visualised. -""" + This class is in charge of running the simulation, creating the various models + and stepping them forward in time. It is the control interface for the gui + or script that is running the simulation. + + Stores a map from cell_id to CellState, which stores the current simulation + state of each cell. + + Constructed on a user-defined python file. This file implements a + function setup(Simulator, Gui) that constructs the requiredx modules + (Regulator, Signalling, Integrator), and calls Simulator.init(). It + can also create Renderers and add them by calling + Simulator.addRenderer(renderer) so that the simulation can be + visualised. + """ ## Construct an empty simulator object. This object will not be able to # do anything yet unti we use 'init' method to specify the models for # physical interaction, genetic circuit, diffusion and integrator. - def __init__(self, \ - moduleName, \ - dt, \ - pickleSteps=50, \ - outputDirName=None, \ - moduleStr=None, \ - saveOutput=False, \ - clPlatformNum=0, \ - clDeviceNum=0, \ - is_gui=False): + def __init__( + self, + moduleName, + dt, + pickleSteps=50, + outputDirName=None, + moduleStr=None, + saveOutput=False, + clPlatformNum=0, + clDeviceNum=0, + is_gui=False, + ): # Is this simulator running in a gui? self.is_gui = is_gui @@ -64,50 +67,51 @@ def __init__(self, \ self.dt = dt if "CMPATH" in os.environ: - self.cfg_file = os.path.join(os.environ["CMPATH"], 'CMconfig.cfg') + self.cfg_file = os.path.join(os.environ["CMPATH"], "CMconfig.cfg") else: - self.cfg_file = 'CellModeller/CMconfig.cfg' + self.cfg_file = "cellmodeller/CMconfig.cfg" if not self.init_cl(platnum=clPlatformNum, devnum=clDeviceNum): print("Couldn't initialise OpenCL context") return # Two ways to specify a module (model): - self.moduleName = moduleName # Import via standard python - self.moduleStr = moduleStr # Import stored python code string (from a pickle usually) + self.moduleName = moduleName # Import via standard python + self.moduleStr = ( + moduleStr # Import stored python code string (from a pickle usually) + ) if self.moduleStr: - print("Importing model %s from string"%(self.moduleName)) + print("Importing model %s from string" % (self.moduleName)) module_spec = importlib.util.spec_from_loader(moduleName, loader=None) self.module = importlib.util.module_from_spec(module_spec) exec(moduleStr, self.module.__dict__) else: # In case the moduleName is a path to a python file: # Get path and file name - (path,name) = os.path.split(self.moduleName) + path, name = os.path.split(self.moduleName) # Append path to PYTHONPATH, if no path do nothing if path: if path not in sys.path: sys.path.append(path) # Remove .py extension if present - self.moduleName = str(name).split('.')[0] - print("Importing model %s"%(self.moduleName)) + self.moduleName = str(name).split(".")[0] + print("Importing model %s" % (self.moduleName)) if self.moduleName in sys.modules: self.module = sys.modules[self.moduleName] importlib.reload(self.module) else: self.module = __import__(self.moduleName, globals(), locals(), [], 0) - # TJR: What is this invar thing? I have never seen this used... - #setup the simulation here: - #if invar: + # setup the simulation here: + # if invar: # self.module.setup(self, invar) - #else: + # else: # Set up the data output directory - self.dataOutputInitialised=False + self.dataOutputInitialised = False self.outputDirName = outputDirName self.setSaveOutput(saveOutput) - + # Call the user-defined setup function on ourself self.module.setup(self) @@ -118,32 +122,38 @@ def setSaveOutput(self, save): def init_data_output(self): import time + startTime = time.localtime() - outputFileRoot = self.outputDirName if self.outputDirName else self.moduleName + '-' + time.strftime('%y-%m-%d-%H-%M', startTime) - self.outputDirPath = os.path.join('data', outputFileRoot) - if 'CMPATH' in os.environ: + outputFileRoot = ( + self.outputDirName + if self.outputDirName + else self.moduleName + "-" + time.strftime("%y-%m-%d-%H-%M", startTime) + ) + self.outputDirPath = os.path.join("data", outputFileRoot) + if "CMPATH" in os.environ: self.outputDirPath = os.path.join(os.environ["CMPATH"], self.outputDirPath) - # Add a number to end of dir name if it already exists + # Add a number to end of dir name if it already exists label = 2 while os.path.exists(self.outputDirPath): - if label>2: - self.outputDirPath = self.outputDirPath[:-2]+"_"+str(label) + if label > 2: + self.outputDirPath = self.outputDirPath[:-2] + "_" + str(label) else: - self.outputDirPath = self.outputDirPath+"_"+str(label) - label+=1 + self.outputDirPath = self.outputDirPath + "_" + str(label) + label += 1 os.mkdir(self.outputDirPath) - # write a copy of the model into the dir (for reference), + # write a copy of the model into the dir (for reference), # this goes in the pickle too (and gets loaded when a pickle is loaded) if self.moduleStr: self.moduleOutput = self.moduleStr else: self.moduleOutput = inspect.getsource(self.module) - open(os.path.join(self.outputDirPath, self.moduleName), 'w').write(self.moduleOutput) - - self.dataOutputInitialised=True + open(os.path.join(self.outputDirPath, self.moduleName), "w").write( + self.moduleOutput + ) + self.dataOutputInitialised = True ## Get an id for the next cell to be created def next_id(self): @@ -157,7 +167,6 @@ def next_idx(self): self._next_idx += 1 return idx - # Currently, the user-defined regulation module creates the # biophysics, regulation, and signalling objects in a function # setup(). @@ -190,69 +199,72 @@ def init(self, phys, reg, sig, integ): self.integ.setSignalling(sig) self.reg.setSignalling(sig) - ## Set up the OpenCL context, the configuration is set up the first time, and is saved in the config file def init_cl(self, platnum, devnum): # Check that specified platform exists platforms = cl.get_platforms() - if len(platforms)<=platnum: + if len(platforms) <= platnum: print("Specified OpenCL platform number (%d) does not exist.") print("Options are:") for p in range(len(platforms)): - print("%d: %s"%(p, str(platforms[p]))) + print("%d: %s" % (p, str(platforms[p]))) return False else: platform = platforms[platnum] # Check that specified device exists on that platform devices = platforms[platnum].get_devices() - if len(devices)<=devnum: - print("Specified OpenCL device number (%d) does not exist on platform %s."%(devnum,platform)) + if len(devices) <= devnum: + print( + "Specified OpenCL device number (%d) does not exist on platform %s." + % (devnum, platform) + ) print("Options are:") for d in range(len(devices)): - print("%d: %s"%(d, str(devices[d]))) + print("%d: %s" % (d, str(devices[d]))) return False else: device = devices[devnum] # Create a context and queue - self.CLContext = cl.Context(properties=[(cl.context_properties.PLATFORM, platform)], - devices=[device]) + self.CLContext = cl.Context( + properties=[(cl.context_properties.PLATFORM, platform)], devices=[device] + ) self.CLQueue = cl.CommandQueue(self.CLContext) print("Set up OpenCL context:") - print(" Platform: %s"%(str(platform.name))) - print(" Device: %s"%(str(device.name))) + print(" Platform: %s" % (str(platform.name))) + print(" Device: %s" % (str(device.name))) return True - - ## Get the OpenCL context and queue for running kernels + + ## Get the OpenCL context and queue for running kernels def getOpenCL(self): return (self.CLContext, self.CLQueue) ## set cell states from a given dict def setCellStates(self, cellStates): - #Set cell states, e.g. from pickle file - #this sets them on the card too + # Set cell states, e.g. from pickle file + # this sets them on the card too self.cellStates = {} self.cellStates = cellStates idx_map = {} id_map = {} idmax = 0 - for id,state in cellStates.items(): + for id, state in cellStates.items(): idx_map[state.id] = state.idx id_map[state.idx] = state.id - if id>idmax: - idmax=id + if id > idmax: + idmax = id self.idToIdx = idx_map self.idxToId = id_map - self._next_id = idmax+1 + self._next_id = idmax + 1 self._next_idx = len(cellStates) self.reg.cellStates = cellStates self.phys.load_from_cellstates(cellStates) - + ## Add a graphics renderer - this should not be here --> GUI def addRenderer(self, renderer): self.renderers.append(renderer) - + ## Reset the simulation back to initial conditions def reset(self): # Delete existing models @@ -265,8 +277,8 @@ def reset(self): if self.reg: del self.reg - if not self.moduleStr: - #This will take up any changes made in the model file + if not self.moduleStr: + # This will take up any changes made in the model file importlib.reload(self.module) else: # TJR: Module loaded from pickle, cannot reset? @@ -277,7 +289,6 @@ def reset(self): # Recreate models via module setup self.module.setup(self) - # Divide a cell to two daughter cells def divide(self, pState): pState.divideFlag = False @@ -289,13 +300,13 @@ def divide(self, pState): d1State.id = d1id d2State.id = d2id - #reset cell ages + # reset cell ages d1State.cellAge = 0 d2State.cellAge = 0 - #inherit effGrowth + # inherit effGrowth d1State.effGrowth = pState.effGrowth d2State.effGrowth = pState.effGrowth - + self.lineage[d1id] = pid self.lineage[d2id] = pid @@ -314,7 +325,7 @@ def divide(self, pState): del self.idToIdx[pid] # Divide the cell in each model - asymm = getattr(pState, 'asymm', [1, 1]) + asymm = getattr(pState, "asymm", [1, 1]) self.phys.divide(pState, d1State, d2State, f1=asymm[0], f2=asymm[1]) if self.integ: self.integ.divide(pState, d1State, d2State) @@ -338,7 +349,7 @@ def addCell(self, cellType=0, cellAdh=0, length=3.5, **kwargs): self.sig.addCell(cs) self.phys.addCell(cs, **kwargs) - #--- + # --- # Some functions to modify existing cells (e.g. from GUI) # Eventually prob better to have a generic editCell() that deals with this stuff # @@ -351,78 +362,79 @@ def moveCell(self, cid, delta_pos): def step(self): self.reg.step(self.dt) states = dict(self.cellStates) - for (cid,state) in list(states.items()): + for cid, state in list(states.items()): state.time = self.stepNum * self.dt if state.divideFlag: - self.divide(state) #neighbours no longer current + self.divide(state) # neighbours no longer current self.phys.set_cells() - while not self.phys.step(self.dt): #neighbours are current here + while not self.phys.step(self.dt): # neighbours are current here pass if self.sig: self.sig.step(self.dt) if self.integ: self.integ.step(self.dt) - if self.saveOutput and self.stepNum%self.pickleSteps==0: + if self.saveOutput and self.stepNum % self.pickleSteps == 0: self.writePickle() self.stepNum += 1 return True - ## Import cells to the simulator from csv file. The file contains a list of 7-coordinates {pos,dir,len} (comma delimited) of each cell - also, there should be no cells around - ie run this from an empty model instead of addcell def importCells_file(self, filename): - f=open(filename, 'rU') - list=csv.reader(f,delimiter=',') + f = open(filename, "rU") + list = csv.reader(f, delimiter=",") for row in list: - cpos = [float(row[0]),float(row[1]),float(row[2])] - cdir = [float(row[3]),float(row[4]),float(row[5])] - cell_len = float(row[6]) #radius should be removed from this in the analysis - ndir = cdir/numpy.linalg.norm(cdir) #normalize cell dir just in case - #this should probably also check for overlaps + cpos = [float(row[0]), float(row[1]), float(row[2])] + cdir = [float(row[3]), float(row[4]), float(row[5])] + cell_len = float( + row[6] + ) # radius should be removed from this in the analysis + ndir = cdir / numpy.linalg.norm(cdir) # normalize cell dir just in case + # this should probably also check for overlaps self.addCell(pos=tuple(cpos), dir=tuple(ndir), length=cell_len) ## Write current simulation state to an output file def writePickle(self, csv=False): - filename = os.path.join(self.outputDirPath, 'step-%05i.pickle' % self.stepNum) - outfile = open(filename, 'wb') + filename = os.path.join(self.outputDirPath, "step-%05i.pickle" % self.stepNum) + outfile = open(filename, "wb") data = {} - data['cellStates'] = self.cellStates - data['stepNum'] = self.stepNum - data['lineage'] = self.lineage - data['moduleStr'] = self.moduleOutput - data['moduleName'] = self.moduleName + data["cellStates"] = self.cellStates + data["stepNum"] = self.stepNum + data["lineage"] = self.lineage + data["moduleStr"] = self.moduleOutput + data["moduleName"] = self.moduleName if self.integ: - # print("Writing new pickle format") - data['specData'] = self.integ.levels + # print("Writing new pickle format") + data["specData"] = self.integ.levels if self.sig: - data['sigGridOrig'] = self.sig.gridOrig - data['sigGridDim'] = self.sig.gridDim - data['sigGridSize'] = self.sig.gridSize + data["sigGridOrig"] = self.sig.gridOrig + data["sigGridDim"] = self.sig.gridDim + data["sigGridSize"] = self.sig.gridSize if self.sig and self.integ: - data['sigGrid'] = self.integ.signalLevel - data['sigData'] = self.integ.cellSigLevels - data['sigGrid'] = self.integ.signalLevel + data["sigGrid"] = self.integ.signalLevel + data["sigData"] = self.integ.cellSigLevels + data["sigGrid"] = self.integ.signalLevel pickle.dump(data, outfile, protocol=-1) - #output csv file with cell pos,dir,len - sig? + # output csv file with cell pos,dir,len - sig? # Populate simulation from saved data pickle def loadGeometryFromPickle(self, data): - self.setCellStates(data['cellStates']) - self.lineage = data['lineage'] + self.setCellStates(data["cellStates"]) + self.lineage = data["lineage"] idx_map = {} id_map = {} idmax = 0 - for id,state in data['cellStates'].items(): + for id, state in data["cellStates"].items(): idx_map[state.id] = state.idx id_map[state.idx] = state.id - if id>idmax: - idmax=id + if id > idmax: + idmax = id self.idToIdx = idx_map self.idxToId = id_map - self._next_id = idmax+1 - self._next_idx = len(data['cellStates']) + self._next_id = idmax + 1 + self._next_idx = len(data["cellStates"]) if self.integ: self.integ.setCellStates(self.cellStates) if self.sig: @@ -430,25 +442,23 @@ def loadGeometryFromPickle(self, data): # Populate simulation from saved data pickle def loadFromPickle(self, data): - self.setCellStates(data['cellStates']) - self.lineage = data['lineage'] - self.stepNum = data['stepNum'] + self.setCellStates(data["cellStates"]) + self.lineage = data["lineage"] + self.stepNum = data["stepNum"] idx_map = {} id_map = {} idmax = 0 - for id,state in data['cellStates'].items(): + for id, state in data["cellStates"].items(): idx_map[state.id] = state.idx id_map[state.idx] = state.id - if id>idmax: - idmax=id + if id > idmax: + idmax = id self.idToIdx = idx_map self.idxToId = id_map - self._next_id = idmax+1 - self._next_idx = len(data['cellStates']) + self._next_id = idmax + 1 + self._next_idx = len(data["cellStates"]) if self.integ: - if 'sigData' in data: - self.integ.setLevels(data['specData'],data['sigData']) - elif 'specData' in data: - self.integ.setLevels(data['specData']) - - + if "sigData" in data: + self.integ.setLevels(data["specData"], data["sigData"]) + elif "specData" in data: + self.integ.setLevels(data["specData"]) diff --git a/Scripts/batch.py b/scripts/batch.py similarity index 60% rename from Scripts/batch.py rename to scripts/batch.py index 6d618d2f..a9ceca55 100644 --- a/Scripts/batch.py +++ b/scripts/batch.py @@ -4,44 +4,49 @@ import string import shutil -from CellModeller.simulator import Simulator +from cellmodeller.simulator import Simulator max_cells = 50000 cell_buffer = 256 + def simulate(modfilename, platform, device, steps=50): - (path,name) = os.path.split(modfilename) - modname = str(name).split('.')[0] + path, name = os.path.split(modfilename) + modname = str(name).split(".")[0] sys.path.append(path) - sim = Simulator(modname, 0.025, clPlatformNum=platform, clDeviceNum=device, saveOutput=True) - while len(sim.cellStates) < max_cells-cell_buffer: + sim = Simulator( + modname, 0.025, clPlatformNum=platform, clDeviceNum=device, saveOutput=True + ) + while len(sim.cellStates) < max_cells - cell_buffer: sim.step() + def main(): # Get module name to load - if len(sys.argv)<2: + if len(sys.argv) < 2: print("Please specify a model (.py) file") exit(0) else: moduleName = sys.argv[1] # Get OpenCL platform/device numbers - if len(sys.argv)<3: + if len(sys.argv) < 3: # User input of OpenCL setup import pyopencl as cl + # Platform platforms = cl.get_platforms() print("Select OpenCL platform:") for i in range(len(platforms)): - print(('press '+str(i)+' for '+str(platforms[i]))) - platnum = int(eval(input('Platform Number: '))) + print(("press " + str(i) + " for " + str(platforms[i]))) + platnum = int(eval(input("Platform Number: "))) # Device devices = platforms[platnum].get_devices() print("Select OpenCL device:") for i in range(len(devices)): - print(('press '+str(i)+' for '+str(devices[i]))) - devnum = int(eval(input('Device Number: '))) + print(("press " + str(i) + " for " + str(devices[i]))) + devnum = int(eval(input("Device Number: "))) else: platnum = int(sys.argv[2]) devnum = int(sys.argv[3]) @@ -49,6 +54,7 @@ def main(): # Set up complete, now run the simulation simulate(moduleName, platnum, devnum) + # Make sure we are running as a script -if __name__ == "__main__": +if __name__ == "__main__": main() diff --git a/Scripts/batch_file.py b/scripts/batch_file.py similarity index 64% rename from Scripts/batch_file.py rename to scripts/batch_file.py index 5b11c171..6017c25e 100644 --- a/Scripts/batch_file.py +++ b/scripts/batch_file.py @@ -6,16 +6,15 @@ import time from exceptions import OSError -from CellModeller.simulator import Simulator +from cellmodeller.simulator import Simulator -sys.path.append('./') +sys.path.append("./") print((os.getcwd())) -import CellModeller.AdaptiveSimulator +import cellmodeller.AdaptiveSimulator - -sys.path.append('Models') +sys.path.append("Models") cells = 300000 cell_buffer = 256 @@ -24,7 +23,7 @@ try: mod_name = sys.argv[1] except: - mod_name = 'DaninoOscillator' + mod_name = "DaninoOscillator" try: pickleDir = sys.argv[2] @@ -34,21 +33,21 @@ try: pickleSteps = int(sys.argv[3]) except: - 'setting pickleSteps to %i' % pickleSteps + "setting pickleSteps to %i" % pickleSteps try: os.mkdir(pickleDir) except OSError: - print((pickleDir, 'exists')) + print((pickleDir, "exists")) pickleSetDir = os.path.join(pickleDir, mod_name) try: os.mkdir(pickleSetDir) except: - print((pickleSetDir, 'exists')) + print((pickleSetDir, "exists")) -folderName = time.strftime('%Y%m%d-%H%M%S', time.localtime()) +folderName = time.strftime("%Y%m%d-%H%M%S", time.localtime()) pickleFileRoot = os.path.join(pickleSetDir, folderName) max_cells = 300000 @@ -56,14 +55,15 @@ def simulate(mod_name, steps=50): - print('simulate') + print("simulate") sim = Simulator(mod_name, 0.25, None, pickleSteps=50, pickleFileRoot=pickleFileRoot) - print('start') + print("start") sim.phys.set_cells() sim.phys.calc_cell_geom() - while len(sim.cellStates) < max_cells-cell_buffer: + while len(sim.cellStates) < max_cells - cell_buffer: sim.step() + simulate(mod_name) diff --git a/scripts/batch_iter.py b/scripts/batch_iter.py new file mode 100644 index 00000000..cbcae202 --- /dev/null +++ b/scripts/batch_iter.py @@ -0,0 +1,25 @@ +import os +import sys +import subprocess +import string +import shutil + +from cellmodeller.simulator import Simulator + +sys.path.append("../../Models") + +max_cells = 100000 +cell_buffer = 256 + +gammas = [2, 5, 10, 15, 30, 50, 100, 500] + + +def simulate(mod_name, ingam, fname): + sim = Simulator(mod_name, 0.25, ingam, 25, fname) + while len(sim.cellStates) < max_cells - cell_buffer: + sim.step() + + +# here we are varying gamma from 1 to 50, every 2 +for i in range(len(gammas)): + simulate(sys.argv[1], gammas[i], (str(sys.argv[1]) + "_gamma=" + str(gammas[i]))) diff --git a/Scripts/cell_modeller_gui.py b/scripts/cell_modeller_gui.py similarity index 69% rename from Scripts/cell_modeller_gui.py rename to scripts/cell_modeller_gui.py index b91d5fcb..77191c40 100644 --- a/Scripts/cell_modeller_gui.py +++ b/scripts/cell_modeller_gui.py @@ -10,9 +10,9 @@ from PyQt5 import uic from PyQt5.QtCore import * -import CellModeller.gui.renderers -from CellModeller import simulator -from CellModeller.gui.py_gl_cm_viewer import PyGLCMViewer, RenderInfo +import cellmodeller.gui.renderers +from cellmodeller import simulator +from cellmodeller.gui.py_gl_cm_viewer import PyGLCMViewer, RenderInfo from pkg_resources import resource_stream import os @@ -22,7 +22,7 @@ qapp = QApplication([]) # The UI -uifile = resource_stream('CellModeller.gui', 'py_gl_gui.ui') +uifile = resource_stream("cellmodeller.gui", "py_gl_gui.ui") ui = uic.loadUi(uifile) ui.show() ui.raise_() @@ -34,7 +34,8 @@ label.setAlignment(Qt.AlignJustify) # Load a model if specified -if len(sys.argv) > 1: cmv.loadModelFile(sys.argv[1]) +if len(sys.argv) > 1: + cmv.loadModelFile(sys.argv[1]) # Launch app main loop sys.exit(qapp.exec_()) diff --git a/scripts/contact_graph.py b/scripts/contact_graph.py new file mode 100644 index 00000000..0e8e5ef4 --- /dev/null +++ b/scripts/contact_graph.py @@ -0,0 +1,154 @@ +import sys +import os +import math +import numpy as np +import pickle +import cellmodeller +import subprocess +import string +import shutil +from cellmodeller.simulator import Simulator +import networkx +from reportlab.pdfgen.canvas import Canvas +from reportlab.lib import units +from reportlab.lib.colors import Color +from reportlab.graphics.shapes import Circle + + +class CellModellerPDFGenerator(Canvas): + # --- + # Class that extends reportlab pdf canvas to draw CellModeller simulations + # --- + def __init__(self, name, network, bg_color): + self.name = name + self.data = data + self.bg_color = bg_color + Canvas.__init__(self, name) + self.network = network + + def setup_canvas(self, name, world, page, page_center): + worldx, worldy = world + pagex, pagey = page + page_centx, page_centy = page_center + self.setPageSize((pagex * units.cm, pagey * units.cm)) + self.setFillColor(self.bg_color) + self.rect(0, 0, pagex * units.cm, pagey * units.cm, fill=1) + self.translate(pagex * units.cm / 2.0, pagey * units.cm / 2.0) + self.translate(page_centx * units.cm, page_centy * units.cm) + self.scale(float(pagex * units.cm) / worldx, float(pagey * units.cm) / worldy) + + def drawNode(self, pos, col): + Canvas.setFillColor(self, col) + Canvas.circle(self, pos[0], pos[1], 0.3, fill=1) + + def drawEdge(self, pos1, pos2): + path = self.beginPath() + path.moveTo(pos1[0], pos1[1]) + path.lineTo(pos2[0], pos2[1]) + self.drawPath(path, fill=1) + + def draw_frame(self, name, world, page, center): + self.setup_canvas(name, world, page, center) + self.draw_graph() + self.showPage() + self.save() + + def draw_graph(self): + self.saveState() + self.setLineWidth(0.003 * units.cm) + self.setStrokeColor((0.0, 0.0, 0.0)) + positions = list(networkx.get_node_attributes(G, "pos").values()) + colors = list(networkx.get_node_attributes(G, "color").values()) + for u, v, di in self.network.edges_iter(data=True): + self.drawEdge(positions[u], positions[v]) + for n in self.network.nodes_iter(): + self.drawNode(positions[n], colors[n]) + self.restoreState() + + +def generate_network(G, num_cells, ct_tos, n_cts, pos, celldata): + for i in range(0, num_cells): # make all nodes + G.add_node( + i, + pos=pos[i], + type=celldata[i], + color=(1, 0, 0) if celldata[i] == 0 else (0, 1, 0), + ) + for i in range(0, num_cells): # make all edges (when all nodes are present) + for j in range(0, n_cts[i]): # make all edges from contact_tos + G.add_edge(i, ct_tos[i, j], width=8, color="black") + + +def get_current_contacts(G, data): + cs = data["cellStates"] + it = iter(cs) + n = len(cs) + cell_type = {} + pos_dict = {} + for it in cs: + cell_type[cs[it].idx] = cs[it].cellType + pos_dict[cs[it].idx] = cs[it].pos[0:2] + modname = data["moduleName"] + moduleStr = data["moduleStr"] + sim = Simulator(modname, 0.0, moduleStr=moduleStr, saveOutput=False) + sim.loadFromPickle(data) + sim.phys.update_grid() # we assume local cell_centers is current + sim.phys.bin_cells() + sim.phys.cell_sqs = sim.phys.cell_sqs_dev.get() # get updated cell sqs + sim.phys.sort_cells() + sim.phys.sorted_ids_dev.set(sim.phys.sorted_ids) # push changes to the device + sim.phys.sq_inds_dev.set(sim.phys.sq_inds) + sim.phys.find_contacts(predict=False) + sim.phys.get_cts() + ct_pts = ( + sim.phys.ct_pts + ) # these are the points on the cell surface - they can be transformed into the global coordinate system + ct_tos = ( + sim.phys.ct_tos + ) # this is the list of *some* contacts from each cell (only for lower cell_ids) + ct_dists = sim.phys.ct_dists + cell_cts = ( + sim.phys.cell_n_cts + ) # not really all the contacts of the cell, because the tos are only defined between a cell and ones with lower ids + generate_network(G, n, ct_tos, cell_cts, pos_dict, cell_type) + + +bg_color = Color(1.0, 1.0, 1.0, alpha=1.0) + + +G = networkx.Graph() +fname = sys.argv[1] +data = pickle.load(open(fname, "rb")) +cs = data["cellStates"] +it = iter(cs) +n = len(cs) +oname = fname.replace(".pickle", "_graph.pdf") + +print(("num_cells = " + str(n))) + +cell_type = {} +pos_dict = {} +for it in cs: + cell_type[cs[it].idx] = cs[it].cellType + pos_dict[cs[it].idx] = cs[it].pos[0:2] + +get_current_contacts(G, data) + +print(("num_contacts = " + str(networkx.number_of_edges(G)))) +degrees = list(G.degree().values()) +print(("mean degree = " + str(np.mean(degrees)))) + +if list(networkx.get_edge_attributes(G, "color").items()): + edges, ecolor = list(zip(*list(networkx.get_edge_attributes(G, "color").items()))) +ncolor = list(networkx.get_node_attributes(G, "color").values()) + +pdf = CellModellerPDFGenerator(oname, G, bg_color) +world = (120, 120) +page = (50, 50) +center = (0, 0) + +pdf.draw_frame(oname, world, page, center) + + +# from this, I want the spatial position of every contact, and the graph of the cells that each cell is touching +# This can be all in one data structure - a graph where each vertex has a position, and each node has a cellState diff --git a/Scripts/contact_video.sh b/scripts/contact_video.sh similarity index 91% rename from Scripts/contact_video.sh rename to scripts/contact_video.sh index 09ef0233..bbb3e25f 100755 --- a/Scripts/contact_video.sh +++ b/scripts/contact_video.sh @@ -7,7 +7,7 @@ # Run Draw2DPDF to generate pdf files for f in $( ls *.pickle ); do echo Processing: $f - $CMPATH/bin/cmpython $CMPATH/Scripts/contact_graph.py $f + $CMPATH/bin/cmpython $CMPATH/scripts/contact_graph.py $f done # Convert and resize etc. pdf files into jpegs diff --git a/Scripts/draw_2d_pdf.py b/scripts/draw_2d_pdf.py similarity index 57% rename from Scripts/draw_2d_pdf.py rename to scripts/draw_2d_pdf.py index af920463..0150d7d6 100644 --- a/Scripts/draw_2d_pdf.py +++ b/scripts/draw_2d_pdf.py @@ -12,21 +12,22 @@ mxsig0 = 0 + class CellModellerPDFGenerator(Canvas): # --- # Class that extends reportlab pdf canvas to draw CellModeller simulations # --- def __init__(self, name, data, bg_color): self.name = name - self.states = data.get('cellStates') + self.states = data.get("cellStates") - self.signal_levels = data.get('sigGrid', None) - self.signal_grid_orig = data.get('sigGridOrig', None) - self.signal_grid_dim = data.get('sigGridDim', None) - self.signal_grid_size = data.get('sigGridSize', None) + self.signal_levels = data.get("sigGrid", None) + self.signal_grid_orig = data.get("sigGridOrig", None) + self.signal_grid_dim = data.get("sigGridDim", None) + self.signal_grid_size = data.get("sigGridSize", None) self.signals = self.signal_levels is not None - - self.parents = data.get('lineage') + + self.parents = data.get("lineage") self.data = data self.bg_color = bg_color Canvas.__init__(self, name) @@ -38,40 +39,41 @@ def __init__(self, name, data, bg_color): # ---- def calc_cell_colors(self, state): # Generate Color objects from cellState, with black outline - (r,g,b) = state.color + r, g, b = state.color # Return value is tuple of colors, (fill, stroke) - return [Color(r,g,b,alpha=1.0) , Color(0,0,0,alpha=1.0)] + return [Color(r, g, b, alpha=1.0), Color(0, 0, 0, alpha=1.0)] + # ---- def setup_canvas(self, name, world, page, page_center): - worldx,worldy = world - pagex,pagey = page - page_centx,page_centy = page_center - self.setPageSize((pagex*units.cm, pagey*units.cm)) + worldx, worldy = world + pagex, pagey = page + page_centx, page_centy = page_center + self.setPageSize((pagex * units.cm, pagey * units.cm)) self.setFillColor(self.bg_color) - self.rect(0, 0, pagex*units.cm, pagey*units.cm, fill=1) - self.translate(pagex*units.cm/2.0, pagey*units.cm/2.0) - self.translate(page_centx*units.cm, page_centy*units.cm) - self.scale(float(pagex*units.cm)/worldx, float(pagey*units.cm)/worldy) + self.rect(0, 0, pagex * units.cm, pagey * units.cm, fill=1) + self.translate(pagex * units.cm / 2.0, pagey * units.cm / 2.0) + self.translate(page_centx * units.cm, page_centy * units.cm) + self.scale(float(pagex * units.cm) / worldx, float(pagey * units.cm) / worldy) def capsule_path(self, l, r): path = self.beginPath() - path.moveTo(-l/2.0, -r) - path.lineTo(l/2.0, -r) + path.moveTo(-l / 2.0, -r) + path.lineTo(l / 2.0, -r) - path.arcTo(l/2.0-r, -r, l/2.0+r, r, -90, 180) + path.arcTo(l / 2.0 - r, -r, l / 2.0 + r, r, -90, 180) - path.lineTo(-l/2.0, r) - path.arc(-l/2.0-r, -r, -l/2.0+r, r, 90, 180) - #path.close() + path.lineTo(-l / 2.0, r) + path.arc(-l / 2.0 - r, -r, -l / 2.0 + r, r, 90, 180) + # path.close() return path def draw_capsule(self, p, d, l, r, fill_color, stroke_color): self.saveState() self.setStrokeColor(stroke_color) self.setFillColor(fill_color) - self.setLineWidth(0.001*units.cm) - self.setLineWidth(0.001*units.cm) + self.setLineWidth(0.001 * units.cm) + self.setLineWidth(0.001 * units.cm) self.translate(p[0], p[1]) self.rotate(math.degrees(math.atan2(d[1], d[0]))) path = self.capsule_path(l, r) @@ -89,41 +91,43 @@ def draw_cells(self): def draw_chamber(self): # for EdgeDetectorChamber-22-57-02-06-12 - self.setLineWidth(0.015*units.cm) + self.setLineWidth(0.015 * units.cm) self.line(-100, -16, 100, -16) self.line(-100, 16, 100, 16) def draw_signals(self, index=0, scale=0.0192, z=0): - ''' + """ Function for drawing signal grids, currently limited to 1 signal a plane at a fixed z-axis level through the grid index = index of signal to render scale = scale factor for signal level - z = height of slice through grid - ''' + z = height of slice through grid + """ global mxsig0 # for EdgeDetectorChamber-22-57-02-06-12 - l, orig, dim, levels = self.signal_grid_size, \ - self.signal_grid_orig, \ - self.signal_grid_dim, \ - self.signal_levels + l, orig, dim, levels = ( + self.signal_grid_size, + self.signal_grid_orig, + self.signal_grid_dim, + self.signal_levels, + ) levels = levels.reshape(dim) - mx = levels[index,:,:,z].max() - l = list(map(float,l)) + mx = levels[index, :, :, z].max() + l = list(map(float, l)) for i in range(dim[1]): - x = l[0]*i + orig[0] + x = l[0] * i + orig[0] for j in range(dim[2]): - y = l[1]*j + orig[1] - lvls = levels[index,i,j,z]/mx + y = l[1] * j + orig[1] + lvls = levels[index, i, j, z] / mx mxsig0 = max(lvls, mxsig0) self.setFillColorRGB(lvls, 0, 0) - self.rect(x-l[0]/2.0, y-l[1]/2.0, l[0], l[1], stroke=0, fill=1) + self.rect(x - l[0] / 2.0, y - l[1] / 2.0, l[0], l[1], stroke=0, fill=1) def draw_frame(self, name, world, page, center): self.setup_canvas(name, world, page, center) - #draw_chamber(c) - if self.signals: + # draw_chamber(c) + if self.signals: print("Drawing signals") self.draw_signals() self.draw_cells() @@ -141,62 +145,67 @@ def computeBox(self): mnx = -20 mxy = 20 mny = -20 - for (id,s) in list(self.states.items()): - pos = s.pos - l = s.length # add/sub length to keep cell in frame - mxx = max(mxx,pos[0]+l) - mnx = min(mnx,pos[0]-l) - mxy = max(mxy,pos[1]+l) - mny = min(mny,pos[1]-l) - w = (mxx-mnx) - h = (mxy-mny) - return (w,h) + for id, s in list(self.states.items()): + pos = s.pos + l = s.length # add/sub length to keep cell in frame + mxx = max(mxx, pos[0] + l) + mnx = min(mnx, pos[0] - l) + mxy = max(mxy, pos[1] + l) + mny = min(mny, pos[1] - l) + w = mxx - mnx + h = mxy - mny + return (w, h) + + # -#End class CellModellerPDFGenerator +# End class CellModellerPDFGenerator + def importPickle(fname): - if fname[-7:]=='.pickle': - print(('Importing CellModeller pickle file: %s'%fname)) - data = pickle.load(open(fname, 'rb')) + if fname[-7:] == ".pickle": + print(("Importing CellModeller pickle file: %s" % fname)) + data = pickle.load(open(fname, "rb")) - # Check for old-style pickle that is tuple, + # Check for old-style pickle that is tuple, # just extract cellStates from 1st element if isinstance(data, tuple): - data = {'cellStates':data[0]} + data = {"cellStates": data[0]} # Return dictionary of simulation data return data else: return None + # Define a pdf generator class with cell outline color same as fill color class MyPDFGenerator(CellModellerPDFGenerator): def calc_cell_colors(self, state): # Generate Color objects from cellState, fill=stroke - (r,g,b) = state.color + r, g, b = state.color # Return value is tuple of colors, (fill, stroke) - fcol = Color(r,g,b,alpha=1.0) - scol = Color(r*0.5,g*0.5,b*0.5,alpha=1.0) - return [fcol,scol] + fcol = Color(r, g, b, alpha=1.0) + scol = Color(r * 0.5, g * 0.5, b * 0.5, alpha=1.0) + return [fcol, scol] + def main(): # To do: parse some useful arguments as rendering options here # e.g. outline color, page size, etc. # # For now, put these options into variables here: - bg_color = Color(0,0,0,alpha=1.0) + bg_color = Color(0, 0, 0, alpha=1.0) # For now just assume a list of files infns = sys.argv[1:] for infn in infns: # File names - if infn[-7:]!='.pickle': - print(('Ignoring file %s, because its not a pickle...'%(infn))) + if infn[-7:] != ".pickle": + print(("Ignoring file %s, because its not a pickle..." % (infn))) continue - outfn = infn.replace('.pickle', '.pdf') - outfn = os.path.basename(outfn) # Put output in this dir - print(('Processing %s to generate %s'%(infn,outfn))) - + outfn = infn.replace(".pickle", ".pdf") + outfn = os.path.basename(outfn) # Put output in this dir + print(("Processing %s to generate %s" % (infn, outfn))) + # Import data data = importPickle(infn) if not data: @@ -209,19 +218,19 @@ def main(): # Get the bounding square of the colony to size the image # This will resize the image to fit the page... # ** alternatively you can specify a fixed world size here - '''(w,h) = pdf.computeBox() + """(w,h) = pdf.computeBox() sqrt2 = math.sqrt(2) - world = (w/sqrt2,h/sqrt2)''' - world = (150,150) + world = (w/sqrt2,h/sqrt2)""" + world = (150, 150) # Page setup - page = (20,20) - center = (0,0) + page = (20, 20) + center = (0, 0) # Render pdf - print(('Rendering PDF output to %s'%outfn)) + print(("Rendering PDF output to %s" % outfn)) pdf.draw_frame(outfn, world, page, center) -if __name__ == "__main__": - main() +if __name__ == "__main__": + main() diff --git a/Scripts/git_publish.sh b/scripts/git_publish.sh similarity index 100% rename from Scripts/git_publish.sh rename to scripts/git_publish.sh diff --git a/scripts/length_histogram.py b/scripts/length_histogram.py new file mode 100644 index 00000000..504eafd0 --- /dev/null +++ b/scripts/length_histogram.py @@ -0,0 +1,61 @@ +import sys +import os +import math +import numpy as np + +sys.path.append(".") +import pickle +import cellmodeller + +# import matplotlib.pyplot as plt + +file = sys.argv[1] # select pickle file in command line +output_file = open("LengthData.csv", "w") + + +def rad_pos(cellstate): + return np.sqrt( + cellstate.pos[0] * cellstate.pos[0] + cellstate.pos[1] * cellstate.pos[1] + ) + + +def lengthHist(pickle, bins, file=False): + print(("opening " + pickle)) + data = pickle.load(open(pickle, "r")) + cs = data["cellStates"] + it = iter(cs) + n = len(cs) + print(("Number of cells = " + str(n))) + lens = [] + r = [] + for it in cs: + radialPosition = rad_pos(cs[it]) + cellLength = cs[it].length + 2 * cs[it].radius + r.append(radialPosition) + lens.append(cellLength) + if file: + file.write(str(radialPosition) + ", " + str(cellLength) + "\n") + # plt.hist(lens,bins) + # heatmap, xedges, yedges = np.histogram2d(r, lens, bins=100) + # extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]] + # plt.imshow(heatmap,extent=extent,aspect='auto') + # plt.show() + # print r, lens + return r, lens + + +def dirLengthHist(dir, bins, file=False): + for f in os.listdir(dir): + if ".pickle" in f: + number = f[5:-7] + if file: + fout = open("LengthData" + number + ".csv") + print(("step number = ", number)) + n, lens = lengthHist(dir + f, bins, file) + if file: + fout.close() + + +bin = np.arange(0.0, 6.0, 0.1) +lengthHist(file, bin, output_file) +output_file.close() diff --git a/Scripts/multi_batch.py b/scripts/multi_batch.py similarity index 67% rename from Scripts/multi_batch.py rename to scripts/multi_batch.py index c0cba510..dfb34d7c 100644 --- a/Scripts/multi_batch.py +++ b/scripts/multi_batch.py @@ -4,44 +4,47 @@ import string import shutil -from CellModeller.simulator import Simulator +from cellmodeller.simulator import Simulator max_cells = 100000 cell_buffer = 256 + def simulate(modfilename, platform, device, steps=50): - (path,name) = os.path.split(modfilename) - modname = str(name).split('.')[0] + path, name = os.path.split(modfilename) + modname = str(name).split(".")[0] sys.path.append(path) sim = Simulator(modname, 0.25, clPlatformNum=platform, clDeviceNum=device) - while len(sim.cellStates) < max_cells-cell_buffer: + while len(sim.cellStates) < max_cells - cell_buffer: sim.step() + def main(): # Get module name to load - if len(sys.argv)<2: + if len(sys.argv) < 2: print("Please specify a model (.py) file") exit(0) else: moduleName = sys.argv[1] # Get OpenCL platform/device numbers - if len(sys.argv)<3: + if len(sys.argv) < 3: # User input of OpenCL setup import pyopencl as cl + # Platform platforms = cl.get_platforms() print("Select OpenCL platform:") for i in range(len(platforms)): - print(('press '+str(i)+' for '+str(platforms[i]))) - platnum = int(eval(input('Platform Number: '))) + print(("press " + str(i) + " for " + str(platforms[i]))) + platnum = int(eval(input("Platform Number: "))) # Device devices = platforms[platnum].get_devices() print("Select OpenCL device:") for i in range(len(devices)): - print(('press '+str(i)+' for '+str(devices[i]))) - devnum = int(eval(input('Device Number: '))) + print(("press " + str(i) + " for " + str(devices[i]))) + devnum = int(eval(input("Device Number: "))) else: platnum = int(sys.argv[2]) devnum = int(sys.argv[3]) @@ -50,7 +53,7 @@ def main(): for i in range(3): simulate(moduleName, platnum, devnum) + # Make sure we are running as a script -if __name__ == "__main__": +if __name__ == "__main__": main() - diff --git a/scripts/print_timing.py b/scripts/print_timing.py new file mode 100644 index 00000000..8967bf6b --- /dev/null +++ b/scripts/print_timing.py @@ -0,0 +1,18 @@ +import sys +import os + +sys.path.append(".") +import pickle +import cellmodeller + +dir = os.path.join("data", sys.argv[1]) + +print("No. cells\ttime (s)") +print("-------------------") +for f in os.listdir(dir): + if "pickle" in f: + ff = os.path.join(dir, f) + cs, lin = pickle.load(open(ff, "r")) + n = len(cs) + t = os.path.getmtime(ff) + print(("%i\t%f" % (n, t))) diff --git a/scripts/spatial_analyze.py b/scripts/spatial_analyze.py new file mode 100644 index 00000000..9a8c386e --- /dev/null +++ b/scripts/spatial_analyze.py @@ -0,0 +1,47 @@ +import sys +import os +import math +import numpy as np + +sys.path.append(".") +import pickle +import cellmodeller +import matplotlib.pyplot as plt + +pname = sys.argv[1] +print(("opening " + pname)) +fout = open("spatial.txt", "w") +fout.write("r spec_level \n") + +bin_num = 20 +rad_max = 95 + +narray = np.zeros(bin_num) +specArray = np.zeros(bin_num) + +data = pickle.load(open(pname, "r")) +cs = data["cellStates"] +it = iter(cs) +n = len(cs) + +rArray = np.multiply(list(range(0, bin_num)), rad_max / bin_num) + +for it in cs: + r = np.sqrt(cs[it].pos[0] * cs[it].pos[0] + cs[it].pos[1] * cs[it].pos[1]) + for x in range(0, bin_num): + if (r >= x * rad_max / bin_num) and (r < (x + 1) * rad_max / bin_num): + specArray[x] = specArray[x] * narray[x] + cs[it].species[0] + narray[x] += 1 + specArray[x] = specArray[x] / narray[x] + +for x in range(0, bin_num): + fout.write(str((x + 1) * rad_max / bin_num / 2) + " " + str(narray[x])) + if narray[x] == 0: + fout.write(" 0.0\n") + else: + fout.write(" " + str(specArray[x]) + "\n") + +fout.close() + +plt.plot(rArray, specArray) +plt.show() diff --git a/Scripts/video.sh b/scripts/video.sh similarity index 91% rename from Scripts/video.sh rename to scripts/video.sh index feb681c7..ee880a1f 100755 --- a/Scripts/video.sh +++ b/scripts/video.sh @@ -7,7 +7,7 @@ # Run Draw2DPDF to generate pdf files for f in $( ls *.pickle ); do echo Processing: $f - python $HOME/Code/CellModeller/Scripts/draw_2d_pdf.py $f + python $HOME/Code/CellModeller/scripts/draw_2d_pdf.py $f done # Convert and resize etc. pdf files into jpegs diff --git a/setup.py b/setup.py index cfb4e15a..d0a961e6 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ from setuptools import setup import subprocess -version_py = os.path.join(os.path.dirname(__file__), 'CellModeller/version.py') +version_py = os.path.join(os.path.dirname(__file__), 'cellmodeller/version.py') try: version_git = subprocess.check_output(["git", "describe"], text=True).strip() @@ -24,9 +24,9 @@ 'setuptools>=70,<82', ], packages=[ - 'CellModeller', - 'CellModeller.Regulation', - 'CellModeller.gui' + 'cellmodeller', + 'cellmodeller.regulation', + 'cellmodeller.gui' ], package_data={'': ['*.cl', '*.ui']}, python_requires='>=3',