def set_label(self, new_name): if self.initSize == Vector(): self.size = Vector() self.name = new_name self.buildCaption() # It's after buildCaption() because self.size is decided only after buildCaption() if size=(0,0,0) self.radius = self.size.length()/2
def draw(self): """Draw sphere particle.""" hs = self.radius glColor4f(self.sidecurcolour[0] / 256.0, self.sidecurcolour[1] / 256.0, self.sidecurcolour[2] / 256.0, 0.5) # Create a quadratic object for sphere rendering quadratic = gluNewQuadric() gluQuadricNormals(quadratic, GLU_SMOOTH) gluQuadricTexture(quadratic, GL_TRUE) # Add texture glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, self.texID) glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE) # Draw sphere gluSphere(quadratic, hs, 32, 32) #glutSolidSphere(hs,32,32) glDisable(GL_TEXTURE_2D) # Draw links glMatrixMode(GL_MODELVIEW) glPushMatrix() glLoadMatrixf(self.linkTransform.getMatrix()) for p in self.bondedTo: glBegin(GL_LINES) glVertex3f(*self.initialpos.toTuple()) glVertex3f(*(Vector(*p.pos) - Vector(*self.pos)).toTuple()) glEnd() glPopMatrix()
def buildCaption(self): """Pre-render the text to go on the label.""" # Text is rendered to self.image if self.pic is not None: if self.pic.find('://') != -1 and not os.path.exists(self.pic): """ FIXME: either use thread to wrap urlopen or kamaelia HTTP components in case urlopen is blocked """ fObject = urlopen(self.pic) picData = fObject.read() pic = StringIO(picData) else: pic = self.pic self.image = pygame.image.load(pic).convert() else: pygame.font.init() font = pygame.font.Font(None, self.fontsize) self.image = font.render(self.name,True, self.foregroundColour, ) if self.size != Vector(0,0,0): texsize = (self.size.x*self.pixelscaling, self.size.y*self.pixelscaling) else: texsize = ( self.image.get_width()+2*self.margin, self.image.get_height()+2*self.margin ) self.size=Vector(texsize[0]/float(self.pixelscaling), texsize[1]/float(self.pixelscaling), self.thickness) # create power of 2 dimensioned surface pow2size = (int(2**(math.ceil(math.log(texsize[0]+2*self.margin, 2)))), int(2**(math.ceil(math.log(texsize[1]+2*self.margin, 2))))) textureSurface = pygame.Surface(pow2size) textureSurface.fill( self.backgroundColour ) # determine texture coordinates self.tex_w = float(texsize[0])/pow2size[0] self.tex_h = float(texsize[1])/pow2size[1] # copy image data to pow2surface dest = ( max((texsize[0]-self.image.get_width())/2, 0), max((texsize[1]-self.image.get_height())/2, 0) ) textureSurface.blit(self.image, dest) # textureSurface.set_alpha(128) textureSurface = textureSurface.convert_alpha() # read pixel data textureData = pygame.image.tostring(textureSurface, "RGBX", 1) #print self.image.get_width(), self.image.get_height() #print textureSurface.get_width(), textureSurface.get_height() #print textureData self.texID = glGenTextures(1) # create texture glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, self.texID) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, textureSurface.get_width(), textureSurface.get_height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData ); glDisable(GL_TEXTURE_2D)
def frame(self): while self.dataReady("position"): self.position = Vector(*self.recv("position")) if self.lastValidPos is None: self.lastValidPos = self.position.copy() while self.dataReady("inbox"): msg = self.recv("inbox") if msg == "ACK": self.lastValidPos = self.position.copy() elif msg == "INVALID": diff = self.lastValidPos - self.position diff.z = 0 self.send(diff.toTuple(), "movement")
def rotateParticles(self, particles, dAngle): """\ Rotate the particles around their common centre dAngle degree. Particles is a list; dAngle is a triple tuple of degree. If particles are given an empty list, rotate all particles instead. """ if particles == []: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) if dAngle[0] != 0: # Rotate around x axis for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z * relativePosVector.z + relativePosVector.y * relativePosVector.y)**0.5 newAngle = ( math.atan2(relativePosVector.z, relativePosVector.y) + dAngle[0] * math.pi / 180) particle.pos = (posVector.x, radius * math.cos(newAngle) + centrePoint.y, radius * math.sin(newAngle) + centrePoint.z) particle.drotation += Vector(dAngle[0], 0, 0) if dAngle[1] != 0: # Rotate around y axis for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z * relativePosVector.z + relativePosVector.x * relativePosVector.x)**0.5 newAngle = ( math.atan2(relativePosVector.z, relativePosVector.x) + dAngle[1] * math.pi / 180) particle.pos = (radius * math.cos(newAngle) + centrePoint.x, posVector.y, radius * math.sin(newAngle) + centrePoint.z) particle.drotation += Vector(0, -dAngle[1], 0) if dAngle[2] != 0: # Rotate around z axis for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.x * relativePosVector.x + relativePosVector.y * relativePosVector.y)**0.5 newAngle = ( math.atan2(relativePosVector.y, relativePosVector.x) + dAngle[2] * math.pi / 180) particle.pos = (radius * math.cos(newAngle) + centrePoint.x, radius * math.sin(newAngle) + centrePoint.y, posVector.z) particle.drotation += Vector(0, 0, dAngle[2]) # An angle keeps the same with when it minus muptiple 360 particle.drotation %= 360
def buildCaption(self): """Pre-render the text to go on the label.""" # Text is rendered to self.image if self.image is not None: if self.imageIO is not None: self.imageSurface = pygame.image.load(self.imageIO).convert() self.imageIO = None elif os.path.exists(self.image): self.imageSurface = pygame.image.load(self.image).convert() # Image texture is used instead of label texture if 'image' argument is specified elif self.image.find('://') != -1: # Use text label for notification of waiting before the picture is available pygame.font.init() font = pygame.font.Font(None, self.fontsize) self.imageSurface = font.render("Loading image...",True, self.fgcurcolour, ) # Use thread to wrap urlopen in case urlopen is blocked import thread thread.start_new(self.readURLFile, ()) else: # Label texture is used if 'image' argument is not specified pygame.font.init() font = pygame.font.Font(None, self.fontsize) self.imageSurface = font.render(self.name,True, self.fgcurcolour, ) if self.size != Vector(0,0,0): texsize = (self.size.x*self.pixelscaling, self.size.y*self.pixelscaling) else: texsize = ( self.imageSurface.get_width()+2*self.margin, self.imageSurface.get_height()+2*self.margin ) self.size=Vector(texsize[0]/float(self.pixelscaling), texsize[1]/float(self.pixelscaling), self.thickness) # create power of 2 dimensioned surface pow2size = (int(2**(math.ceil(math.log(texsize[0]+2*self.margin, 2)))), int(2**(math.ceil(math.log(texsize[1]+2*self.margin, 2))))) textureSurface = pygame.Surface(pow2size) textureSurface.fill( self.bgcurcolour ) # determine texture coordinates self.tex_w = float(texsize[0])/pow2size[0] self.tex_h = float(texsize[1])/pow2size[1] # copy image data to pow2surface dest = ( max((texsize[0]-self.imageSurface.get_width())/2, 0), max((texsize[1]-self.imageSurface.get_height())/2, 0) ) textureSurface.blit(self.imageSurface, dest) textureSurface = textureSurface.convert_alpha() # read pixel data textureData = pygame.image.tostring(textureSurface, "RGBX", 1) self.texID = glGenTextures(1) # create texture glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, self.texID) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, textureSurface.get_width(), textureSurface.get_height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData ); glDisable(GL_TEXTURE_2D)
def draw(self): """Draw teapot particle.""" hs = self.radius glColor4f(self.sidecurcolour[0] / 256.0, self.sidecurcolour[1] / 256.0, self.sidecurcolour[2] / 256.0, 0.5) # Add texture glMatrixMode(GL_TEXTURE) glPushMatrix() glLoadIdentity() glRotatef(180.0, 0.0, 0.0, 1.0) glMatrixMode(GL_MODELVIEW) glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, self.texID) glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE) # Draw teapot glFrontFace(GL_CW) glutSolidTeapot(hs) glFrontFace(GL_CCW) glDisable(GL_TEXTURE_2D) glMatrixMode(GL_TEXTURE) glPopMatrix() glMatrixMode(GL_MODELVIEW) # Draw links glMatrixMode(GL_MODELVIEW) glPushMatrix() glLoadMatrixf(self.linkTransform.getMatrix()) for p in self.bondedTo: glBegin(GL_LINES) glVertex3f(*self.initialpos.toTuple()) glVertex3f(*(Vector(*p.pos) - Vector(*self.pos)).toTuple()) glEnd() glPopMatrix() # Licensed to the BBC under a Contributor Agreement: CL
def applyTransforms(self): """ Use the objects translation/rotation/scaling values to generate a new transformation Matrix if changes have happened. """ # generate new transformation matrix if needed if self.oldscaling != self.scaling or self.drotation != Vector() or self.oldpos != Vector(*self.pos): self.transform = Transform() self.linkTransform = Transform() drotationTransform = Transform() drotationTransform.applyRotation(self.drotation) self.transform.applyScaling(self.scaling) self.linkTransform.applyScaling(self.scaling) self.transform = self.transform*self.oldrotTransform*drotationTransform self.oldrotTransform = self.oldrotTransform*drotationTransform self.transform.applyTranslation(Vector(*self.pos)) self.linkTransform.applyTranslation(Vector(*self.pos)) if self.oldscaling != self.scaling: self.oldscaling = self.scaling.copy() self.drotation = Vector() if self.oldpos != Vector(*self.pos): self.oldpos = Vector(*self.pos) # send new transform to display service transform_update = { "TRANSFORM_UPDATE": True, "objectid": id(self), "transform": self.transform } return transform_update else: return None
def handleEvents(self): while self.dataReady("events"): setPullPoint = False event = self.recv("events") if (event.type == pygame.MOUSEBUTTONDOWN and event.button == 1 and self.identifier in event.hitobjects): self.pulling = True setPullPoint = True if (event.type == pygame.MOUSEBUTTONUP and self.pulling): self.pulling = False self.foldpoint, self.folddelta = calcFoldLine( (0.0, 0.0), (0.0, 0.0), self.radius) if self.pulling: # transform vertices for intersection test transformedPoly = [ self.transform.transformVector(Vector(x, y, 0.0)) for ((x, y), _) in self.polys[0] ] # calculate distance of intersection t = Intersect.ray_Plane(Vector(0, 0, 0), event.direction, transformedPoly[0:3]) # point of intersection p = event.direction * t point = mapPlaneToPoly( transformedPoly[0].toTuple(), transformedPoly[1].toTuple(), transformedPoly[2].toTuple(), self.polys[0][0][0], self.polys[0][1][0], self.polys[0][2][0], p.toTuple(), ) if setPullPoint: self.pullpoint = point[0], point[1] self.foldpoint, self.folddelta = calcFoldLine( self.pullpoint, point, self.radius)
def __init__(self, position=(-1, 0, -10), ID="", **argd): """x.__init__(...) initializes x; see x.__class__.__doc__ for signature""" super(Particle3D, self).__init__(position=position, ID=ID) self.pos = position self.initSize = Vector(*argd.get("size", (0, 0, 0))) self.selected = False self.bgcolour = argd.get("bgcolour", (230, 230, 230)) self.fgcolour = argd.get("fgcolour", (0, 0, 0)) self.sidecolour = argd.get("sidecolour", (200, 200, 244)) self.bgcolourselected = argd.get("bgcolourselected", (0, 0, 0)) self.fgcolourselected = argd.get("fgcolourselected", (244, 244, 244)) self.sidecolourselected = argd.get("sidecolourselected", (0, 0, 100)) self.margin = argd.get("margin", 8) self.fontsize = argd.get("fontsize", 50) self.pixelscaling = argd.get("pixelscaling", 100) self.thickness = argd.get("thickness", 0.3) # For picture texture self.image = argd.get("image", None) # For remote picture self.imageIO = None name = argd.get("name", "NoName") self.set_label(name) # For rotation and scaling self.drotation = Vector() self.scaling = Vector(*argd.get("scaling", (1, 1, 1))) # For detection of changes self.oldpos = self.initialpos = Vector() self.oldscaling = Vector() # For transformation matrix multiplication # Store all transformations self.transform = Transform() # Specially store link transformations because link doesn't do rotation and scaling self.linkTransform = Transform() # Store all previous transformations to be multiplied with the current one self.oldrotTransform = Transform() # For redraw detection self.needRedraw = True # For drag handling self.oldpoint = None
def draw(self): """ DRAW teapot particle.""" hs = self.radius # Add texture glMatrixMode(GL_TEXTURE) glPushMatrix() glLoadIdentity() glRotatef(180.0,0.0,0.0,1.0) glMatrixMode(GL_MODELVIEW) glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, self.texID) glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE) # Draw teapot glFrontFace(GL_CW) glutSolidTeapot(hs) glFrontFace(GL_CCW) glDisable(GL_TEXTURE_2D) glMatrixMode(GL_TEXTURE) glPopMatrix() glMatrixMode(GL_MODELVIEW) # Draw links glMatrixMode(GL_MODELVIEW) glPushMatrix() glLoadMatrixf(self.linkTransform.getMatrix()) for p in self.bondedTo: glBegin(GL_LINES) glVertex3f(*self.initialpos.toTuple()) glVertex3f(*(Vector(*p.pos)-Vector(*self.pos)).toTuple()) glEnd() glPopMatrix()
def __init__(self, position=(-1, 0, -10), ID='', **argd): """x.__init__(...) initializes x; see x.__class__.__doc__ for signature""" super(Particle3D, self).__init__(position=position, ID=ID) self.pos = position self.initSize = Vector(*argd.get("size", (0, 0, 0))) self.selected = False self.bgcolour = argd.get("bgcolour", (230, 230, 230)) self.fgcolour = argd.get("fgcolour", (0, 0, 0)) self.sidecolour = argd.get("sidecolour", (200, 200, 244)) self.bgcolourselected = argd.get("bgcolourselected", (0, 0, 0)) self.fgcolourselected = argd.get("fgcolourselected", (244, 244, 244)) self.sidecolourselected = argd.get("sidecolourselected", (0, 0, 100)) self.margin = argd.get("margin", 8) self.fontsize = argd.get("fontsize", 50) self.pixelscaling = argd.get("pixelscaling", 100) self.thickness = argd.get("thickness", 0.3) # For picture texture self.image = argd.get("image", None) # For remote picture self.imageIO = None name = argd.get("name", "NoName") self.set_label(name) # For rotation and scaling self.drotation = Vector() self.scaling = Vector(*argd.get("scaling", (1, 1, 1))) # For detection of changes self.oldpos = self.initialpos = Vector() self.oldscaling = Vector() # For transformation matrix multiplication # Store all transformations self.transform = Transform() # Specially store link transformations because link doesn't do rotation and scaling self.linkTransform = Transform() # Store all previous transformations to be multiplied with the current one self.oldrotTransform = Transform() # For redraw detection self.needRedraw = True # For drag handling self.oldpoint = None
def gotoDisplayLevel(self, dlevel): # Save current level's viewer position self.levelViewerPos[ self.currentLevel] = self.display.viewerposition.copy() # Display next level self.currentLevel += dlevel # Reset viewer position to previous try: self.display.viewerposition = self.levelViewerPos[ self.currentLevel].copy() except KeyError: self.display.viewerposition = self.levelViewerPos[ self.currentLevel] = Vector() # Remove current displayed particles for particle in self.currentDisplayedPhysics.particles: self.display.ogl_displaylists.pop(id(particle)) self.display.ogl_transforms.pop(id(particle)) self.currentDisplayedPhysics.removeByID( *self.currentDisplayedPhysics.particleDict.keys())
def __init__(self, position = (-1,0,-10), ID='', **argd): super(Particle3D, self).__init__(position=position, ID = ID) self.pos = position self.initSize = Vector(*argd.get("size", (0,0,0))) self.backgroundColourWhenUnselected = self.backgroundColour = argd.get("bgcolour", (230,230,230)) self.foregroundColourWhenUnselected = self.foregroundColour = argd.get("fgcolour", (0,0,0)) self.sideColourWhenUnselected = self.sideColour = argd.get("sidecolour", (200,200,244)) self.backgroundColourWhenSelected = argd.get("bgcolourselected", (0,0,0)) self.foregroundColourWhenSelected = argd.get("fgcolourselected", (244,244,244)) self.sideColourWhenSelected = argd.get("sidecolourselected", (200,200,244)) self.margin = argd.get("margin", 8) self.fontsize = argd.get("fontsize", 50) self.pixelscaling = argd.get("pixelscaling", 100) self.thickness = argd.get("thickness", 0.3) # For picture texture self.pic = argd.get("image", None) name = argd.get("name","NoName") self.set_label(name) # For rotation and scaling self.drotation = Vector() self.scaling = Vector( *argd.get("scaling", (1,1,1) ) ) # For detection of changes self.oldpos = self.initialpos = Vector() self.oldscaling = Vector() # For transformation matrix multiplication self.transform = Transform() self.linkTransform = Transform() self.oldrotTransform = Transform() # For redraw detection self.needRedraw = True # For drag handling self.oldpoint = None
def buildCaption(self): """Pre-render the text to go on the label.""" # Text is rendered to self.image if self.image is not None: if self.imageIO is not None: self.imageSurface = pygame.image.load(self.imageIO).convert() self.imageIO = None elif os.path.exists(self.image): self.imageSurface = pygame.image.load(self.image).convert() # Image texture is used instead of label texture if 'image' argument is specified elif self.image.find('://') != -1: # Use text label for notification of waiting before the picture is available pygame.font.init() font = pygame.font.Font(None, self.fontsize) self.imageSurface = font.render( "Loading image...", True, self.fgcurcolour, ) # Use thread to wrap urlopen in case urlopen is blocked import thread thread.start_new(self.readURLFile, ()) else: # Label texture is used if 'image' argument is not specified pygame.font.init() font = pygame.font.Font(None, self.fontsize) self.imageSurface = font.render( self.name, True, self.fgcurcolour, ) if self.size != Vector(0, 0, 0): texsize = (self.size.x * self.pixelscaling, self.size.y * self.pixelscaling) else: texsize = (self.imageSurface.get_width() + 2 * self.margin, self.imageSurface.get_height() + 2 * self.margin) self.size = Vector(texsize[0] / float(self.pixelscaling), texsize[1] / float(self.pixelscaling), self.thickness) # create power of 2 dimensioned surface pow2size = (int(2**(math.ceil(math.log(texsize[0] + 2 * self.margin, 2)))), int(2**(math.ceil(math.log(texsize[1] + 2 * self.margin, 2))))) textureSurface = pygame.Surface(pow2size) textureSurface.fill(self.bgcurcolour) # determine texture coordinates self.tex_w = float(texsize[0]) / pow2size[0] self.tex_h = float(texsize[1]) / pow2size[1] # copy image data to pow2surface dest = (max((texsize[0] - self.imageSurface.get_width()) / 2, 0), max((texsize[1] - self.imageSurface.get_height()) / 2, 0)) textureSurface.blit(self.imageSurface, dest) textureSurface = textureSurface.convert_alpha() # read pixel data textureData = pygame.image.tostring(textureSurface, "RGBX", 1) self.texID = glGenTextures(1) # create texture glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, self.texID) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureSurface.get_width(), textureSurface.get_height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData) glDisable(GL_TEXTURE_2D)
def __init__(self, **argd): super(SphereParticle3D, self).__init__(**argd) self.drotation = Vector(0,0,90)
class Particle3D(BaseParticle): """\ A super class for 3D particles super(RenderingParticle3D, self).__init__(**argd) Simple 3D generic superclass particle for topology visualisation. Keyword arguments: - ID -- a unique ID for this particle - position -- (x,y,z) tuple of particle coordinates - name -- A name this particle will be labelled with - bgcolour -- Colour of surfaces behind text label (default=(230,230,230)), only apply to label texture - fgcolour -- Colour of the text label (default=(0,0,0), only apply to label texture - sidecolour -- Colour of side planes (default=(200,200,244)), only apply to CuboidParticle3D - bgcolourselected -- Background colour when the particle is selected (default=(0,0,0) - bgcolourselected -- Frontground colour when the particle is selected (default=(244,244,244)) - sidecolourselected -- Side colour when the particle is selected (default=(0,0,100)) - size -- Size of particle (length, width, depth), it depends on texture size if unspecified - margin -- Margin size in pixels (default=8) - fontsize -- Font size for label text (default=50) - pixelscaling -- Factor to convert pixels to units in 3d, ignored if size is specified (default=100) - thickness -- Thickness of button widget, ignored if size is specified (default=0.3) - image -- The uri of image, image texture instead of label texture is used if specified """ def __init__(self, position=(-1, 0, -10), ID='', **argd): """x.__init__(...) initializes x; see x.__class__.__doc__ for signature""" super(Particle3D, self).__init__(position=position, ID=ID) self.pos = position self.initSize = Vector(*argd.get("size", (0, 0, 0))) self.selected = False self.bgcolour = argd.get("bgcolour", (230, 230, 230)) self.fgcolour = argd.get("fgcolour", (0, 0, 0)) self.sidecolour = argd.get("sidecolour", (200, 200, 244)) self.bgcolourselected = argd.get("bgcolourselected", (0, 0, 0)) self.fgcolourselected = argd.get("fgcolourselected", (244, 244, 244)) self.sidecolourselected = argd.get("sidecolourselected", (0, 0, 100)) self.margin = argd.get("margin", 8) self.fontsize = argd.get("fontsize", 50) self.pixelscaling = argd.get("pixelscaling", 100) self.thickness = argd.get("thickness", 0.3) # For picture texture self.image = argd.get("image", None) # For remote picture self.imageIO = None name = argd.get("name", "NoName") self.set_label(name) # For rotation and scaling self.drotation = Vector() self.scaling = Vector(*argd.get("scaling", (1, 1, 1))) # For detection of changes self.oldpos = self.initialpos = Vector() self.oldscaling = Vector() # For transformation matrix multiplication # Store all transformations self.transform = Transform() # Specially store link transformations because link doesn't do rotation and scaling self.linkTransform = Transform() # Store all previous transformations to be multiplied with the current one self.oldrotTransform = Transform() # For redraw detection self.needRedraw = True # For drag handling self.oldpoint = None def get_bgcolour(self): """Get bgcolour.""" return self._bgcolour def set_bgcolour(self, value): """Set bgcolour and bgcurcolour as well if it is not selected.""" self._bgcolour = value if not self.selected: self.bgcurcolour = value bgcolour = property(get_bgcolour, set_bgcolour, None, None) def get_fgcolour(self): """Get fgcolour.""" return self._fgcolour def set_fgcolour(self, value): """Set fgcolour and fgcurcolour as well if it is not selected.""" self._fgcolour = value if not self.selected: self.fgcurcolour = value fgcolour = property(get_fgcolour, set_fgcolour, None, None) def get_sidecolour(self): """Get sidecolour.""" return self._sidecolour def set_sidecolour(self, value): """Set sidecolour and sidecurcolour as well if it is not selected.""" self._sidecolour = value if not self.selected: self.sidecurcolour = value sidecolour = property(get_sidecolour, set_sidecolour, None, None) def get_bgcolourselected(self): """Get bgcolourselected.""" return self._bgcolourselected def set_bgcolourselected(self, value): """Set bgcolourselected and bgcurcolour as well if it is selected.""" self._bgcolourselected = value if self.selected: self.bgcurcolour = value bgcolourselected = property(get_bgcolourselected, set_bgcolourselected, None, None) def get_fgcolourselected(self): """Get fgcolourselected.""" return self._fgcolourselected def set_fgcolourselected(self, value): """Set fgcolourselected and fgcurcolour as well if it is selected.""" self._fgcolourselected = value if self.selected: self.fgcurcolour = value fgcolourselected = property(get_fgcolourselected, set_fgcolourselected, None, None) def get_sidecolourselected(self): """Get sidecolourselected.""" return self._sidecolourselected def set_sidecolourselected(self, value): """Set sidecolourselected and sidecurcolour as well if it is selected.""" self._sidecolourselected = value if self.selected: self.sidecurcolour = value sidecolourselected = property(get_sidecolourselected, set_sidecolourselected, None, None) def set_label(self, new_name): """Set text label.""" if self.initSize == Vector(): self.size = Vector() self.name = new_name self.buildCaption() # It's after buildCaption() because self.size is decided only after buildCaption() if size=(0,0,0) self.radius = self.size.length() / 2 def updateAttrs(self, **params): """Update attributes.""" for key, value in params.iteritems(): setattr(self, key, value) self.buildCaption() def draw(self): """\Stub method Override this method to draw concrete particles and links. """ pass def readURLFile(self): """Read a string buffer of an object denoted by a URL.""" fObject = urlopen(self.image) imageData = fObject.read() self.imageIO = StringIO(imageData) # Text label is not needed for picture texture self.set_label("Dummy") def buildCaption(self): """Pre-render the text to go on the label.""" # Text is rendered to self.image if self.image is not None: if self.imageIO is not None: self.imageSurface = pygame.image.load(self.imageIO).convert() self.imageIO = None elif os.path.exists(self.image): self.imageSurface = pygame.image.load(self.image).convert() # Image texture is used instead of label texture if 'image' argument is specified elif self.image.find('://') != -1: # Use text label for notification of waiting before the picture is available pygame.font.init() font = pygame.font.Font(None, self.fontsize) self.imageSurface = font.render( "Loading image...", True, self.fgcurcolour, ) # Use thread to wrap urlopen in case urlopen is blocked import thread thread.start_new(self.readURLFile, ()) else: # Label texture is used if 'image' argument is not specified pygame.font.init() font = pygame.font.Font(None, self.fontsize) self.imageSurface = font.render( self.name, True, self.fgcurcolour, ) if self.size != Vector(0, 0, 0): texsize = (self.size.x * self.pixelscaling, self.size.y * self.pixelscaling) else: texsize = (self.imageSurface.get_width() + 2 * self.margin, self.imageSurface.get_height() + 2 * self.margin) self.size = Vector(texsize[0] / float(self.pixelscaling), texsize[1] / float(self.pixelscaling), self.thickness) # create power of 2 dimensioned surface pow2size = (int(2**(math.ceil(math.log(texsize[0] + 2 * self.margin, 2)))), int(2**(math.ceil(math.log(texsize[1] + 2 * self.margin, 2))))) textureSurface = pygame.Surface(pow2size) textureSurface.fill(self.bgcurcolour) # determine texture coordinates self.tex_w = float(texsize[0]) / pow2size[0] self.tex_h = float(texsize[1]) / pow2size[1] # copy image data to pow2surface dest = (max((texsize[0] - self.imageSurface.get_width()) / 2, 0), max((texsize[1] - self.imageSurface.get_height()) / 2, 0)) textureSurface.blit(self.imageSurface, dest) textureSurface = textureSurface.convert_alpha() # read pixel data textureData = pygame.image.tostring(textureSurface, "RGBX", 1) self.texID = glGenTextures(1) # create texture glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, self.texID) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureSurface.get_width(), textureSurface.get_height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData) glDisable(GL_TEXTURE_2D) def applyTransforms(self): """Use the objects translation/rotation/scaling values to generate a new transformation Matrix if changes have happened.""" # generate new transformation matrix if needed if self.oldscaling != self.scaling or self.drotation != Vector( ) or self.oldpos != Vector(*self.pos): self.transform = Transform() self.linkTransform = Transform() drotationTransform = Transform() drotationTransform.applyRotation(self.drotation) self.transform.applyScaling(self.scaling) self.linkTransform.applyScaling(self.scaling) self.transform = self.transform * self.oldrotTransform * drotationTransform self.oldrotTransform = self.oldrotTransform * drotationTransform self.transform.applyTranslation(Vector(*self.pos)) self.linkTransform.applyTranslation(Vector(*self.pos)) if self.oldscaling != self.scaling: self.oldscaling = self.scaling.copy() self.drotation = Vector() if self.oldpos != Vector(*self.pos): self.oldpos = Vector(*self.pos) # send new transform to display service transform_update = { "TRANSFORM_UPDATE": True, "objectid": id(self), "transform": self.transform } return transform_update else: return None def select(self): """Tell this particle it is selected""" self.selected = True self.sidecurcolour = self.sidecolourselected self.bgcurcolour = self.bgcolourselected self.fgcurcolour = self.fgcolourselected self.buildCaption() def deselect(self): """Tell this particle it is deselected""" self.selected = False self.sidecurcolour = self.sidecolour self.bgcurcolour = self.bgcolour self.fgcurcolour = self.fgcolour self.buildCaption()
def gotoDisplayLevel(self, dlevel): """Switch to another display level.""" isValid = False if self.currentLevel + dlevel > self.maxLevel: print("Warning: max hierarchy level has reached!") elif self.currentLevel + dlevel < 0: print("Warning: The first hierarchy level has reached!") else: if dlevel < 0: # Go to the last dlevel level self.previousParentParticleID = self.currentParentParticleID items = self.currentParentParticleID.split(':') for _ in xrange(-dlevel): items.pop() self.currentParentParticleID = ':'.join(items) isValid = True if dlevel == 1: # It only makes sense if dlevel == 1 when go to next dlevel level if len(self.selectedParticles) == 1: hasChildParticles = False for particle in self.physics.particles: if particle.ID.find( self.selectedParticles[0].ID ) == 0 and particle.ID != self.selectedParticles[0].ID: hasChildParticles = True break if hasChildParticles: self.previousParentParticleID = self.currentParentParticleID self.currentParentParticleID = self.selectedParticles[ 0].ID isValid = True else: print( 'Warning: The particle you double-clicked has no children!' ) else: print( "Tips: To extend a node, please double-click the node you want to extend" ) # Show the specified display level if valid if isValid: # Save current level's viewer position self.levelViewerPos[ self.currentLevel, self. previousParentParticleID] = self.display.viewerposition.copy() # Deselect all self.deselectAll() # Display next level self.currentLevel += dlevel # Reset viewer position to previous try: self.display.viewerposition = self.levelViewerPos[ self.currentLevel, self.currentParentParticleID].copy() except KeyError: self.display.viewerposition = self.levelViewerPos[ self.currentLevel, self.currentParentParticleID] = Vector() # Remove current displayed particles for particle in self.currentDisplayedPhysics.particles: self.display.ogl_displaylists.pop(id(particle)) self.display.ogl_transforms.pop(id(particle)) self.currentDisplayedPhysics.removeByID( *self.currentDisplayedPhysics.particleDict.keys()) # Add current level's particles to self.currentDisplayedPhysics.particles for display self.currentDisplayedPhysics.particles = [] if self.physics.particles != []: for particle in self.physics.particles: if self.currentParentParticleID == '': # If no parent, it's the top level if ':' not in particle.ID: self.currentDisplayedPhysics.add(particle) particle.oldpos = particle.initialpos # The child particles of self.currentParentParticleID elif particle.ID.find( self.currentParentParticleID ) == 0 and particle.ID.count(':') == self.currentLevel: self.currentDisplayedPhysics.add(particle) particle.oldpos = particle.initialpos
class Particle3D(BaseParticle): """\ A super class for 3D particles super(RenderingParticle3D, self).__init__(**argd) Simple 3D generic superclass particle for topology visualisation. Keyword arguments: - ID -- a unique ID for this particle - position -- (x,y,z) tuple of particle coordinates - name -- A name this particle will be labelled with - bgcolour -- Colour of surfaces behind text label (default=(230,230,230)), only apply to label texture - fgcolour -- Colour of the text label (default=(0,0,0), only apply to label texture - sidecolour -- Colour of side planes (default=(200,200,244)), only apply to CuboidParticle3D - bgcolourselected -- Background colour when the particle is selected (default=(0,0,0) - bgcolourselected -- Frontground colour when the particle is selected (default=(244,244,244)) - sidecolourselected -- Side colour when the particle is selected (default=(0,0,100)) - size -- Size of particle (length, width, depth), it depends on texture size if unspecified - margin -- Margin size in pixels (default=8) - fontsize -- Font size for label text (default=50) - pixelscaling -- Factor to convert pixels to units in 3d, ignored if size is specified (default=100) - thickness -- Thickness of button widget, ignored if size is specified (default=0.3) - image -- The uri of image, image texture instead of label texture is used if specified """ def __init__(self, position=(-1, 0, -10), ID="", **argd): """x.__init__(...) initializes x; see x.__class__.__doc__ for signature""" super(Particle3D, self).__init__(position=position, ID=ID) self.pos = position self.initSize = Vector(*argd.get("size", (0, 0, 0))) self.selected = False self.bgcolour = argd.get("bgcolour", (230, 230, 230)) self.fgcolour = argd.get("fgcolour", (0, 0, 0)) self.sidecolour = argd.get("sidecolour", (200, 200, 244)) self.bgcolourselected = argd.get("bgcolourselected", (0, 0, 0)) self.fgcolourselected = argd.get("fgcolourselected", (244, 244, 244)) self.sidecolourselected = argd.get("sidecolourselected", (0, 0, 100)) self.margin = argd.get("margin", 8) self.fontsize = argd.get("fontsize", 50) self.pixelscaling = argd.get("pixelscaling", 100) self.thickness = argd.get("thickness", 0.3) # For picture texture self.image = argd.get("image", None) # For remote picture self.imageIO = None name = argd.get("name", "NoName") self.set_label(name) # For rotation and scaling self.drotation = Vector() self.scaling = Vector(*argd.get("scaling", (1, 1, 1))) # For detection of changes self.oldpos = self.initialpos = Vector() self.oldscaling = Vector() # For transformation matrix multiplication # Store all transformations self.transform = Transform() # Specially store link transformations because link doesn't do rotation and scaling self.linkTransform = Transform() # Store all previous transformations to be multiplied with the current one self.oldrotTransform = Transform() # For redraw detection self.needRedraw = True # For drag handling self.oldpoint = None def get_bgcolour(self): """Get bgcolour.""" return self._bgcolour def set_bgcolour(self, value): """Set bgcolour and bgcurcolour as well if it is not selected.""" self._bgcolour = value if not self.selected: self.bgcurcolour = value bgcolour = property(get_bgcolour, set_bgcolour, None, None) def get_fgcolour(self): """Get fgcolour.""" return self._fgcolour def set_fgcolour(self, value): """Set fgcolour and fgcurcolour as well if it is not selected.""" self._fgcolour = value if not self.selected: self.fgcurcolour = value fgcolour = property(get_fgcolour, set_fgcolour, None, None) def get_sidecolour(self): """Get sidecolour.""" return self._sidecolour def set_sidecolour(self, value): """Set sidecolour and sidecurcolour as well if it is not selected.""" self._sidecolour = value if not self.selected: self.sidecurcolour = value sidecolour = property(get_sidecolour, set_sidecolour, None, None) def get_bgcolourselected(self): """Get bgcolourselected.""" return self._bgcolourselected def set_bgcolourselected(self, value): """Set bgcolourselected and bgcurcolour as well if it is selected.""" self._bgcolourselected = value if self.selected: self.bgcurcolour = value bgcolourselected = property(get_bgcolourselected, set_bgcolourselected, None, None) def get_fgcolourselected(self): """Get fgcolourselected.""" return self._fgcolourselected def set_fgcolourselected(self, value): """Set fgcolourselected and fgcurcolour as well if it is selected.""" self._fgcolourselected = value if self.selected: self.fgcurcolour = value fgcolourselected = property(get_fgcolourselected, set_fgcolourselected, None, None) def get_sidecolourselected(self): """Get sidecolourselected.""" return self._sidecolourselected def set_sidecolourselected(self, value): """Set sidecolourselected and sidecurcolour as well if it is selected.""" self._sidecolourselected = value if self.selected: self.sidecurcolour = value sidecolourselected = property(get_sidecolourselected, set_sidecolourselected, None, None) def set_label(self, new_name): """Set text label.""" if self.initSize == Vector(): self.size = Vector() self.name = new_name self.buildCaption() # It's after buildCaption() because self.size is decided only after buildCaption() if size=(0,0,0) self.radius = self.size.length() / 2 def updateAttrs(self, **params): """Update attributes.""" for key, value in params.iteritems(): setattr(self, key, value) self.buildCaption() def draw(self): """\Stub method Override this method to draw concrete particles and links. """ pass def readURLFile(self): """Read a string buffer of an object denoted by a URL.""" fObject = urlopen(self.image) imageData = fObject.read() self.imageIO = StringIO(imageData) # Text label is not needed for picture texture self.set_label("Dummy") def buildCaption(self): """Pre-render the text to go on the label.""" # Text is rendered to self.image if self.image is not None: if self.imageIO is not None: self.imageSurface = pygame.image.load(self.imageIO).convert() self.imageIO = None elif os.path.exists(self.image): self.imageSurface = pygame.image.load(self.image).convert() # Image texture is used instead of label texture if 'image' argument is specified elif self.image.find("://") != -1: # Use text label for notification of waiting before the picture is available pygame.font.init() font = pygame.font.Font(None, self.fontsize) self.imageSurface = font.render("Loading image...", True, self.fgcurcolour) # Use thread to wrap urlopen in case urlopen is blocked import thread thread.start_new(self.readURLFile, ()) else: # Label texture is used if 'image' argument is not specified pygame.font.init() font = pygame.font.Font(None, self.fontsize) self.imageSurface = font.render(self.name, True, self.fgcurcolour) if self.size != Vector(0, 0, 0): texsize = (self.size.x * self.pixelscaling, self.size.y * self.pixelscaling) else: texsize = ( self.imageSurface.get_width() + 2 * self.margin, self.imageSurface.get_height() + 2 * self.margin, ) self.size = Vector( texsize[0] / float(self.pixelscaling), texsize[1] / float(self.pixelscaling), self.thickness ) # create power of 2 dimensioned surface pow2size = ( int(2 ** (math.ceil(math.log(texsize[0] + 2 * self.margin, 2)))), int(2 ** (math.ceil(math.log(texsize[1] + 2 * self.margin, 2)))), ) textureSurface = pygame.Surface(pow2size) textureSurface.fill(self.bgcurcolour) # determine texture coordinates self.tex_w = float(texsize[0]) / pow2size[0] self.tex_h = float(texsize[1]) / pow2size[1] # copy image data to pow2surface dest = ( max((texsize[0] - self.imageSurface.get_width()) / 2, 0), max((texsize[1] - self.imageSurface.get_height()) / 2, 0), ) textureSurface.blit(self.imageSurface, dest) textureSurface = textureSurface.convert_alpha() # read pixel data textureData = pygame.image.tostring(textureSurface, "RGBX", 1) self.texID = glGenTextures(1) # create texture glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, self.texID) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, textureSurface.get_width(), textureSurface.get_height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData, ) glDisable(GL_TEXTURE_2D) def applyTransforms(self): """Use the objects translation/rotation/scaling values to generate a new transformation Matrix if changes have happened.""" # generate new transformation matrix if needed if self.oldscaling != self.scaling or self.drotation != Vector() or self.oldpos != Vector(*self.pos): self.transform = Transform() self.linkTransform = Transform() drotationTransform = Transform() drotationTransform.applyRotation(self.drotation) self.transform.applyScaling(self.scaling) self.linkTransform.applyScaling(self.scaling) self.transform = self.transform * self.oldrotTransform * drotationTransform self.oldrotTransform = self.oldrotTransform * drotationTransform self.transform.applyTranslation(Vector(*self.pos)) self.linkTransform.applyTranslation(Vector(*self.pos)) if self.oldscaling != self.scaling: self.oldscaling = self.scaling.copy() self.drotation = Vector() if self.oldpos != Vector(*self.pos): self.oldpos = Vector(*self.pos) # send new transform to display service transform_update = {"TRANSFORM_UPDATE": True, "objectid": id(self), "transform": self.transform} return transform_update else: return None def select(self): """Tell this particle it is selected""" self.selected = True self.sidecurcolour = self.sidecolourselected self.bgcurcolour = self.bgcolourselected self.fgcurcolour = self.fgcolourselected self.buildCaption() def deselect(self): """Tell this particle it is deselected""" self.selected = False self.sidecurcolour = self.sidecolour self.bgcurcolour = self.bgcolour self.fgcurcolour = self.fgcolour self.buildCaption()
def handleMouseEvents(self, event): """Handle mouse events.""" if event.type == pygame.MOUSEBUTTONDOWN or pygame.MOUSEMOTION and self.grabbed: if not self.rotationMode: for particle in self.hitParticles: p1 = Vector(*particle.pos).copy() p1.x += 10 p2 = Vector(*particle.pos).copy() p2.y += 10 # Get the position of mouse z = Intersect.ray_Plane(Vector(0, 0, 0), event.direction, [ Vector(*particle.pos) - Vector(0, 0, self.display.viewerposition.z), p1 - Vector(0, 0, self.display.viewerposition.z), p2 - Vector(0, 0, self.display.viewerposition.z) ]) newpoint = event.direction * z if event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: # Handle double click clickPos = event.pos currentTime = time.time() elapsedTime = currentTime - self.lastClickTime # If it's a double-click if clickPos == self.lastClickPos and elapsedTime < self.dClickRes: self.gotoDisplayLevel(1) else: # Single click if not self.rotationMode: # Select particle for particle in self.currentDisplayedPhysics.particles: if particle.identifier in event.hitobjects: self.grabbed = True self.hitParticles.append(particle) self.selectParticle(particle) # If click places other than particles in non multiSelectMode, deselect all if not self.hitParticles and not self.multiSelectMode: self.deselectAll() self.lastClickPos = clickPos self.lastClickTime = currentTime elif event.button == 3: # Right-clicked self.gotoDisplayLevel(-1) elif event.button == 4: # Scrolled-up: zoom out if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles for particle in particles: posVector = Vector(*particle.pos) posVector.z -= 1 particle.pos = posVector.toTuple() elif event.button == 5: # Scrolled-down: zoom in if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles for particle in particles: posVector = Vector(*particle.pos) posVector.z += 1 particle.pos = posVector.toTuple() if event.type == pygame.MOUSEBUTTONUP: if event.button == 1: for particle in self.hitParticles: self.grabbed = False particle.oldpoint = None self.hitParticles.pop(self.hitParticles.index(particle)) if event.type == pygame.MOUSEMOTION: if not self.rotationMode and self.grabbed: # Drag particles for particle in self.hitParticles: try: if particle.oldpoint is not None: diff = newpoint - particle.oldpoint amount = (diff.x, diff.y) particle.pos = (Vector(*particle.pos) + Vector(*amount)).toTuple() except NameError: pass # Redraw the link so that the link can move with the particle for p in particle.bondedFrom: p.needRedraw = True elif self.rotationMode: # Rotate particles dAnglex = float(event.rel[1]) dAngley = -float(event.rel[0]) self.rotateParticles(self.selectedParticles, (dAnglex, dAngley, 0)) try: for particle in self.hitParticles: particle.oldpoint = newpoint except NameError: pass
class CheckersInteractor(Interactor): def __init__(self, **argd): super(CheckersInteractor, self).__init__(**argd) self.addInbox("position") self.addOutbox("movement") self.liftheight = argd.get("liftheight", 0.2) self.colour = argd.get("colour") self.grabbed = False self.position = None self.oldpoint = None self.lastValidPos = None if self.nolink == False: self.link( (self, "movement"), (self.target, "rel_position") ) self.link( (self.target, "position"), (self, "position") ) def setup(self): self.addListenEvents( [pygame.MOUSEMOTION, pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP ]) def handleEvents(self): while self.dataReady("events"): event = self.recv("events") if self.position is not None: if event.type == pygame.MOUSEBUTTONDOWN or pygame.MOUSEMOTION and self.grabbed: p1 = self.position.copy() p1.x += 10 p2 = self.position.copy() p2.y += 10 z = Intersect.ray_Plane(event.viewerposition, event.direction, [self.position, p1, p2]) newpoint = event.direction * z if event.type == pygame.MOUSEBUTTONDOWN and self.identifier in event.hitobjects: if event.button == 1: self.grabbed = True self.send((0,0,self.liftheight), "movement") if event.type == pygame.MOUSEBUTTONUP: if event.button == 1 and self.grabbed: self.grabbed = False # place piece in the middle of a black field alignedpos = self.position.copy() alignedpos.x = floor(alignedpos.x)+0.5 alignedpos.y = floor(alignedpos.y)+0.5 diff = alignedpos - self.position self.send((diff.x,diff.y,-self.liftheight), "movement") self.position = alignedpos fr = (floor(self.lastValidPos.x)+4, floor(self.lastValidPos.y)+4) to = (floor(alignedpos.x)+4, floor(alignedpos.y)+4 ) self.send( {"PLACEMENT":True, "from":fr, "to":to, "colour":self.colour, "objectid": id(self)}, "outbox") if event.type == pygame.MOUSEMOTION: if self.grabbed == True: if self.oldpoint is not None: diff = newpoint-self.oldpoint diff.z = 0 self.send(diff.toTuple(), "movement") try: self.oldpoint = newpoint except NameError: pass def frame(self): while self.dataReady("position"): self.position = Vector(*self.recv("position")) if self.lastValidPos is None: self.lastValidPos = self.position.copy() while self.dataReady("inbox"): msg = self.recv("inbox") if msg == "ACK": self.lastValidPos = self.position.copy() elif msg == "INVALID": diff = self.lastValidPos - self.position diff.z = 0 self.send(diff.toTuple(), "movement")
def __init__(self, screensize=(800, 600), fullscreen=False, caption="3D Topology Viewer", particleTypes=None, initialTopology=None, laws=None, simCyclesPerRedraw=1, border=0): """x.__init__(...) initializes x; see x.__class__.__doc__ for signature""" super(TopologyViewer3D, self).__init__() glutInit(sys.argv) tracker = _cat.coordinatingassistanttracker.getcat() try: self.display = tracker.retrieveService("ogl_display")[0] except KeyError: self.display = OpenGLDisplay(width=screensize[0], height=screensize[1], fullscreen=fullscreen, title=caption) self.display.activate() OpenGLDisplay.setDisplayService(self.display, tracker) self.display = OpenGLDisplay.getDisplayService()[0] self.link((self, "display_signal"), (self.display, "notify")) self.link((self.display, "signal"), (self, "control")) self.border = border if particleTypes == None: self.particleTypes = { "-": CuboidParticle3D, "cuboid": CuboidParticle3D, "sphere": SphereParticle3D, "teapot": TeapotParticle3D } else: self.particleTypes = particleTypes if initialTopology == None: initialTopology = ([], []) self.initialNodes = list(initialTopology[0]) self.initialBonds = list(initialTopology[1]) self.hitParticles = [] self.multiSelectMode = False self.selectedParticles = [] self.grabbed = False self.rotationMode = False if laws == None: self.laws = Kamaelia.Support.Particles.SimpleLaws(bondLength=2) else: self.laws = laws self.physics = ParticleSystem(self.laws, [], 0) self.biggestRadius = 0 # Do interaction self.simCyclesPerRedraw = simCyclesPerRedraw self.lastIdleTime = time.time() # Tell if new node is added; if true, new id needs adding to OpenGLDisplay list self.isNewNode = False # For hierarchy structure self.maxLevel = 0 self.currentLevel = 0 self.previousParentParticleID = self.currentParentParticleID = '' self.viewerOldPos = Vector() self.levelViewerPos = {} # The Physics particle system of current display level for display self.currentDisplayedPhysics = ParticleSystem(self.laws, [], 0) # For double click self.lastClickPos = (0, 0) self.lastClickTime = time.time() self.dClickRes = 0.3
class Particle3D(BaseParticle): """\ A super class for 3D particles """ def __init__(self, position = (-1,0,-10), ID='', **argd): super(Particle3D, self).__init__(position=position, ID = ID) self.pos = position self.initSize = Vector(*argd.get("size", (0,0,0))) self.backgroundColourWhenUnselected = self.backgroundColour = argd.get("bgcolour", (230,230,230)) self.foregroundColourWhenUnselected = self.foregroundColour = argd.get("fgcolour", (0,0,0)) self.sideColourWhenUnselected = self.sideColour = argd.get("sidecolour", (200,200,244)) self.backgroundColourWhenSelected = argd.get("bgcolourselected", (0,0,0)) self.foregroundColourWhenSelected = argd.get("fgcolourselected", (244,244,244)) self.sideColourWhenSelected = argd.get("sidecolourselected", (200,200,244)) self.margin = argd.get("margin", 8) self.fontsize = argd.get("fontsize", 50) self.pixelscaling = argd.get("pixelscaling", 100) self.thickness = argd.get("thickness", 0.3) # For picture texture self.pic = argd.get("image", None) name = argd.get("name","NoName") self.set_label(name) # For rotation and scaling self.drotation = Vector() self.scaling = Vector( *argd.get("scaling", (1,1,1) ) ) # For detection of changes self.oldpos = self.initialpos = Vector() self.oldscaling = Vector() # For transformation matrix multiplication self.transform = Transform() self.linkTransform = Transform() self.oldrotTransform = Transform() # For redraw detection self.needRedraw = True # For drag handling self.oldpoint = None def set_label(self, new_name): if self.initSize == Vector(): self.size = Vector() self.name = new_name self.buildCaption() # It's after buildCaption() because self.size is decided only after buildCaption() if size=(0,0,0) self.radius = self.size.length()/2 def draw(self): """\Stub method Override this method to draw particles and links. """ pass def buildCaption(self): """Pre-render the text to go on the label.""" # Text is rendered to self.image if self.pic is not None: if self.pic.find('://') != -1 and not os.path.exists(self.pic): """ FIXME: either use thread to wrap urlopen or kamaelia HTTP components in case urlopen is blocked """ fObject = urlopen(self.pic) picData = fObject.read() pic = StringIO(picData) else: pic = self.pic self.image = pygame.image.load(pic).convert() else: pygame.font.init() font = pygame.font.Font(None, self.fontsize) self.image = font.render(self.name,True, self.foregroundColour, ) if self.size != Vector(0,0,0): texsize = (self.size.x*self.pixelscaling, self.size.y*self.pixelscaling) else: texsize = ( self.image.get_width()+2*self.margin, self.image.get_height()+2*self.margin ) self.size=Vector(texsize[0]/float(self.pixelscaling), texsize[1]/float(self.pixelscaling), self.thickness) # create power of 2 dimensioned surface pow2size = (int(2**(math.ceil(math.log(texsize[0]+2*self.margin, 2)))), int(2**(math.ceil(math.log(texsize[1]+2*self.margin, 2))))) textureSurface = pygame.Surface(pow2size) textureSurface.fill( self.backgroundColour ) # determine texture coordinates self.tex_w = float(texsize[0])/pow2size[0] self.tex_h = float(texsize[1])/pow2size[1] # copy image data to pow2surface dest = ( max((texsize[0]-self.image.get_width())/2, 0), max((texsize[1]-self.image.get_height())/2, 0) ) textureSurface.blit(self.image, dest) # textureSurface.set_alpha(128) textureSurface = textureSurface.convert_alpha() # read pixel data textureData = pygame.image.tostring(textureSurface, "RGBX", 1) #print self.image.get_width(), self.image.get_height() #print textureSurface.get_width(), textureSurface.get_height() #print textureData self.texID = glGenTextures(1) # create texture glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, self.texID) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, textureSurface.get_width(), textureSurface.get_height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData ); glDisable(GL_TEXTURE_2D) def applyTransforms(self): """ Use the objects translation/rotation/scaling values to generate a new transformation Matrix if changes have happened. """ # generate new transformation matrix if needed if self.oldscaling != self.scaling or self.drotation != Vector() or self.oldpos != Vector(*self.pos): self.transform = Transform() self.linkTransform = Transform() drotationTransform = Transform() drotationTransform.applyRotation(self.drotation) self.transform.applyScaling(self.scaling) self.linkTransform.applyScaling(self.scaling) self.transform = self.transform*self.oldrotTransform*drotationTransform self.oldrotTransform = self.oldrotTransform*drotationTransform self.transform.applyTranslation(Vector(*self.pos)) self.linkTransform.applyTranslation(Vector(*self.pos)) if self.oldscaling != self.scaling: self.oldscaling = self.scaling.copy() self.drotation = Vector() if self.oldpos != Vector(*self.pos): self.oldpos = Vector(*self.pos) # send new transform to display service transform_update = { "TRANSFORM_UPDATE": True, "objectid": id(self), "transform": self.transform } return transform_update else: return None def select( self ): """Tell this particle it is selected""" #self.selected = True self.sideColour = self.sideColourWhenSelected self.backgroundColour = self.backgroundColourWhenSelected self.foregroundColour = self.foregroundColourWhenSelected self.buildCaption() def deselect( self ): """Tell this particle it is deselected""" #self.selected = False self.sideColour = self.sideColourWhenUnselected self.backgroundColour = self.backgroundColourWhenUnselected self.foregroundColour = self.foregroundColourWhenUnselected self.buildCaption()
def handleEvents(self): """ Handle events. """ while self.dataReady("events"): event = self.recv("events") if event.type == pygame.MOUSEBUTTONDOWN or pygame.MOUSEMOTION and self.grabbed: if not self.rotationMode: for particle in self.hitParticles: p1 = Vector(*particle.pos).copy() p1.x += 10 p2 = Vector(*particle.pos).copy() p2.y += 10 #z = Intersect.ray_Plane(Vector(0,0,0), event.direction, [Vector(*particle.pos)-self.display.viewerposition, p1, p2]) z = Intersect.ray_Plane(Vector(0,0,0), event.direction, [Vector(*particle.pos)-Vector(0,0,self.display.viewerposition.z), p1-Vector(0,0,self.display.viewerposition.z), p2-Vector(0,0,self.display.viewerposition.z)]) newpoint = event.direction * z if event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: # Handle double click clickPos = event.pos currentTime = time.time() elapsedTime = currentTime - self.lastClickTime if clickPos == self.lastClickPos and elapsedTime<self.dClickRes: if self.currentLevel < self.maxLevel and len(self.selectedParticles) == 1: hasChildParticles = False for particle in self.physics.particles: if particle.ID.find(self.selectedParticles[0].ID) == 0 and particle.ID != self.selectedParticles[0].ID: hasChildParticles = True break if hasChildParticles: self.currentParentParticleID = self.selectedParticles[0].ID self.gotoDisplayLevel(1) else: print 'Warning: The particle you double-clicked has no children!' else: if self.currentLevel == self.maxLevel: print "Warning: max hierarchy level has reached!" if len(self.selectedParticles) != 1: print "Tips: To extend a node, please double-click the node you want to extend" else: if not self.rotationMode: for particle in self.currentDisplayedPhysics.particles: if particle.identifier in event.hitobjects: #particle.oldpos = particle.oldpos - self.display.viewerposition self.grabbed = True #particle.scaling = Vector(0.9,0.9,0.9) self.hitParticles.append(particle) self.selectParticle(particle) #print str(id(particle))+'hit' #print self.hitParticles # If click places other than particles in non multiSelectMode, deselect all if not self.hitParticles and not self.multiSelectMode: self.deselectAll() self.lastClickPos = clickPos self.lastClickTime = currentTime if event.button == 3: if self.currentLevel > 0: items = self.currentParentParticleID.split(':') items.pop() self.currentParentParticleID = ':'.join(items) self.gotoDisplayLevel(-1) else: print "Warning: The first hierarchy level has reached!" if event.button == 4: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles for particle in particles: posVector = Vector(*particle.pos) posVector.z -= 1 particle.pos = posVector.toTuple() if event.button == 5: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles for particle in particles: posVector = Vector(*particle.pos) posVector.z += 1 particle.pos = posVector.toTuple() if event.type == pygame.MOUSEBUTTONUP: if event.button == 1: for particle in self.hitParticles: self.grabbed = False particle.oldpoint = None #particle.scaling = Vector(1,1,1) self.hitParticles.pop(self.hitParticles.index(particle)) #print self.hitParticles if event.type == pygame.MOUSEMOTION: if not self.rotationMode and self.grabbed: for particle in self.hitParticles: try: if particle.oldpoint is not None: #print particle.pos diff = newpoint-particle.oldpoint amount = (diff.x, diff.y) particle.pos = (Vector(*particle.pos)+Vector(*amount)).toTuple() except NameError: pass # Redraw the link so that the link can move with the particle for p in particle.bondedFrom: p.needRedraw = True elif self.rotationMode: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAnglex = float(event.rel[1])*math.pi/180 dAngley = -float(event.rel[0])*math.pi/180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z*relativePosVector.z+relativePosVector.y*relativePosVector.y)**0.5 newAnglex = (math.atan2(relativePosVector.z,relativePosVector.y)+dAnglex) particle.pos = (posVector.x, radius*math.cos(newAnglex)+centrePoint.y, radius*math.sin(newAnglex)+centrePoint.z) posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z*relativePosVector.z+relativePosVector.x*relativePosVector.x)**0.5 newAngley = (math.atan2(relativePosVector.z,relativePosVector.x)+dAngley) particle.pos = (radius*math.cos(newAngley)+centrePoint.x, posVector.y, radius*math.sin(newAngley)+centrePoint.z) particle.drotation.y = float(event.rel[0]) particle.drotation.x = float(event.rel[1]) particle.drotation %= 360 try: for particle in self.hitParticles: particle.oldpoint = newpoint except NameError: pass # Keyboard events handling if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: self.quit() elif event.key == pygame.K_BACKSPACE: if self.currentLevel > 0: items = self.currentParentParticleID.split(':') items.pop() self.currentParentParticleID = ':'.join(items) self.gotoDisplayLevel(-1) else: print "Warning: The first hierarchy level has reached!" elif event.key == pygame.K_RETURN: if self.currentLevel < self.maxLevel and len(self.selectedParticles) == 1: hasChildParticles = False for particle in self.physics.particles: if particle.ID.find(self.selectedParticles[0].ID) == 0 and particle.ID != self.selectedParticles[0].ID: hasChildParticles = True break if hasChildParticles: self.currentParentParticleID = self.selectedParticles[0].ID self.gotoDisplayLevel(1) else: print 'Warning: The particle you double-clicked has no children!' else: if self.currentLevel == self.maxLevel: print "Warning: max hierarchy level has reached!" if len(self.selectedParticles) != 1: print "Tips: To extend a node, please click to select the node (only one) you want to extend first." elif event.key == pygame.K_LSHIFT or event.key == pygame.K_RSHIFT: self.multiSelectMode = True elif event.key == pygame.K_LCTRL or event.key == pygame.K_RCTRL: self.rotationMode = True elif event.key == pygame.K_PAGEUP: self.display.viewerposition.z -= 0.5 elif event.key == pygame.K_PAGEDOWN: self.display.viewerposition.z += 0.5 elif event.key == pygame.K_w: self.display.viewerposition.y += 0.5 elif event.key == pygame.K_s: self.display.viewerposition.y -= 0.5 elif event.key == pygame.K_a: self.display.viewerposition.x -= 0.5 elif event.key == pygame.K_d: self.display.viewerposition.x += 0.5 elif event.key == pygame.K_UP: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = -20*math.pi/180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z*relativePosVector.z+relativePosVector.y*relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.z,relativePosVector.y)+dAngle) particle.pos = (posVector.x, radius*math.cos(newAngle)+centrePoint.y, radius*math.sin(newAngle)+centrePoint.z) particle.drotation = Vector(dAngle*180/math.pi,0,0) elif event.key == pygame.K_DOWN: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = 20*math.pi/180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z*relativePosVector.z+relativePosVector.y*relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.z,relativePosVector.y)+dAngle) particle.pos = (posVector.x, radius*math.cos(newAngle)+centrePoint.y, radius*math.sin(newAngle)+centrePoint.z) particle.drotation = Vector(dAngle*180/math.pi,0,0) elif event.key == pygame.K_LEFT: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = 20*math.pi/180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z*relativePosVector.z+relativePosVector.x*relativePosVector.x)**0.5 newAngle = (math.atan2(relativePosVector.z,relativePosVector.x)+dAngle) particle.pos = (radius*math.cos(newAngle)+centrePoint.x, posVector.y, radius*math.sin(newAngle)+centrePoint.z) particle.drotation = Vector(0,-dAngle*180/math.pi,0) elif event.key == pygame.K_RIGHT: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = -20*math.pi/180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z*relativePosVector.z+relativePosVector.x*relativePosVector.x)**0.5 newAngle = (math.atan2(relativePosVector.z,relativePosVector.x)+dAngle) particle.pos = (radius*math.cos(newAngle)+centrePoint.x, posVector.y, radius*math.sin(newAngle)+centrePoint.z) particle.drotation = Vector(0,-dAngle*180/math.pi,0) elif event.key == pygame.K_COMMA: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = 20*math.pi/180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.x*relativePosVector.x+relativePosVector.y*relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.y,relativePosVector.x)+dAngle) particle.pos = (radius*math.cos(newAngle)+centrePoint.x, radius*math.sin(newAngle)+centrePoint.y, posVector.z) particle.drotation = Vector(0,0,dAngle*180/math.pi) elif event.key == pygame.K_PERIOD: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = -20*math.pi/180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.x*relativePosVector.x+relativePosVector.y*relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.y,relativePosVector.x)+dAngle) particle.pos = (radius*math.cos(newAngle)+centrePoint.x, radius*math.sin(newAngle)+centrePoint.y, posVector.z) particle.drotation = Vector(0,0,dAngle*180/math.pi) #print self.display.viewerposition # Scroll if self.display.viewerposition changes if self.display.viewerposition.copy() != self.viewerOldPos: self.scroll() self.viewerOldPos = self.display.viewerposition.copy() # for particle in self.currentDisplayedPhysics.particles: # particle.oldpoint = None if event.type == pygame.KEYUP: if event.key == pygame.K_LSHIFT or event.key == pygame.K_RSHIFT: self.multiSelectMode = False elif event.key == pygame.K_LCTRL or event.key == pygame.K_RCTRL: self.rotationMode = False
def handleEvents(self): """ Handle events. """ while self.dataReady("events"): event = self.recv("events") if event.type == pygame.MOUSEBUTTONDOWN or pygame.MOUSEMOTION and self.grabbed: if not self.rotationMode: for particle in self.hitParticles: p1 = Vector(*particle.pos).copy() p1.x += 10 p2 = Vector(*particle.pos).copy() p2.y += 10 #z = Intersect.ray_Plane(Vector(0,0,0), event.direction, [Vector(*particle.pos)-self.display.viewerposition, p1, p2]) z = Intersect.ray_Plane(Vector( 0, 0, 0), event.direction, [ Vector(*particle.pos) - Vector(0, 0, self.display.viewerposition.z), p1 - Vector(0, 0, self.display.viewerposition.z), p2 - Vector(0, 0, self.display.viewerposition.z) ]) newpoint = event.direction * z if event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: # Handle double click clickPos = event.pos currentTime = time.time() elapsedTime = currentTime - self.lastClickTime if clickPos == self.lastClickPos and elapsedTime < self.dClickRes: if self.currentLevel < self.maxLevel and len( self.selectedParticles) == 1: hasChildParticles = False for particle in self.physics.particles: if particle.ID.find( self.selectedParticles[0].ID ) == 0 and particle.ID != self.selectedParticles[ 0].ID: hasChildParticles = True break if hasChildParticles: self.currentParentParticleID = self.selectedParticles[ 0].ID self.gotoDisplayLevel(1) else: print 'Warning: The particle you double-clicked has no children!' else: if self.currentLevel == self.maxLevel: print "Warning: max hierarchy level has reached!" if len(self.selectedParticles) != 1: print "Tips: To extend a node, please double-click the node you want to extend" else: if not self.rotationMode: for particle in self.currentDisplayedPhysics.particles: if particle.identifier in event.hitobjects: #particle.oldpos = particle.oldpos - self.display.viewerposition self.grabbed = True #particle.scaling = Vector(0.9,0.9,0.9) self.hitParticles.append(particle) self.selectParticle(particle) #print str(id(particle))+'hit' #print self.hitParticles # If click places other than particles in non multiSelectMode, deselect all if not self.hitParticles and not self.multiSelectMode: self.deselectAll() self.lastClickPos = clickPos self.lastClickTime = currentTime if event.button == 3: if self.currentLevel > 0: items = self.currentParentParticleID.split(':') items.pop() self.currentParentParticleID = ':'.join(items) self.gotoDisplayLevel(-1) else: print "Warning: The first hierarchy level has reached!" if event.button == 4: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles for particle in particles: posVector = Vector(*particle.pos) posVector.z -= 1 particle.pos = posVector.toTuple() if event.button == 5: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles for particle in particles: posVector = Vector(*particle.pos) posVector.z += 1 particle.pos = posVector.toTuple() if event.type == pygame.MOUSEBUTTONUP: if event.button == 1: for particle in self.hitParticles: self.grabbed = False particle.oldpoint = None #particle.scaling = Vector(1,1,1) self.hitParticles.pop( self.hitParticles.index(particle)) #print self.hitParticles if event.type == pygame.MOUSEMOTION: if not self.rotationMode and self.grabbed: for particle in self.hitParticles: try: if particle.oldpoint is not None: #print particle.pos diff = newpoint - particle.oldpoint amount = (diff.x, diff.y) particle.pos = (Vector(*particle.pos) + Vector(*amount)).toTuple() except NameError: pass # Redraw the link so that the link can move with the particle for p in particle.bondedFrom: p.needRedraw = True elif self.rotationMode: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAnglex = float(event.rel[1]) * math.pi / 180 dAngley = -float(event.rel[0]) * math.pi / 180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = ( relativePosVector.z * relativePosVector.z + relativePosVector.y * relativePosVector.y)**0.5 newAnglex = (math.atan2(relativePosVector.z, relativePosVector.y) + dAnglex) particle.pos = (posVector.x, radius * math.cos(newAnglex) + centrePoint.y, radius * math.sin(newAnglex) + centrePoint.z) posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = ( relativePosVector.z * relativePosVector.z + relativePosVector.x * relativePosVector.x)**0.5 newAngley = (math.atan2(relativePosVector.z, relativePosVector.x) + dAngley) particle.pos = (radius * math.cos(newAngley) + centrePoint.x, posVector.y, radius * math.sin(newAngley) + centrePoint.z) particle.drotation.y = float(event.rel[0]) particle.drotation.x = float(event.rel[1]) particle.drotation %= 360 try: for particle in self.hitParticles: particle.oldpoint = newpoint except NameError: pass # Keyboard events handling if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: self.quit() elif event.key == pygame.K_BACKSPACE: if self.currentLevel > 0: items = self.currentParentParticleID.split(':') items.pop() self.currentParentParticleID = ':'.join(items) self.gotoDisplayLevel(-1) else: print "Warning: The first hierarchy level has reached!" elif event.key == pygame.K_RETURN: if self.currentLevel < self.maxLevel and len( self.selectedParticles) == 1: hasChildParticles = False for particle in self.physics.particles: if particle.ID.find( self.selectedParticles[0].ID ) == 0 and particle.ID != self.selectedParticles[ 0].ID: hasChildParticles = True break if hasChildParticles: self.currentParentParticleID = self.selectedParticles[ 0].ID self.gotoDisplayLevel(1) else: print 'Warning: The particle you double-clicked has no children!' else: if self.currentLevel == self.maxLevel: print "Warning: max hierarchy level has reached!" if len(self.selectedParticles) != 1: print "Tips: To extend a node, please click to select the node (only one) you want to extend first." elif event.key == pygame.K_LSHIFT or event.key == pygame.K_RSHIFT: self.multiSelectMode = True elif event.key == pygame.K_LCTRL or event.key == pygame.K_RCTRL: self.rotationMode = True elif event.key == pygame.K_PAGEUP: self.display.viewerposition.z -= 0.5 elif event.key == pygame.K_PAGEDOWN: self.display.viewerposition.z += 0.5 elif event.key == pygame.K_w: self.display.viewerposition.y += 0.5 elif event.key == pygame.K_s: self.display.viewerposition.y -= 0.5 elif event.key == pygame.K_a: self.display.viewerposition.x -= 0.5 elif event.key == pygame.K_d: self.display.viewerposition.x += 0.5 elif event.key == pygame.K_UP: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = -20 * math.pi / 180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = ( relativePosVector.z * relativePosVector.z + relativePosVector.y * relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.z, relativePosVector.y) + dAngle) particle.pos = (posVector.x, radius * math.cos(newAngle) + centrePoint.y, radius * math.sin(newAngle) + centrePoint.z) particle.drotation = Vector(dAngle * 180 / math.pi, 0, 0) elif event.key == pygame.K_DOWN: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = 20 * math.pi / 180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = ( relativePosVector.z * relativePosVector.z + relativePosVector.y * relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.z, relativePosVector.y) + dAngle) particle.pos = (posVector.x, radius * math.cos(newAngle) + centrePoint.y, radius * math.sin(newAngle) + centrePoint.z) particle.drotation = Vector(dAngle * 180 / math.pi, 0, 0) elif event.key == pygame.K_LEFT: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = 20 * math.pi / 180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = ( relativePosVector.z * relativePosVector.z + relativePosVector.x * relativePosVector.x)**0.5 newAngle = (math.atan2(relativePosVector.z, relativePosVector.x) + dAngle) particle.pos = (radius * math.cos(newAngle) + centrePoint.x, posVector.y, radius * math.sin(newAngle) + centrePoint.z) particle.drotation = Vector(0, -dAngle * 180 / math.pi, 0) elif event.key == pygame.K_RIGHT: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = -20 * math.pi / 180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = ( relativePosVector.z * relativePosVector.z + relativePosVector.x * relativePosVector.x)**0.5 newAngle = (math.atan2(relativePosVector.z, relativePosVector.x) + dAngle) particle.pos = (radius * math.cos(newAngle) + centrePoint.x, posVector.y, radius * math.sin(newAngle) + centrePoint.z) particle.drotation = Vector(0, -dAngle * 180 / math.pi, 0) elif event.key == pygame.K_COMMA: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = 20 * math.pi / 180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = ( relativePosVector.x * relativePosVector.x + relativePosVector.y * relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.y, relativePosVector.x) + dAngle) particle.pos = (radius * math.cos(newAngle) + centrePoint.x, radius * math.sin(newAngle) + centrePoint.y, posVector.z) particle.drotation = Vector(0, 0, dAngle * 180 / math.pi) elif event.key == pygame.K_PERIOD: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = -20 * math.pi / 180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = ( relativePosVector.x * relativePosVector.x + relativePosVector.y * relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.y, relativePosVector.x) + dAngle) particle.pos = (radius * math.cos(newAngle) + centrePoint.x, radius * math.sin(newAngle) + centrePoint.y, posVector.z) particle.drotation = Vector(0, 0, dAngle * 180 / math.pi) #print self.display.viewerposition # Scroll if self.display.viewerposition changes if self.display.viewerposition.copy() != self.viewerOldPos: self.scroll() self.viewerOldPos = self.display.viewerposition.copy() # for particle in self.currentDisplayedPhysics.particles: # particle.oldpoint = None if event.type == pygame.KEYUP: if event.key == pygame.K_LSHIFT or event.key == pygame.K_RSHIFT: self.multiSelectMode = False elif event.key == pygame.K_LCTRL or event.key == pygame.K_RCTRL: self.rotationMode = False
def main(self): """\ """ # create display request for itself self.size = Vector(0, 0, 0) disprequest = { "OGL_DISPLAYREQUEST": True, "objectid": id(self), "callback": (self, "callback"), "events": (self, "events"), "size": self.size } # send display request self.send(disprequest, "display_signal") # wait for response on displayrequest and get identifier of the viewer while not self.dataReady("callback"): yield 1 self.identifier = self.recv("callback") self.addListenEvents([ pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP, pygame.MOUSEMOTION, pygame.KEYDOWN, pygame.KEYUP ]) pygame.key.set_repeat(100, 100) while True: # process incoming messages if self.dataReady("inbox"): message = self.recv("inbox") self.doCommand(message) #print message # wait for response on displayrequest and get identifier of the particle if self.isNewNode: while not self.dataReady("callback"): yield 1 self.physics.particles[-1].identifier = self.recv( "callback") self.isNewNode = False else: self.lastIdleTime = 0 yield 1 if self.lastIdleTime + 1.0 < time.time(): #print [particle.pos for particle in self.physics.particles] avoidedList = [] avoidedList.extend(self.hitParticles) avoidedList.extend(self.selectedParticles) #self.currentDisplayedPhysics.particleDict[ident].breakAllBonds() self.currentDisplayedPhysics.particles = [] if self.physics.particles != []: for particle in self.physics.particles: if self.currentParentParticleID == '': if ':' not in particle.ID: self.currentDisplayedPhysics.add(particle) particle.oldpos = particle.initialpos elif particle.ID.find( self.currentParentParticleID ) == 0 and particle.ID.count(':') == self.currentLevel: self.currentDisplayedPhysics.add(particle) particle.oldpos = particle.initialpos self.currentDisplayedPhysics.run(self.simCyclesPerRedraw, avoidedList=avoidedList) #print [particle.pos for particle in self.physics.particles] # Draw particles if new or updated for particle in self.currentDisplayedPhysics.particles: if particle.needRedraw: self.drawParticles(particle) #particle.needRedraw = False self.handleEvents() # Perform transformation for particle in self.currentDisplayedPhysics.particles: transform_update = particle.applyTransforms() if transform_update is not None: self.send(transform_update, "display_signal") #print transform_update #print [particle.pos for particle in self.physics.particles] self.lastIdleTime = time.time() else: yield 1 if self.dataReady("control"): msg = self.recv("control") if isinstance(msg, Axon.Ipc.shutdownMicroprocess): self.quit(msg)
class CheckersInteractor(Interactor): def __init__(self, **argd): super(CheckersInteractor, self).__init__(**argd) self.addInbox("position") self.addOutbox("movement") self.liftheight = argd.get("liftheight", 0.2) self.colour = argd.get("colour") self.grabbed = False self.position = None self.oldpoint = None self.lastValidPos = None if self.nolink == False: self.link((self, "movement"), (self.target, "rel_position")) self.link((self.target, "position"), (self, "position")) def setup(self): self.addListenEvents( [pygame.MOUSEMOTION, pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP]) def handleEvents(self): while self.dataReady("events"): event = self.recv("events") if self.position is not None: if event.type == pygame.MOUSEBUTTONDOWN or pygame.MOUSEMOTION and self.grabbed: p1 = self.position.copy() p1.x += 10 p2 = self.position.copy() p2.y += 10 z = Intersect.ray_Plane(event.viewerposition, event.direction, [self.position, p1, p2]) newpoint = event.direction * z if event.type == pygame.MOUSEBUTTONDOWN and self.identifier in event.hitobjects: if event.button == 1: self.grabbed = True self.send((0, 0, self.liftheight), "movement") if event.type == pygame.MOUSEBUTTONUP: if event.button == 1 and self.grabbed: self.grabbed = False # place piece in the middle of a black field alignedpos = self.position.copy() alignedpos.x = floor(alignedpos.x) + 0.5 alignedpos.y = floor(alignedpos.y) + 0.5 diff = alignedpos - self.position self.send((diff.x, diff.y, -self.liftheight), "movement") self.position = alignedpos fr = (floor(self.lastValidPos.x) + 4, floor(self.lastValidPos.y) + 4) to = (floor(alignedpos.x) + 4, floor(alignedpos.y) + 4) self.send( { "PLACEMENT": True, "from": fr, "to": to, "colour": self.colour, "objectid": id(self) }, "outbox") if event.type == pygame.MOUSEMOTION: if self.grabbed == True: if self.oldpoint is not None: diff = newpoint - self.oldpoint diff.z = 0 self.send(diff.toTuple(), "movement") try: self.oldpoint = newpoint except NameError: pass def frame(self): while self.dataReady("position"): self.position = Vector(*self.recv("position")) if self.lastValidPos is None: self.lastValidPos = self.position.copy() while self.dataReady("inbox"): msg = self.recv("inbox") if msg == "ACK": self.lastValidPos = self.position.copy() elif msg == "INVALID": diff = self.lastValidPos - self.position diff.z = 0 self.send(diff.toTuple(), "movement")
def draw(self): """Draw CUBOID Particle.""" hs = self.size / 2 # draw faces glBegin(GL_QUADS) glColor4f(self.sidecurcolour[0] / 256.0, self.sidecurcolour[1] / 256.0, self.sidecurcolour[2] / 256.0, 0.5) glVertex3f(hs.x, hs.y, hs.z) glVertex3f(hs.x, -hs.y, hs.z) glVertex3f(hs.x, -hs.y, -hs.z) glVertex3f(hs.x, hs.y, -hs.z) glVertex3f(-hs.x, hs.y, hs.z) glVertex3f(-hs.x, -hs.y, hs.z) glVertex3f(-hs.x, -hs.y, -hs.z) glVertex3f(-hs.x, hs.y, -hs.z) glVertex3f(hs.x, hs.y, hs.z) glVertex3f(-hs.x, hs.y, hs.z) glVertex3f(-hs.x, hs.y, -hs.z) glVertex3f(hs.x, hs.y, -hs.z) glVertex3f(hs.x, -hs.y, hs.z) glVertex3f(-hs.x, -hs.y, hs.z) glVertex3f(-hs.x, -hs.y, -hs.z) glVertex3f(hs.x, -hs.y, -hs.z) glEnd() glEnable(GL_TEXTURE_2D) glBindTexture(GL_TEXTURE_2D, self.texID) glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE) glBegin(GL_QUADS) # back plane glTexCoord2f(self.tex_w, 1.0 - self.tex_h) glVertex3f(hs.x, hs.y, -hs.z) glTexCoord2f(0.0, 1.0 - self.tex_h) glVertex3f(-hs.x, hs.y, -hs.z) glTexCoord2f(0.0, 1.0) glVertex3f(-hs.x, -hs.y, -hs.z) glTexCoord2f(self.tex_w, 1.0) glVertex3f(hs.x, -hs.y, -hs.z) # front plane glTexCoord2f(0.0, 1.0 - self.tex_h) glVertex3f(-hs.x, -hs.y, hs.z) glTexCoord2f(self.tex_w, 1.0 - self.tex_h) glVertex3f(hs.x, -hs.y, hs.z) glTexCoord2f(self.tex_w, 1.0) glVertex3f(hs.x, hs.y, hs.z) glTexCoord2f(0.0, 1.0) glVertex3f(-hs.x, hs.y, hs.z) glEnd() glDisable(GL_TEXTURE_2D) # Draw links glMatrixMode(GL_MODELVIEW) glPushMatrix() glLoadMatrixf(self.linkTransform.getMatrix()) for p in self.bondedTo: glBegin(GL_LINES) glVertex3f(*self.initialpos.toTuple()) glVertex3f(*(Vector(*p.pos) - Vector(*self.pos)).toTuple()) glEnd() glPopMatrix()
def __init__(self, **argd): """x.__init__(...) initializes x; see x.__class__.__doc__ for signature""" super(SphereParticle3D, self).__init__(**argd) self.drotation = Vector(0, 0, 90)
def main(self): """Main loop.""" # Make display request for event listening purpose self.size = Vector(0, 0, 0) disprequest = { "OGL_DISPLAYREQUEST": True, "objectid": id(self), "callback": (self, "callback"), "events": (self, "events"), "size": self.size } # send display request self.send(disprequest, "display_signal") # Wait for response on displayrequest and get identifier of the viewer while not self.dataReady("callback"): yield 1 self.identifier = self.recv("callback") self.initialiseComponent() while True: # Process incoming messages if self.dataReady("inbox"): message = self.recv("inbox") self.doCommand(message) # Wait for response on displayrequest and get identifier of the particle if self.isNewNode: while not self.dataReady("callback"): yield 1 self.physics.particles[-1].identifier = self.recv( "callback") self.isNewNode = False else: self.lastIdleTime = 0 yield 1 if self.lastIdleTime + 1.0 < time.time(): #Freeze selected particles so that they are not subject to the physics law for particle in self.selectedParticles: particle.freeze() # Do interaction between particles self.currentDisplayedPhysics.run(self.simCyclesPerRedraw) # Unfreeze selected particles for particle in self.selectedParticles: particle.unFreeze() # Draw particles if new or updated for particle in self.currentDisplayedPhysics.particles: if particle.needRedraw: self.drawParticles(particle) self.handleEvents() # Perform transformation for particle in self.currentDisplayedPhysics.particles: transform_update = particle.applyTransforms() if transform_update is not None: self.send(transform_update, "display_signal") self.lastIdleTime = time.time() else: yield 1 if self.dataReady("control"): msg = self.recv("control") if isinstance(msg, Axon.Ipc.shutdownMicroprocess): self.quit(msg)
def handleMouseEvents(self, event): """Handle mouse events.""" if event.type == pygame.MOUSEBUTTONDOWN or pygame.MOUSEMOTION and self.grabbed: if not self.rotationMode: for particle in self.hitParticles: p1 = Vector(*particle.pos).copy() p1.x += 10 p2 = Vector(*particle.pos).copy() p2.y += 10 # Get the position of mouse z = Intersect.ray_Plane(Vector(0,0,0), event.direction, [Vector(*particle.pos)-Vector(0,0,self.display.viewerposition.z), p1-Vector(0,0,self.display.viewerposition.z), p2-Vector(0,0,self.display.viewerposition.z)]) newpoint = event.direction * z if event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: # Handle double click clickPos = event.pos currentTime = time.time() elapsedTime = currentTime - self.lastClickTime # If it's a double-click if clickPos == self.lastClickPos and elapsedTime<self.dClickRes: self.gotoDisplayLevel(1) else: # Single click if not self.rotationMode: # Select particle for particle in self.currentDisplayedPhysics.particles: if particle.identifier in event.hitobjects: self.grabbed = True self.hitParticles.append(particle) self.selectParticle(particle) # If click places other than particles in non multiSelectMode, deselect all if not self.hitParticles and not self.multiSelectMode: self.deselectAll() self.lastClickPos = clickPos self.lastClickTime = currentTime elif event.button == 3: # Right-clicked self.gotoDisplayLevel(-1) elif event.button == 4: # Scrolled-up: zoom out if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles for particle in particles: posVector = Vector(*particle.pos) posVector.z -= 1 particle.pos = posVector.toTuple() elif event.button == 5: # Scrolled-down: zoom in if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles for particle in particles: posVector = Vector(*particle.pos) posVector.z += 1 particle.pos = posVector.toTuple() if event.type == pygame.MOUSEBUTTONUP: if event.button == 1: for particle in self.hitParticles: self.grabbed = False particle.oldpoint = None self.hitParticles.pop(self.hitParticles.index(particle)) if event.type == pygame.MOUSEMOTION: if not self.rotationMode and self.grabbed: # Drag particles for particle in self.hitParticles: try: if particle.oldpoint is not None: diff = newpoint-particle.oldpoint amount = (diff.x, diff.y) particle.pos = (Vector(*particle.pos)+Vector(*amount)).toTuple() except NameError: pass # Redraw the link so that the link can move with the particle for p in particle.bondedFrom: p.needRedraw = True elif self.rotationMode: # Rotate particles dAnglex = float(event.rel[1]) dAngley = -float(event.rel[0]) self.rotateParticles(self.selectedParticles, (dAnglex,dAngley,0)) try: for particle in self.hitParticles: particle.oldpoint = newpoint except NameError: pass
def handleEvents(self): while self.dataReady("events"): event = self.recv("events") if event.type == pygame.MOUSEBUTTONDOWN and self.identifier in event.hitobjects: self.rotation += Vector(0, 0, 10) self.rotation %= 360