# # Code under the MIT license by Alexander Pruss # import mcpi.minecraft as minecraft import mcpi.block as block from mcpi.block import * from mcpi.entity import * from math import * from numbers import Number,Integral class V3(tuple): def __new__(cls,*args): if len(args) == 1: return tuple.__new__(cls,tuple(*args)) else: return tuple.__new__(cls,args) def dot(self,other): return self[0]*other[0]+self[1]*other[1]+self[2]*other[2] @property def x(self): return self[0] @property def y(self): return self[1] @property def z(self): return self[2] def __add__(self,other): other = tuple(other) return V3(self[0]+other[0],self[1]+other[1],self[2]+other[2]) def __radd__(self,other): other = tuple(other) return V3(self[0]+other[0],self[1]+other[1],self[2]+other[2]) def __sub__(self,other): other = tuple(other) return V3(self[0]-other[0],self[1]-other[1],self[2]-other[2]) def __rsub__(self,other): other = tuple(other) return V3(other[0]-self[0],other[1]-self[1],other[2]-self[2]) def __neg__(self): return V3(-self[0],-self[1],-self[2]) def __pos__(self): return self def len2(self): return self[0]*self[0]+self[1]*self[1]+self[2]*self[2] def __abs__(self): return sqrt(self.len2()) def __div__(self,other): if isinstance(other,Number): y = float(other) return V3(self[0]/y,self[1]/y,self[2]/y) else: return NotImplemented def __mul__(self,other): if isinstance(other,Number): return V3(self[0]*other,self[1]*other,self[2]*other) else: other = tuple(other) # cross product return V3(self[1]*other[2]-self[2]*other[1],self[2]*other[0]-self[0]*other[2],self[0]*other[1]-self[1]*other[0]) def __rmul__(self,other): return self.__mul__(other) def __repr__(self): return "V3"+repr(tuple(self)) def ifloor(self): return V3(int(floor(self[0])),int(floor(self[1])),int(floor(self[2]))) def iceil(self): return V3(int(ceil(self[0])),int(ceil(self[1])),int(ceil(self[2]))) TO_RADIANS = pi / 180. TO_DEGREES = 180. / pi ICOS = [1,0,-1,0] ISIN = [0,1,0,-1] def makeMatrix(compass,vertical,roll): m0 = matrixMultiply(yawMatrix(compass), pitchMatrix(vertical)) return matrixMultiply(m0, rollMatrix(roll)) def applyMatrix(m,v): if m is None: return v else: return V3(m[i][0]*v[0]+m[i][1]*v[1]+m[i][2]*v[2] for i in range(3)) def matrixDistanceSquared(m1,m2): d2 = 0. for i in range(3): for j in range(3): d2 += (m1[i][j]-m2[i][j])**2 return d2 def iatan2(y,x): """One coordinate must be zero""" if x == 0: return 90 if y > 0 else -90 else: return 0 if x > 0 else 180 def icos(angleDegrees): """Calculate a cosine of an angle that must be a multiple of 90 degrees""" return ICOS[(angleDegrees % 360) // 90] def isin(angleDegrees): """Calculate a sine of an angle that must be a multiple of 90 degrees""" return ISIN[(angleDegrees % 360) // 90] def matrixMultiply(a,b): c = [[0,0,0],[0,0,0],[0,0,0]] for i in range(3): for j in range(3): c[i][j] = a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j] return c def yawMatrix(angleDegrees): if isinstance(angleDegrees, Integral) and angleDegrees % 90 == 0: return [[icos(angleDegrees), 0, -isin(angleDegrees)], [0, 1, 0], [isin(angleDegrees), 0, icos(angleDegrees)]] else: theta = angleDegrees * TO_RADIANS return [[cos(theta), 0., -sin(theta)], [0., 1., 0.], [sin(theta), 0., cos(theta)]] def rollMatrix(angleDegrees): if isinstance(angleDegrees, Integral) and angleDegrees % 90 == 0: return [[icos(angleDegrees), -isin(angleDegrees), 0], [isin(angleDegrees), icos(angleDegrees),0], [0, 0, 1]] else: theta = angleDegrees * TO_RADIANS return [[cos(theta), -sin(theta), 0.], [sin(theta), cos(theta),0.], [0., 0., 1.]] def pitchMatrix(angleDegrees): if isinstance(angleDegrees, Integral) and angleDegrees % 90 == 0: return [[1, 0, 0], [0, icos(angleDegrees),isin(angleDegrees)], [0, -isin(angleDegrees),icos(angleDegrees)]] else: theta = angleDegrees * TO_RADIANS return [[1., 0., 0.], [0., cos(theta),sin(theta)], [0., -sin(theta),cos(theta)]] def get2DTriangle(a,b,c): """get the points of the 2D triangle""" minX = {} maxX = {} for line in (traverse2D(a,b), traverse2D(b,c), traverse2D(a,c)): for p in line: minX0 = minX.get(p[1]) if minX0 == None: minX[p[1]] = p[0] maxX[p[1]] = p[0] yield(p) elif p[0] < minX0: for x in xrange(p[0],minX0): yield(x,p[1]) minX[p[1]] = p[0] else: maxX0 = maxX[p[1]] if maxX0 < p[0]: for x in range(maxX0,p[0]): yield(x,p[1]) maxX[p[1]] = p[0] def getFace(vertices): if len(vertices) < 1: raise StopIteration if len(vertices) <= 2: for p in traverse(V3(vertices[0]), V3(vertices[1])): yield p v0 = V3(vertices[0]) for i in range(2,len(vertices)): for p in traverse(V3(vertices[i-1]), V3(vertices[i])): for q in traverse(p, v0): yield q def getTriangle(p1, p2, p3): """ Note that this will return many duplicate poitns """ p1,p2,p3 = V3(p1),V3(p2),V3(p3) for u in traverse(p2,p3): for w in traverse(p1,u): yield w def frac(x): return x - floor(x) def traverse2D(a,b): """ equation of line: a + t(b-a), t from 0 to 1 Based on Amantides and Woo's ray traversal algorithm with some help from http://stackoverflow.com/questions/12367071/how-do-i-initialize-the-t-variables-in-a-fast-voxel-traversal-algorithm-for-ray """ inf = float("inf") if b[0] == a[0]: if b[1] == a[1]: yield (int(floor(a[0])),int(floor(a[1]))) return tMaxX = inf tDeltaX = 0 else: tDeltaX = 1./abs(b[0]-a[0]) tMaxX = tDeltaX * (1. - frac(a[0])) if b[1] == a[1]: tMaxY = inf tDeltaY = 0 else: tDeltaY = 1./abs(b[1]-a[1]) tMaxY = tDeltaY * (1. - frac(a[1])) endX = int(floor(b[0])) endY = int(floor(b[1])) x = int(floor(a[0])) y = int(floor(a[1])) if x <= endX: stepX = 1 else: stepX = -1 if y <= endY: stepY = 1 else: stepY = -1 yield (x,y) if x == endX: if y == endY: return tMaxX = inf if y == endY: tMaxY = inf while True: if tMaxX < tMaxY: x += stepX yield (x,y) if x == endX: tMaxX = inf else: tMaxX += tDeltaX else: y += stepY yield (x,y) if y == endY: tMaxY = inf else: tMaxY += tDeltaY if tMaxX == inf and tMaxY == inf: return def traverse(a,b): """ equation of line: a + t(b-a), t from 0 to 1 Based on Amantides and Woo's ray traversal algorithm with some help from http://stackoverflow.com/questions/12367071/how-do-i-initialize-the-t-variables-in-a-fast-voxel-traversal-algorithm-for-ray """ inf = float("inf") if b.x == a.x: if b.y == a.y and b.z == a.z: yield a.ifloor() return tMaxX = inf tDeltaX = 0 else: tDeltaX = 1./abs(b.x-a.x) tMaxX = tDeltaX * (1. - frac(a.x)) if b.y == a.y: tMaxY = inf tDeltaY = 0 else: tDeltaY = 1./abs(b.y-a.y) tMaxY = tDeltaY * (1. - frac(a.y)) if b.z == a.z: tMaxZ = inf tDeltaZ = 0 else: tDeltaZ = 1./abs(b.z-a.z) tMaxZ = tDeltaZ * (1. - frac(a.z)) end = b.ifloor() x = int(floor(a.x)) y = int(floor(a.y)) z = int(floor(a.z)) if x <= end.x: stepX = 1 else: stepX = -1 if y <= end.y: stepY = 1 else: stepY = -1 if z <= end.z: stepZ = 1 else: stepZ = -1 yield V3(x,y,z) if x == end.x: if y == end.y and z == end.z: return tMaxX = inf if y == end.y: tMaxY = inf if z == end.z: tMaxZ = inf while True: if tMaxX < tMaxY: if tMaxX < tMaxZ: x += stepX yield V3(x,y,z) if x == end.x: tMaxX = inf else: tMaxX += tDeltaX else: z += stepZ yield V3(x,y,z) if z == end.z: tMaxZ = inf else: tMaxZ += tDeltaZ else: if tMaxY < tMaxZ: y += stepY yield V3(x,y,z) if y == end.y: tMaxY = inf else: tMaxY += tDeltaY else: z += stepZ yield V3(x,y,z) if z == end.z: tMaxZ = inf else: tMaxZ += tDeltaZ if tMaxX == inf and tMaxY == inf and tMaxZ == inf: return # Brasenham's algorithm def getLine(x1, y1, z1, x2, y2, z2): line = [] x1 = int(floor(x1)) y1 = int(floor(y1)) z1 = int(floor(z1)) x2 = int(floor(x2)) y2 = int(floor(y2)) z2 = int(floor(z2)) point = [x1,y1,z1] dx = x2 - x1 dy = y2 - y1 dz = z2 - z1 x_inc = -1 if dx < 0 else 1 l = abs(dx) y_inc = -1 if dy < 0 else 1 m = abs(dy) z_inc = -1 if dz < 0 else 1 n = abs(dz) dx2 = l << 1 dy2 = m << 1 dz2 = n << 1 if l >= m and l >= n: err_1 = dy2 - l err_2 = dz2 - l for i in range(0,l-1): line.append(V3(point[0],point[1],point[2])) if err_1 > 0: point[1] += y_inc err_1 -= dx2 if err_2 > 0: point[2] += z_inc err_2 -= dx2 err_1 += dy2 err_2 += dz2 point[0] += x_inc elif m >= l and m >= n: err_1 = dx2 - m; err_2 = dz2 - m; for i in range(0,m-1): line.append(V3(point[0],point[1],point[2])) if err_1 > 0: point[0] += x_inc err_1 -= dy2 if err_2 > 0: point[2] += z_inc err_2 -= dy2 err_1 += dx2 err_2 += dz2 point[1] += y_inc else: err_1 = dy2 - n; err_2 = dx2 - n; for i in range(0, n-1): line.append(V3(point[0],point[1],point[2])) if err_1 > 0: point[1] += y_inc err_1 -= dz2 if err_2 > 0: point[0] += x_inc err_2 -= dz2 err_1 += dy2 err_2 += dx2 point[2] += z_inc line.append(V3(point[0],point[1],point[2])) if point[0] != x2 or point[1] != y2 or point[2] != z2: line.append(V3(x2,y2,z2)) return line class Drawing: TO_RADIANS = pi / 180. TO_DEGREES = 180. / pi def __init__(self,mc=None): if mc: self.mc = mc else: self.mc = minecraft.Minecraft() self.width = 1 self.nib = [(0,0,0)] def penwidth(self,w): self.width = int(w) if self.width == 0: self.nib = [] elif self.width == 1: self.nib = [(0,0,0)] elif self.width == 2: self.nib = [] for x in range(-1,1): for y in range(0,2): for z in range(-1,1): self.nib.append((x,y,z)) else: self.nib = [] r2 = self.width * self.width / 4. for x in range(-self.width//2 - 1,self.width//2 + 1): for y in range(-self.width//2 - 1, self.width//2 + 1): for z in range(-self.width//2 -1, self.width//2 + 1): if x*x + y*y + z*z <= r2: self.nib.append((x,y,z)) def point(self, x, y, z, block): for p in self.nib: self.mc.setBlock(x+p[0],y+p[1],z+p[2],block) def face(self, vertices, block): self.drawPoints(getFace(vertices), block) def line(self, x1,y1,z1, x2,y2,z2, block): self.drawPoints(getLine(x1,y1,z1, x2,y2,z2), block) def drawPoints(self, points, block): if self.width == 1: done = set() for p in points: if p not in done: self.mc.setBlock(p, block) done.add(p) else: done = set() for p in points: for point in self.nib: x0 = p[0]+point[0] y0 = p[1]+point[1] z0 = p[2]+point[2] if (x0,y0,z0) not in done: self.mc.setBlock(x0,y0,z0,block) done.add((x0,y0,z0)) if __name__ == "__main__": d = Drawing() pos = d.mc.player.getPos() d.face([(pos.x,pos.y,pos.z),(pos.x+20,pos.y+20,pos.z),(pos.x+20,pos.y+20,pos.z+20), (pos.x,pos.y,pos.z+20)], GLASS) n = 20 for t in range(0,n): (x1,z1) = (100*cos(t*2*pi/n),80*sin(t*2*pi/n)) for p in traverse(V3(pos.x,pos.y-1,pos.z),V3(pos.x+x1,pos.y-1,pos.z+z1)): d.mc.setBlock(p,OBSIDIAN) n = 40 vertices = [] for t in range(0,n): (x1,z1) = (100*cos(t*2*pi/n),80*sin(t*2*pi/n)) vertices.append((pos.x+x1,pos.y,pos.z+z1)) d.face(vertices, STAINED_GLASS_BLUE)