Программа, которая создает кратеры в ландшафте квадри, приводит к появлению прямоугольной геометрии? - PullRequest
0 голосов
/ 10 июля 2019

Example Example with Outlines

Когда я создаю кратер в местности, кажется, что иногда появляются нечетные прямоугольники, особенно когда кратер создается на границе окна, вызывая массивный шпильразрушения.Я подозреваю, что это как-то связано с функцией nodeIntersects, которая проверяет, находятся ли прямоугольники в пределах диапазона мыши, но я не могу найти ошибку.

# --------QuadTree Destructable Terrain

# ----Imports
from PIL import Image
import pyglet
from pyglet.window import mouse
from pyglet.window import key
import sys
import math

# ----Variables
terrain = Image.open("terrain.png")
width, height = terrain.size
window = pyglet.window.Window(width=width, height=height)
outline = 1
explosionPower = 50

blackNodes = []
whiteNodes = []
blackNodesCopy=[]

# ----Classes

#-Node: A rectangle that splits into multiple parts
class node:
    def __init__(self, min, width, height, parent):
        self.min = min
        self.width = width
        self.height = height
        self.children = []
        self.parent = parent
        self.black = False

    def subDivide(self):
        if self.height == 1:
            newWidth = math.floor(self.width/2)
            newWidth2 = math.ceil(self.width/2)

            self.children.append(node(self.min, newWidth, 1, self))
            self.children.append(node((self.min[0]+newWidth,self.min[1]), newWidth2, 1, self))
            return self.children
        elif self.width == 1:
            newHeight = math.floor(self.height/2)
            newHeight2 = math.ceil(self.height/2)

            self.children.append(node(self.min, 1, newHeight, self))
            self.children.append(node((self.min[0],self.min[1]+newHeight), 1, newHeight2, self))
            return self.children
        else:
            newHeight = math.floor(self.height/2) 
            newWidth = math.floor(self.width/2) 

            newHeight2 = math.ceil(self.height/2)
            newWidth2 = math.ceil(self.width/2)

            self.children.append(node(self.min, newWidth, newHeight, self))
            self.children.append(
                node((self.min[0]+newWidth, self.min[1]), newWidth2, newHeight, self))
            self.children.append(
                node((self.min[0]+newWidth, self.min[1]+newHeight), newWidth2, newHeight2, self))
            self.children.append(
                node((self.min[0], self.min[1]+newHeight), newWidth, newHeight2, self))
            return self.children

    def allSameColor(self, image):
        getColors = []
        pixels = image.load()
        global height
        if (self.height * self.width) == 1:
            return True
        for x in range(self.min[0], self.min[0]+self.width):
            for y in range(abs(self.min[1]+self.height - height), abs(self.min[1]-height)):
                if pixels[x, y] not in getColors and (pixels[x, y] == (0,0,0,255) or pixels[x,y] == (255,255,255,255)):
                    getColors.append(pixels[x, y])
        return len(getColors) == 1

    def recursionDivision(self, image):
        if not self.allSameColor(image):
            childs = self.subDivide()
            for x in childs:
                    x.recursionDivision(image)
        else:
            pixels = image.load()
            global height
            if pixels[self.min[0], abs(self.min[1]-height) - 1] == (0, 0, 0, 255):
                self.black = True
            else:
                self.black = False

    def getAllNodes(self):
        if len(self.children) == 0:
            global blackNodes
            global whiteNodes

            if(self.black):
                blackNodes.append(self)
            else:
                whiteNodes.append(self)
        else:
            for x in self.children:
                x.getAllNodes()

    def __str__(self):
        return str(self.min[0]) + " " + str(self.min[1]) + " " + str(self.width) + " " + str(self.height)


#-QuadtreeFromImage: Sets into motion the Node chain reaction
class QuadtreeFromImage:
    def __init__(self, image):
        width, height = image.size
        self.coreNode = node((0, 0), width, height, None)

        self.coreNode.recursionDivision(image)

#-------------------------------------------------Decorated Functions
#-Registers all Mouse Presses and creates Craters
@window.event   
def on_mouse_press(x, y, button, modifiers):
    if button == mouse.LEFT:
        #-Check all boxes within the mouse's range
        boxesForUpdate = []
        global blackNodes
        for z in blackNodes:
            if nodeIntersects(z,x,y):
                boxesForUpdate.append(z)

        #-Get all the boxes that stay after the explosion
        newBoxes = []
        for z in boxesForUpdate:
            global explosionPower
            if not (distBetweenPoints(z.min[0],z.min[1],x,y)+distBetweenPoints(z.min[0],z.min[1]+z.height,x,y)+distBetweenPoints(z.min[0]+z.width,z.min[1]+z.height,x,y)+distBetweenPoints(z.min[0]+z.width,z.min[1],x,y) <= explosionPower*4):
                list = explosionRecursion(z,x,y).copy()
                for b in list:
                    newBoxes.append(b)

        #-Create Appropriate changes to the NodeList
        for b in newBoxes:
            blackNodes.append(b)
        for b in boxesForUpdate:
            blackNodes.remove(b)


