Я делаю реализацию алгоритма марширующих кубов, используя PyQt5 и PyOpenGL. Я наконец получил его, чтобы маршировать, рисовать точки и рисовать меня sh, используя следующий код. Единственная проблема заключается в том, что при повторном цикле рисуются сетки из предыдущего цикла. Даже если фильтр изменился, исходные сетки отображаются.
Что я делаю не так?
cubes.py:
import sys
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QSlider,
QOpenGLWidget, QLabel, QPushButton
)
from PyQt5.QtCore import Qt
from OpenGL.GL import (
glLoadIdentity, glTranslatef, glRotatef,
glClear, glBegin, glEnd,
glColor3fv, glVertex3fv,
GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT,
GL_QUADS, GL_LINES
)
from OpenGL.GLU import gluPerspective
from numerics import sin, cos, tan, avg, rnd
from classes import *
from lookup import table
import random, time
class mainWindow(QMainWindow): #Main class.
shapes = [] #place all instaces of shapes in this list in order to have them rendered.
dataPoints = []
zoomLevel = -10
rotateDegreeV = -90
rotateDegreeH = 0
marchActive = False
limit = -1
meshPoints = []
meshSectors = []
def keyPressEvent(self, event): #This is the keypress detector.
try:
key = event.key()
except:
key = -1
#print(key)
if key == 87:
self.rotateV(5)
elif key == 65:
self.rotateH(5)
elif key == 83:
self.rotateV(-5)
elif key == 68:
self.rotateH(-5)
elif key == 67:
self.zoom(1)
elif key == 88:
self.zoom(-1)
elif key == 77:
self.marchStep()
def __init__(self):
super(mainWindow, self).__init__()
self.currentStep = 0
self.width = 700 #Variables used for the setting of the size of everything
self.height = 600
self.setGeometry(0, 0, self.width + 50, self.height) #Set the window size
self.initData(3, 3, 3)
def setupUI(self):
self.openGLWidget = QOpenGLWidget(self) #Create the GLWidget
self.openGLWidget.setGeometry(0, 0, self.width, self.height)
self.openGLWidget.initializeGL()
self.openGLWidget.resizeGL(self.width, self.height) #Resize GL's knowledge of the window to match the physical size?
self.openGLWidget.paintGL = self.paintGL #override the default function with my own?
self.filterSlider = QSlider(Qt.Vertical, self)
self.filterSlider.setGeometry(self.width + 10, int(self.height / 2) - 100, 30, 200)
self.filterSlider.valueChanged[int].connect(self.filter)
self.limitDisplay = QLabel(self)
self.limitDisplay.setGeometry(self.width, int(self.height / 2) - 130, 50, 30)
self.limitDisplay.setAlignment(Qt.AlignCenter)
self.limitDisplay.setText('-1')
self.marchButton = QPushButton(self)
self.marchButton.setGeometry(self.width, int(self.height / 2) - 160, 50, 30)
self.marchButton.setText('March!')
self.marchButton.clicked.connect(self.marchStep)
def initData(self, sizeX, sizeY, sizeZ):
marchSizeX = sizeX - 1
marchSizeY = sizeY - 1
marchSizeZ = sizeZ - 1
xOff = -(sizeX / 2) + 0.5
yOff = -(sizeY / 2) + 0.5
zOff = -(sizeZ / 2) + 0.5
xMarchOff = -(marchSizeX / 2) + 0.5
yMarchOff = -(marchSizeY / 2) + 0.5
zMarchOff = -(marchSizeZ / 2) + 0.5
self.marchPoints = []
for z in range(marchSizeZ):
for y in range(marchSizeY):
for x in range(marchSizeX):
self.marchPoints.append((x + xMarchOff, y + yMarchOff ,z + zMarchOff))
for z in range(sizeZ):
for y in range(sizeY):
for x in range(sizeX):
val = self.generate(x + xOff, y + yOff, z + zOff)
dpColor = (0, (val + 1) / 2, (val + 1) / -2 + 1)
dpShape = cube((x + xOff, y + yOff, z + zOff), drawWires = False, drawFaces = True, color = dpColor)
dp = dataPoint((x + xOff, y + yOff, z + zOff), val, dpShape)
self.dataPoints.append(dp)
self.shapes.append(dpShape)
def paintGL(self):
#This function uses shape objects, such as cube() or meshSector(). Shape objects require the following:
#a list named 'vertices' - This list is a list of points, from which edges and faces are drawn.
#a list named 'wires' - This list is a list of tuples which refer to vertices, dictating where to draw wires.
#a list named 'facets' - This list is a list of tuples which refer to vertices, ditating where to draw facets.
#a bool named 'render' - This bool is used to dictate whether or not to draw the shape.
#a bool named 'drawWires' - This bool is used to dictate whether wires should be drawn.
#a bool named 'drawFaces' - This bool is used to dictate whether facets should be drawn.
glLoadIdentity()
gluPerspective(45, self.width / self.height, 0.1, 110.0) #set perspective?
glTranslatef(0, 0, self.zoomLevel) #I used -10 instead of -2 in the PyGame version.
glRotatef(self.rotateDegreeV, 1, 0, 0) #I used 2 instead of 1 in the PyGame version.
glRotatef(self.rotateDegreeH, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
if len(self.shapes) != 0:
glBegin(GL_LINES)
for s in self.shapes:
glColor3fv(s.color)
if s.render and s.drawWires:
for w in s.wires:
for v in w:
glVertex3fv(s.vertices[v])
glEnd()
glBegin(GL_QUADS)
for s in self.shapes:
glColor3fv(s.color)
if s.render and s.drawFaces:
for f in s.facets:
for v in f:
glVertex3fv(s.vertices[v])
glEnd()
def marchStep(self):
print(self.currentStep)
if not self.marchActive: #initialize
marchAddr = len(self.shapes)
self.marchingCube = cube(size = 1)
self.shapes.append(self.marchingCube)
self.marchActive = True
self.currentStep = 0
if self.currentStep == len(self.marchPoints): #1 step after last
self.marchingCube.hide()
self.currentStep += 1
for mp in self.meshPoints:
mp.shape.hide()
self.meshPoints = []
self.openGLWidget.update()
return
if self.currentStep == len(self.marchPoints) + 1: #2 steps after last
#print('meshPoints: {}'.format(self.meshPoints))
for mp in self.meshPoints:
#print(mp.shape)
self.shapes.remove(mp.shape)
self.meshPoints.clear()
for shape in self.shapes:
if shape in self.meshSectors:
self.shapes.remove(shape)
self.meshSectors = []
self.currentStep = -1
self.openGLWidget.update()
return
if self.currentStep == -1: #1 step before first
self.marchingCube.hide()
self.currentStep += 1
print('self.meshPoints: {}\nself.meshSectors: {}\nself.shapes: {}'.format(self.meshPoints, self.meshSectors, self.shapes))
self.openGLWidget.update()
return
self.marchingCube.show()
p = self.marchPoints[self.currentStep]
x, y, z = p
self.marchingCube.move((x, y, z))
points = []
for i in range(8):
#print(self.marchingCube.vertices[i])
point = self.getDataPointByLocation(self.marchingCube.vertices[i])
points.append(point)
#place meshpoints and highlight the active ones.
MPs = []
for pair in self.marchingCube.wires:
pointA = points[pair[0]]
pointB = points[pair[1]]
#print('pointA.value: {} pointB.value: {} limit: {}'.formatpointA.value, pointB.value, self.limit)
xA, yA, zA = pointA.location
xB, yB, zB = pointB.location
valA = (pointA.value + 1) / 2
valB = (pointB.value + 1) / 2
xC = float(avg([xA, xB]))
yC = float(avg([yA, yB]))
zC = float(avg([zA, zB]))
mp = meshPoint()
mp.place(xC, yC, zC)
mp.setShape(cube(size = 0.05, drawWires = False, drawFaces = True, color = (1, 0, 0)))
mp.shape.move((xC, yC, zC))
self.shapes.append(mp.shape)
self.meshPoints.append(mp)
MPs.append(mp)
if (pointA.value < self.limit and pointB.value > self.limit) or (pointA.value > self.limit and pointB.value < self.limit):
mp.setActive(True)
else:
mp.setActive(False)
activeConfig = 0
sector = meshSector()
self.meshSectors.append(sector)
self.shapes.append(sector)
for i in range(8):
if points[i].value > self.limit:
activeConfig += int(2 ** i)
print('Configuration number: {}'.format(activeConfig))
if activeConfig > 127:
activeConfig = 255 - activeConfig
print('Configuration number: {}'.format(activeConfig))
config = table[activeConfig]
print('Configuration: {}'.format(config))
print('number of points: {}'.format(len(MPs)))
for data in config:
a, b, c = data
locA = MPs[a].location
locB = MPs[b].location
locC = MPs[c].location
sector.addFacet((locA, locB, locC))
print('stepping')
self.currentStep += 1
self.rotateH(0)
def zoom(self, value):
self.zoomLevel += value
self.openGLWidget.update()
def rotateV(self, value):
self.rotateDegreeV += value
self.openGLWidget.update()
def rotateH(self, value):
self.rotateDegreeH += value
self.openGLWidget.update()
def filter(self, value):
self.limit = rnd((value / 49.5) -1, -2)
for d in self.dataPoints:
if d.value < self.limit:
d.shape.hide()
else:
d.shape.show()
self.limitDisplay.setText(str(self.limit))
self.openGLWidget.update()
def getDataPointByLocation(self, coord):
x, y, z = coord
#print(self.dataPoints)
#print('requested coordinates: {}'.format(coord))
for dp in self.dataPoints:
#print('dataPoint.location: {}'.format(dp.location))
if dp.location == (x, y, z):
return dp
return False
def generate2(self, xIn, yIn, zIn):
if xIn == 0 and yIn == 0 and zIn == 0:
return 0.5
return -0.5
def generate(self, xIn, yIn, zIn): #Function which produces semi-random values based on the supplied coordinates.
i = -int(xIn * yIn * (10 + zIn))
j = int(xIn * yIn * (10 + zIn))
if i < j:
mixer = random.randint(i, j + 1)
else:
mixer = random.randint(j, i + 1)
a = avg([sin(cos(xIn)), tan(tan(yIn)), cos(tan(zIn))])
out = mixer * a
while out > 10:
out -= 5
while out < -10:
out += 5
return float(out / 10)
#classes------------------------------------------------------------
app = QApplication([])
window = mainWindow()
window.setupUI()
window.show()
sys.exit(app.exec_())
classes.py:
class cube():
render = True
def __init__(self, location = (0, 0, 0), size = 0.1, drawWires = True, drawFaces = False, color = (1, 1, 1)):
self.location = location
self.size = size
self.drawWires = drawWires
self.drawFaces = drawFaces
self.color = color
self.compute()
def compute(self):
x, y, z = self.location
s = self.size / 2
self.vertices = [ #8 corner points calculated in reference to the supplied center point
(-s + x, s + y, -s + z), (s + x, s + y, -s + z),
(s + x, -s + y, -s + z), (-s + x, -s + y, -s + z),
(-s + x, s + y, s + z), (s + x, s + y, s + z),
(s + x, -s + y, s + z), (-s + x, -s + y, s + z)
]
self.wires = [ #12 tuples referencing the corner points
(0,1), (0,3), (0,4), (2,1), (2,3), (2,6),
(7,3), (7,4), (7,6), (5,1), (5,4), (5,6)
]
self.facets = [ #6 tuples referencing the corner points
(0, 1, 2, 3), (0, 1, 6, 5), (0, 3, 7, 4),
(6, 5, 1, 2), (6, 7, 4, 5), (6, 7, 3, 2)
]
def show(self):
self.render = True
def hide(self):
self.render = False
def move(self, location):
self.location = location
self.compute()
def recolor(self, col):
if type(col) is tuple:
self.color = col
class dataPoint():
def __init__(self, location = (0, 0, 0), value = 0, shape = None):
self.location = location
self.value = value
self.shape = shape
def place(self, x, y, z):
self.location = (x, y, z)
def set(self, val):
self.value = val
def setShape(self, shape):
self.shape = shape
class meshPoint():
active = False
def __init__(self, location = (0, 0, 0), shape = None):
self.location = location
self.shape = shape
if self.shape != None:
#print('{} is hiding shape'.format(self))
self.shape.hide()
def place(self, x, y, z):
self.location = (x, y, z)
def setShape(self, shape):
self.shape = shape
if self.shape != None:
#print('{} is hiding shape'.format(self))
self.shape.hide()
def setActive(self, state):
self.active = state
if self.active and self.shape != None:
#print('{} is showing shape'.format(self))
self.shape.show()
elif self.shape != None:
#print('{} is hiding shape'.format(self))
self.shape.hide()
class meshSector():
vertices = []
facets = []
wires = []
render = True
def __init__(self, drawWires = True, drawFaces = False, color = (1, 1, 1)):
self.drawWires = drawWires
self.drawFaces = drawFaces
self.color = color
def addFacet(self, coords): #takes a tuple of three location tuples.
addr = len(self.vertices)
for c in coords:
self.vertices.append(c)
self.facets.append((addr, addr + 1, addr + 2))
self.wires.append((addr, addr + 1))
self.wires.append((addr, addr + 2))
self.wires.append((addr + 1, addr + 2))
lookup.py:
table = [ #config number is line number - 2
[], #config 0
[(0, 1, 2)],
[(0, 3, 9)],
[(1, 2, 3), (2, 3, 9)],
[(3, 4, 5)],
[(0, 1, 2), (3, 4, 5)],
[(0, 4, 5), (0, 5, 9)],
[(1, 2, 9), (1, 4, 9), (4, 5, 9)],
[(1, 4, 6)],
[(0, 2, 4), (0, 2, 6)],
[(0, 3, 9), (1, 4, 6)], #config 10
[(2, 3, 4), (2, 3, 9), (2, 4, 6)],
[(1, 3, 5), (1, 5, 6)],
[(0, 2, 6), (0, 3, 6), (3, 5, 6)],
[(0, 1, 5), (0, 5, 9), (1, 5, 6)],
[(2, 5, 6), (2, 5, 9)],
[(2, 7, 10)],
[(0, 1, 7), (0, 7, 10)],
[(2, 7, 10), (0, 3, 9)],
[(1, 3, 9), (1, 7, 10), (1, 9, 10)],
[(2, 6, 10), (3, 4, 5)], #config 20
[(0, 1, 7), (0, 1, 10), (3, 4, 5)],
[(0, 4, 5), (0, 5, 9), (2, 7, 10)],
[(1, 7, 10), (1, 9, 10), (1, 5, 9), (1, 4, 5)],
[(1, 4, 6), (2, 7, 10)],
[(0, 4, 6), (0, 6, 7), (0, 7, 10)],
[(0, 3, 9), (1, 4, 6), (2, 7, 10)],
[(6, 7, 10), (3, 9, 10), (3, 4, 6)],
[(1, 3, 5), (1, 5, 6), (2, 7, 10)],
[(0, 7, 10), (0, 6, 7), (0, 5, 6), (0, 3, 5)],
[(0, 1, 2), (5, 6, 7), (5, 7, 10), (5, 9, 10)], #config 30
[(5, 6, 7), (5, 7, 10), (5, 9, 10)],
[(9, 10, 11)],
[(0, 1, 2), (9, 10, 11)],
[(0, 3, 10), (3, 10, 11)],
[(1, 2, 3), (2, 3, 10), (3, 10, 11)],
[(3, 4, 5), (9, 10, 11)],
[(0, 1, 2), (3, 4, 5), (9, 10, 11)],
[(0, 4, 5), (0, 5, 11), (0, 10, 11)],
[(2, 10, 11), (4, 5, 11), (1, 2, 4), (2, 4, 11)],
[(1, 4, 6), (9, 10, 11)], #config 40
[(0, 2, 4), (2, 4, 6), (9, 10, 11)],
[(0, 3, 10), (3, 10, 11), (1, 4, 6)],
[(2, 4, 6), (2, 3, 4), (2, 3, 11), (2, 10, 11)],
[(1, 3, 5), (1, 5, 6), (9, 10, 11)],
[(3, 5, 6), (0, 3, 6), (0, 2, 6), (9, 10, 11)],
[(0, 1, 6), (0, 5, 6), (0, 5, 11), (0, 10, 11)],
[(5, 6, 11), (6, 10, 11), (2, 6, 10)],
[(2, 7, 9), (7, 9, 11)],
[(0, 1, 7), (0, 7, 9), (7, 9, 11)],
[(0, 3, 11), (0, 2, 11), (2, 7, 11)], #config 50
[(1, 3, 11), (1, 7, 11)],
[(2, 9, 11), (1, 7, 11), (3, 4, 5)],
[(7, 9, 11), (0, 7, 9), (0, 1, 7), (3, 4, 5)],
[(2, 7, 11), (0, 2, 11), (0, 4, 11), (0, 5, 11)],
[(1, 4, 7), (1, 5, 7), (5, 7, 11)],
[(2, 7, 9), (7, 9, 11), (1, 4, 6)],
[(4, 6, 7), (0, 4, 7), (0, 7, 11), (0, 9, 11)],
[(1, 4, 6), (2, 7, 11), (0, 2, 11), (0, 3, 11)],
[(3, 7, 11), (3, 6, 7), (3, 4, 6)],
[(2, 7, 11), (2, 9, 11), (1, 3, 5), (1, 5, 6)], #config 60
[(0, 9, 11), (0, 7, 11), (0, 6, 7), (0, 3, 6), (3, 5, 6)],
[(2, 7, 11), (0, 2, 11), (0, 5, 11), (0, 1, 5), (1, 5, 6)],
[(5, 6, 7), (5, 7, 11)],
[(5, 8, 11)],
[(0, 1, 2), (5, 8, 11)],
[(0, 3, 9), (5, 8, 11)],
[(1, 2, 3), (2, 3, 9), (5, 8, 11)],
[(3, 4, 8), (3, 8, 11)],
[(3, 4, 8), (3, 8, 11), (0, 1, 2)],
[(0, 9, 11), (0, 8, 11), (0, 4, 8)], #config 70
[(2, 10, 11), (2, 3, 11), (2, 3, 4), (2, 4, 6)],
[(3, 4, 5), (9, 10, 11)],
[(1, 3, 5), (1, 5, 6), (9, 10, 11)],
[(0, 1, 2), (3, 4 ,5), (9, 10, 11)],
[(2, 3, 9), (2, 3, 4), (2, 4, 6), (5, 8, 11)],
[(3, 1, 6), (3, 6, 8), (3, 8, 11)],
[(7, 9, 10), (5, 7, 9), (1, 5, 7), (1, 4, 5)],
[(6, 7, 10), (3, 9, 10), (3, 4, 6), (3, 6, 10)],
[(6, 8, 11), (6, 9, 11), (2, 6, 9)],
[(2, 7, 10), (5, 8, 11)], #config 80
[(0, 1, 7), (0, 7, 10), (5, 8, 11)],
[(0, 3, 9), (2, 7, 10), (5, 8, 11)],
[(1, 7, 10), (1, 9, 10), (1, 3, 9), (5, 8, 11)],
[(3, 4, 8), (3, 8, 11), (2, 7, 10)],
[(0, 1, 7), (0, 7, 10), (3, 4, 8), (3, 8, 11)],
[(4, 8, 11), (4, 9, 11), (0, 4, 9), (2, 7, 10)],
[(9, 10, 11), (1, 4, 7), (4, 7, 8)],
[(1, 4, 6), (2, 7, 10), (5, 8, 11)],
[(0, 4, 6), (0, 6, 7), (0, 7, 10), (5, 8, 11)],
[(0, 3, 9), (1, 4, 6), (5, 8, 11), (2, 7, 10)], #config 90
[(3, 4, 6), (6, 7, 10), (3, 9, 10), (5, 8, 11)],
[(1, 3, 6), (3, 6, 8), (3, 8, 11), (2, 7, 10)],
[(0, 3, 10), (3, 10, 11), (6, 7, 8)],
[(0, 1, 2), (6, 7, 8), (9, 10, 11)],
[(6, 7, 8), (9, 10, 11)],
[(5, 8, 9), (8, 9, 10)],
[(0, 1, 2), (5, 8, 9), (8, 9, 10)],
[(0, 8, 10), (0, 3, 8), (3, 5, 8)],
[(2, 8, 10), (1, 2, 8), (1, 5, 8), (1, 3, 5)],
[(3, 4, 8), (3, 8, 9), (8, 9, 10)], #config 100
[(0, 1, 2), (3, 4, 8), (3, 8, 9), (8, 9, 10)],
[(0, 4, 8), (0, 8, 10)],
[(1, 4, 8), (1, 2, 8), (2, 8, 10)],
[(1, 4, 6), (5, 8, 9), (8, 9, 10)],
[(0, 4, 6), (0, 2, 6), (5, 8, 9), (8, 9, 10)],
[(1, 4, 6), (0, 8, 10), (0, 3, 5), (1, 4, 6)],
[(3, 4, 5), (2, 6, 8), (2, 8, 10)],
[(1, 6, 8), (1, 3, 8), (3, 8, 10), (3, 9, 10)],
[(0, 3, 9), (2, 6, 8), (2, 8, 10)],
[(0, 1, 10), (1, 6, 10), (6, 8, 10)], #config 110
[(2, 6, 8), (2, 8, 10)],
[(2, 5, 9), (2, 5, 8), (2, 7, 8)],
[(0, 5, 9), (0, 5, 8), (0, 1, 8), (1, 7, 8)],
[(0, 3, 5), (5, 7, 8), (0, 2, 7), (0, 5, 7)],
[(1, 3, 5), (1, 5, 8), (1, 7, 8)],
[(3, 4, 9), (4, 7, 9), (2, 7, 9), (4, 7, 8)],
[(0, 3, 9), (1, 4, 8), (1, 7, 8)],
[(0, 4, 8), (0, 7, 8), (0, 2, 7)],
[(1, 4, 7), (4, 7, 8)],
[(2, 5, 9), (1, 2, 4), (2, 4, 5), (6, 7, 8)], #config 120
[(6, 7, 8), (0, 4, 5), (0, 5, 9)],
[(0, 3, 5), (5, 7, 8), (0, 2, 7), (0, 5, 7), (1, 4, 6)],
[(3, 5, 8), (5, 7, 8), (3, 4, 7), (4, 6, 7)],
[(1, 2, 3), (2, 3, 9), (6, 7, 8)],
[(0, 3, 9), (6, 7, 8)],
[(0, 1, 2), (6, 7, 8)],
[(6, 7, 8)]
]
run.bat:
@echo off
cls
cubes.py
Это, вероятно, самый большой проект по написанию кода, который я когда-либо делал.