175 lines
5.4 KiB
Python
175 lines
5.4 KiB
Python
#
|
|
# 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<ESCAPE:
|
|
r = sqrt(x*x+y*y+z*z)
|
|
if r > 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
|