''' 1. Create an image in paint or something with aspect ratio 4:3. 2. Make a billboard with it using: File > Make Billboard... in Alice. 3. Move and resize it so it covers the whole screen. 4. Place the script "main()" in world.my_first_method. If the name starts with a number, place an underscore before it. 5. Create a new world method, handleMouseClick 6. Create a "When mouse is clicked on" event with the billboard and world.handleMouseClick 7. In the handleMouseClick method, call the script "handleMouseClick()". 8 (Optional). Set the ambientLightColor world variable to "no color" to have white cells instead of grey. ''' # apparently nested functions can't see outside variables without this, silly jython from __future__ import nested_scopes # core classes from edu.cmu.cs.stage3.alice.core import * from edu.cmu.cs.stage3.alice.core.event import * from edu.cmu.cs.stage3.alice.core.response import * from edu.cmu.cs.stage3.alice.core.property import * from edu.cmu.cs.stage3.alice.core.question import * # constants from edu.cmu.cs.stage3.alice.core.Direction import * from edu.cmu.cs.stage3.alice.core.style.TraditionalAnimationStyle import * # image from java.awt import * from java.awt.image import * from java.awt.geom import * # for profiling import time # misc from edu.cmu.cs.stage3.alice.authoringtool import * from edu.cmu.cs.stage3.alice.authoringtool.util import * def main(screen): def add(key, value): globals()[key] = value # check if vars already defined if not globals().get('dims'): # the Billboard object that represents the screen add('screenObj', screen) # holds all pixel dimensions add('dims', Generic()) add('screenTexture', TextureMap()) # the actual Component of the screen add('screenComponent', world.camera.renderTarget.getAWTComponent()) # set dimension constants _size = screenComponent.getSize() dims.WIDTH = _size.width dims.HEIGHT = _size.height # make a square grid dims.GRID_WIDTH = dims.HEIGHT # get as close to 20 px per cell as possible, is a float dims.CELL_WIDTH = dims.GRID_WIDTH / round(dims.GRID_WIDTH / 20) dims.MENU_WIDTH = dims.WIDTH - dims.GRID_WIDTH # store images makeImage('main', dims.WIDTH, dims.HEIGHT) makeGrids() makeImage('menu', dims.MENU_WIDTH, dims.HEIGHT) # initialize grids for gridX in xrange(int(dims.GRID_WIDTH / dims.CELL_WIDTH)): for gridY in xrange(int(dims.HEIGHT / dims.CELL_WIDTH)): changeCell(gridX, gridY, OFF) changeCell(gridX, gridY, OFF, false) updateGrid() ### CLASSES class Generic: # TODO: find a better way to have dicts that work with . instead of [] pass ### FUNCTIONS # images images = {} def makeImage(name, w, h): ''' adds an (empty BufferedImage, its Graphics2D) tuple to images if doesn't already exist ''' if name not in images.keys(): image = AuthoringTool.getHack().getJAliceFrame().getGraphicsConfiguration().createCompatibleImage(w, h) modifiable = image.createGraphics() images[name] = (image, modifiable) def getImage(name): return images[name][0] def getModifiable(name): return images[name][1] # textures def setTexture(object, image): ''' takes java.awt.Image, makes it texture of object 0 - 0.001 ''' # these two lines prevent memory leaks, probably -_- object.getSceneGraphAppearance().getDiffuseColorMap().setImage(None) object.getSceneGraphAppearance().getDiffuseColorMap().setBonus(None) # must remove image to set it again screenTexture.propertyChanged(PropertyEvent(screenTexture.image, None)) screenTexture.propertyChanged(PropertyEvent(screenTexture.image, image)) object.propertyChanged(PropertyEvent(object._diffuseColorMap, screenTexture)) # grid def makeGrids(): makeImage('grid0', dims.GRID_WIDTH, dims.HEIGHT) makeImage('grid1', dims.GRID_WIDTH, dims.HEIGHT) curGrid = 0 def getCurGridName(): return 'grid' + str(curGrid) def getOtherGridName(): return 'grid' + str(1 - curGrid) def switchGrids(): global curGrid curGrid = 1 - curGrid def isOn(gridX, gridY, isCurGrid = true): ''' 0.0000333 over 10000 runs ''' if isCurGrid: name = getCurGridName() else: name = getOtherGridName() x, y = gridX * dims.CELL_WIDTH, gridY * dims.CELL_WIDTH return getImage(name).getRGB(int(x + dims.CELL_WIDTH / 2), int(y + dims.CELL_WIDTH / 2)) == Color.BLACK.getRGB() TOGGLE = 0 ON = 1 OFF = 2 def changeCell(gridX, gridY, setting = TOGGLE, isCurGrid = true): ''' 0 - 0.001, 0.000071 over 10000 runs ''' if isCurGrid: name = getCurGridName() else: name = getOtherGridName() x, y = gridX * dims.CELL_WIDTH, gridY * dims.CELL_WIDTH rect = Rectangle2D.Double(x, y, dims.CELL_WIDTH, dims.CELL_WIDTH) g2d = getModifiable(name) if setting == ON: g2d.setPaint(Color.BLACK) elif setting == OFF: g2d.setPaint(Color.WHITE) else: if isOn(gridX, gridY, isCurGrid): g2d.setPaint(Color.WHITE) else: g2d.setPaint(Color.BLACK) g2d.fill(rect) g2d.setPaint(Color.BLACK) g2d.draw(rect) def updateGrid(): ''' blit current grid to main image, then update it as the texture 0.001 - 0.002 ''' g2d = getModifiable('main') g2d.drawImage(getImage(getCurGridName()), None, 0, 0) setTexture(screenObj, getImage('main')) def runOneStep(): start = time.time() gridW = int(dims.GRID_WIDTH / dims.CELL_WIDTH) gridH = int(dims.HEIGHT / dims.CELL_WIDTH) pxArray = getImage(getCurGridName()).getRaster().getDataBuffer().getData() def _isOn(gridX, gridY): x, y = gridX * dims.CELL_WIDTH, gridY * dims.CELL_WIDTH return pxArray[int(x + dims.CELL_WIDTH / 2) + int(y + dims.CELL_WIDTH / 2) * dims.GRID_WIDTH] == Color.BLACK.getRGB() for gridX in xrange(gridW): for gridY in xrange(gridH): count = 0 curIsOn = _isOn(gridX, gridY) if curIsOn: for offX in xrange(-1, 2): for offY in xrange(-1, 2): if offX or offY: if _isOn((gridX + offX) % gridW, (gridY + offY) % gridH): count += 1 if count == 4: break if count == 4: break if count <= 1 or count == 4: changeCell(gridX, gridY, OFF, false) else: changeCell(gridX, gridY, ON, false) else: for offX in xrange(-1, 2): for offY in xrange(-1, 2): if offX or offY: if _isOn((gridX + offX) % gridW, (gridY + offY) % gridH): count += 1 if count == 4: break if count == 4: break if count == 3: changeCell(gridX, gridY, ON, false) else: changeCell(gridX, gridY, OFF, false) switchGrids() updateGrid() print time.time() - start # mouse def handleMouseClick(): pos = screenComponent.getMousePosition() changeCell(int(pos.x / dims.CELL_WIDTH), int(pos.y / dims.CELL_WIDTH)) updateGrid() # misc def onScreen(obj): world.bool = camera.canSee(obj, false)