#-Take in key presses and change Settings
@window.event
def on_key_press(symbol, modifiers):
    #-Turn On Outline
    if symbol == key.O:
        global outline
        outline += 1
        if outline == 2:
            outline = 0
    #-Change ExplosionPower from 10-70
    if symbol == key.E:
        global explosionPower
        explosionPower += 10
        if explosionPower == 80:
            explosionPower = 10
    #-Respawn Terrain
    if symbol == key.R:
        global blackNodes
        global blackNodesCopy
        blackNodes = blackNodesCopy.copy()
#-----------------------------------------------------Normal Functions    
def closestPointToRay(X1, Y1, X2, Y2, X0, Y0):
    result = closestPointToLine(X1, Y1, X2, Y2, X0, Y0)
    biggerx = 0
    biggery = 0
    smallerx = 0
    smallery = 0
    if(X1 > X2):
        biggerx = X1
        biggery = Y1
        smallerx = X2
        smallery = Y2
    elif(X2 > X1):
        biggerx = X2
        biggery = Y2
        smallerx = X1
        biggerx = X2
    else:
        if(Y1 > Y2):
            biggery = Y1
            biggerx = X1
            smallery = Y2
            smallerx = X2
        else:
            biggery = Y2
            biggerx = X2
            smallery = Y1
            smallerx = X1
        if(result[1] < smallery):
            return(smallerx, smallery)
        elif(result[1] > biggery):
            return(biggerx, biggery)
        return result

    if(result[0] < smallerx):
        return(smallerx, smallery)
    elif(result[0] > biggerx):
        return(biggerx, biggery)
    return result

def closestPointToLine(X1, Y1, X2, Y2, X0, Y0):
    if(X1-X2 == 0):
        return (X1, Y0)
    m1 = (Y1-Y2)/(X1-X2)
    if(m1 == 0):
        return (X0, Y1)
    m2 = (m1**-1) * -1

    b1 = -(X1 * m1) + Y1
    b2 = -(X0 * m2) + Y0

    X = (b2-b1)/(m1-m2)
    Y = X * m1 + b1
    return (X, Y)

def distBetweenPoints(x1, y1, x2, y2):
    return math.sqrt((x2 - x1)**2 + (y2 - y1)**2)

def nodeIntersects(z,x,y):
    if(x>= z.min[0] and x<= z.min[0]+z.width and y>=z.min[1] and y<=z.min[1]+z.height):
        return True

    global explosionPower

    closestPoint = closestPointToRay(z.min[0],z.min[1],z.min[0],z.min[1]+z.height,x,y)
    if distBetweenPoints(closestPoint[0],closestPoint[1],x,y) <= explosionPower:
        return True
    closestPoint = closestPointToRay(z.min[0],z.min[1],z.min[0]+z.width,z.min[1],x,y)
    if distBetweenPoints(closestPoint[0],closestPoint[1],x,y) <= explosionPower:
        return True
    closestPoint = closestPointToRay(z.min[0],z.min[1]+z.height,z.min[0]+z.width,z.min[1]+z.height,x,y)
    if distBetweenPoints(closestPoint[0],closestPoint[1],x,y) <= explosionPower:
        return True
    closestPoint = closestPointToRay(z.min[0]+z.width,z.min[1]+z.height,z.min[0]+z.width,z.min[1],x,y)
    if distBetweenPoints(closestPoint[0],closestPoint[1],x,y) <= explosionPower:
        return True

    return False

def explosionRecursion(z,x,y):
    nodeList = []
    for a in z.subDivide():
        if nodeIntersects(a,x,y):
            if(a.width != 1 or a.height != 1) and not (a.width * a.height == 0):
                list = explosionRecursion(a,x,y)
                for b in list:
                    nodeList.append(b)
        else:
            nodeList.append(a)
    return nodeList

#------------------------------------------------------Running
def update(dt):
    global blackNodes
    global whiteNodes
    window.clear()
    for x in blackNodes:
        global height
        global outline
        pyglet.graphics.draw(4, pyglet.gl.GL_QUADS, ('v2f', (x.min[0], x.min[1], 
                                                            x.min[0]  + x.width - outline, x.min[1],
                                                            x.min[0]  + x.width - outline, x.min[1] + x.height - outline,
                                                             x.min[0], x.min[1] + x.height - outline  )), ('c3B', (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) )


newQuadTree = QuadtreeFromImage(terrain)
newQuadTree.coreNode.getAllNodes()
blackNodesCopy =  blackNodes.copy()
pyglet.gl.glClearColor(1,1,1,1)

pyglet.clock.schedule_interval(update, 1/60)
pyglet.app.run()

В коде используется следующее изображениеи должен называться terrain.png: terrain.png

...