# # Code under the MIT license by Alexander Pruss # # # mandelbulb.py [size [power [half]]] # # Options for half: south, north, east, west, up, down from mc import * import mcpi.settings as settings import cmath import time import sys from random import uniform # setting this to 0 makes for more isolated blocks because the connecting filigree was # missed; increasing slows down rendering AVOID_ISOLATES = 12 if not settings.isPE else 0 ESCAPE = 250 if len(sys.argv) < 2: size = 100 else: size = int(sys.argv[1]) if len(sys.argv) < 3: power = 8 else: power = int(sys.argv[2]) if power == 8: fractalSize = 2.16 else: fractalSize = 4. if len(sys.argv) < 4: half = None else: half = sys.argv[3].lower()[0] palette = list(reversed([WOOL_WHITE,HARDENED_CLAY_STAINED_WHITE,WOOL_PINK,WOOL_LIGHT_GRAY,WOOL_LIGHT_BLUE,WOOL_MAGENTA,WOOL_PURPLE,HARDENED_CLAY_STAINED_LIGHT_BLUE,HARDENED_CLAY_STAINED_LIGHT_GRAY,HARDENED_CLAY_STAINED_MAGENTA,HARDENED_CLAY_STAINED_PINK,HARDENED_CLAY_STAINED_RED,WOOL_RED,REDSTONE_BLOCK,HARDENED_CLAY_STAINED_ORANGE,WOOL_ORANGE,HARDENED_CLAY_STAINED_YELLOW,WOOL_YELLOW,WOOL_LIME,HARDENED_CLAY_STAINED_LIME,HARDENED_CLAY_STAINED_PURPLE,HARDENED_CLAY_STAINED_CYAN,WOOL_CYAN,WOOL_BLUE,HARDENED_CLAY_STAINED_BLUE,WOOL_GRAY,HARDENED_CLAY_STAINED_GREEN,WOOL_GREEN,HARDENED_CLAY_STAINED_BROWN,WOOL_BROWN,HARDENED_CLAY_STAINED_GRAY,WOOL_BLACK])); def positions(pos,scale): yield pos for i in range(AVOID_ISOLATES): yield (uniform(pos[0]-0.5*scale,pos[0]+0.5*scale), uniform(pos[1]-0.5*scale,pos[1]+0.5*scale), uniform(pos[2]-0.5*scale,pos[2]+0.5*scale)) def calculate0(pos): x,z,y = pos[0],pos[1],pos[2] cx,cy,cz = x,y,z i = 0 try: wentOut = 0 while i 2: return -1 if r > wentOut: wentOut = r theta = acos(z/r) phi = atan2(y,x) zr = r**power theta *= power phi *= power x = cx+zr*sin(theta)*cos(phi) y = cy+zr*sin(phi)*sin(theta) z = cz+zr*cos(theta) i = i + 1 return wentOut except: return 0 def calculate(pos0,scale): for pos in positions(pos0,scale): r = calculate0(pos) if r >= 0: return r return r # # we could of course just do for x in range(0,size): for y in range(0,size): yield(x,y) # but it will make users happier if we start at the player # def pollZoom(): global lastHitEvent,lastHitPos events = mc.events.pollBlockHits() if len(events) == 0: return lastHitEvent != None lastHitEvent = events[-1] lastHitPos = mc.player.getPos() return True def toBulb(centerMC,centerBulb,scale,x,y,z): return ((x - centerMC.x) * scale + centerBulb[0], (y - centerMC.y) * scale + centerBulb[1], (z - centerMC.z) * scale + centerBulb[2]) def draw(): count = 0 rangeX = range(cornerMC.x, cornerMC.x+size) rangeY = range(cornerMC.y, cornerMC.y+size) rangeZ = range(cornerMC.z, cornerMC.z+size) if not half is None: if half == 'w': rangeX = range(cornerMC.x, cornerMC.x+size/2) elif half == 'e': rangeX = range(cornerMC.x+size/2, cornerMC.x+size) elif half == 'n': rangeZ = range(cornerMC.z, cornerMC.z+size/2) elif half == 's': rangeZ = range(cornerMC.z+size/2, cornerMC.z+size) elif half == 'u': rangeY = range(cornerMC.y+size/2, cornerMC.y+size) elif half == 'd': rangeY = range(cornerMC.y, cornerMC.y+size/2) for mcX in rangeX: for mcY in rangeY: for mcZ in rangeZ: radius = calculate(toBulb(centerMC,centerBulb,scale,mcX,mcY,mcZ),scale) if radius < 0: mc.setBlock(mcX,mcY,mcZ,AIR) else: i = int(len(palette) / (fractalSize/2) * radius) if i >= len(palette): i = len(palette) - 1 mc.setBlock(mcX,mcY,mcZ,palette[i]) if pollZoom(): return mc.postToChat("Rendered") if __name__=='__main__': mc = Minecraft() startPos = mc.player.getTilePos() cornerMC = startPos + Vec3(1,0,1) centerMC = cornerMC + Vec3(size/2,size/2,size/2) centerBulb = (0,0,0) initial = True scale = fractalSize / size lastHitEvent = None while True: mc.player.setPos(startPos) mc.postToChat("Scale: "+str(fractalSize/size/scale)) draw() if not initial: mc.player.setPos(centerMC) while not pollZoom(): time.sleep(0.25) if ( lastHitEvent.pos.x < cornerMC.x or lastHitEvent.pos.x >= cornerMC.x + size or lastHitEvent.pos.y < cornerMC.y or lastHitEvent.pos.y >= cornerMC.y + size or lastHitEvent.pos.z < cornerMC.z or lastHitEvent.pos.z >= cornerMC.z + size ): mc.postToChat("resetting") centerBulb = (0,0,0) scale = fractalSize / size initial = True else: mc.postToChat("zooming") centerBulb = toBulb(centerMC,centerBulb,scale,lastHitPos.x,lastHitPos.y,lastHitPos.z) scale /= 8 initial = False lastHitEvent = None