How to print in Python using class and print function - python

class HexagonInteriorAngle(object):
def __init__(self, x):
self.x = self
def FindInteriorAngle(self):
degrees = int((x - 2) * 180)
interior = int(degrees / x)
def Print(self):
if x == 3:
print(str("an interior angle of a triangle equals " + str(interior)))
elif x == 4:
print("an interior angle of an equilateral equals " + str(interior))
elif x == 5:
print("an interior angle of a pentagon equals " + str(interior))
elif x == 6:
print("an interior angle of a hexagon equals " + str(interior))
elif x == 7:
print("an interior angle of a heptagon equals " + str(interior))
elif x == 8:
print("an interior angle of an octagon equals " + str(interior))
elif x == 9:
print("an interior angle of a nonagon equals " + str(interior))
elif x == 10:
print("an interior angle of a decagon equals " + str(interior))
else:
print(str(interior))
if __name__ == "__main__":
x = int(input("enter: "))
hexaObj = HexagonInteriorAngle(x)
hexaObj.FindInteriorAngle()
hexaObj.Print()
What I want the program to do is to identify what type of polygon it is based off of the number of sides (ex. 6 sides = hexagon, 5 sides = pentagon, etc) and then print what one interior angle would be for that polygon (formula to find the interior angle : (the number of sides - 2) x 180 and then taking that answer and then dividing it by the number of sides). example: hexagon.
( 6 - 2 ) x 180 = 720
720 / 6 = 120
Right now I'm pretty sure the actual code part is correct because if you do this it prints fine:
class HexagonInteriorAngle(object):
def __init__(self, x):
self.x = self
def FindInteriorAngle(self):
degrees = int((x - 2) * 180)
interior = int(degrees / x)
print("interior angle " + str(interior))
if __name__ == "__main__":
x = int(input("enter: "))
hexaObj = HexagonInteriorAngle(x)
hexaObj.FindInteriorAngle()

You should do this:
def FindInteriorAngle(self):
degrees = int((x - 2) * 180)
interior = int(degrees / x)
return interior
# ...
if __name__ == "__main__":
x = int(input("enter: "))
hexaObj = HexagonInteriorAngle(x)
interior = hexaObj.FindInteriorAngle()
print("interior angle " + str(interior))

Almost unrelated to your answer, we could clean up your code a bit with the use of some property fields. Using __str__ instead of a Print method is more idiomatic, as well.
class Polygon:
shapes = [
None, # index 0
None, # index 1
"line", # index 2, etc...
"triangle",
"rectangle",
"pentagon",
"hexagon",
"heptagon",
"octogon",
"nonagon",
"decagon",
]
def __init__(self, sides):
if not 2 <= sides <= 10:
raise ValueError("Polygon only supports shapes with sides 2-10")
self.sides = sides
#property
def shape(self):
return self.shapes[self.sides]
#property
def interior_angle(self):
return (self.sides - 2) * 180 / self.sides
def __str__(self):
return f"The interior angle of a {self.shape} is {self.interior_angle}"
if __name__ == "__main__":
poly = Polygon(3)
print(poly)

Related

Why doesn't update repaint a scene?

I am making a marching cubes project in python using PyQt5 and PyOpenGL. I am trying to hide the wireframe cube which marches across the screen, referenced as mainWindow.marchingCube to disappear after cycling through. I managed to get the disappearing cycle to occur, but the cube does not actually disappear. I called the QOpenGLWidget's update function, but the cube still did not disappear.
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 #Numerics is my custom math library.
import random, time
class mainWindow(QMainWindow): #Main class.
shapes = [] #this will hold instances of the following classes: cube
dataPoints = []
zoomLevel = -10
rotateDegreeV = -90
rotateDegreeH = 0
marchActive = False
limit = -1
meshPoints = []
class cube():
render = True
solid = False
color = (1, 1, 1)
def config(self, x, y, z, size = 0.1, solid = False, color = (1, 1, 1)):
self.solid = solid
self.color = color
self.size = size / 2
s = self.size
self.vertices = [
(-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.edges = [
(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 = [
(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
class dataPoint():
location = (0, 0, 0)
value = 0
shape = None
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():
location = (0, 0, 0)
shape = None
def place(self, x, y, z):
self.location = (x, y, z)
def setShape(self, shape):
self.shape = shape
def keyPressEvent(self, event): #This is the keypress detector. I use this to determine input to edit grids.
try:
key = event.key()
#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()
except:
pass
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 marchStep(self):
if not self.marchActive:
marchAddr = len(self.shapes)
self.shapes.append(self.cube())
self.marchingCube = self.shapes[marchAddr]
self.marchActive = True
self.currentStep = 0
if self.currentStep == len(self.marchPoints):
self.currentStep = 0
#print('meshPoints: {}'.format(self.meshPoints))
for mp in self.meshPoints:
#print(mp.shape)
self.shapes.remove(mp.shape)
self.meshPoints.clear()
self.marchingCube.hide()
return
if self.currentStep == 0:
self.marchingCube.show()
p = self.marchPoints[self.currentStep]
x, y, z = p
self.marchingCube.config(x, y, z, size = 1)
points = []
for i in range(8):
point = self.getDataPointByLocation(self.marchingCube.vertices[i])
points.append(point)
self.openGLWidget.update()
#print('step: {} x: {} y: {} z: {}'.format(self.currentStep, x, y, z))
#for point in points:
# print(point.location, end = ' ')
#print()
for pair in self.marchingCube.edges:
pointA = points[pair[0]]
pointB = points[pair[1]]
#print('pointA.value: {} pointB.value: {} limit: {}'.formatpointA.value, pointB.value, self.limit)
if (pointA.value < self.limit and pointB.value > self.limit) or (pointA.value > self.limit and 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 = self.meshPoint()
mp.place(xC, yC, zC)
mp.setShape(self.cube())
mp.shape.config(xC, yC, zC, size = 0.05, solid = True, color = (1, 0, 0))
self.shapes.append(mp.shape)
self.meshPoints.append(mp)
self.currentStep += 1
self.openGLWidget.update()
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
for dp in self.dataPoints:
if dp.location == (x, y, z):
return dp
return False
def paintGL(self):
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 not s.solid:
for e in s.edges:
for v in e:
glVertex3fv(s.vertices[v])
glEnd()
glBegin(GL_QUADS)
for s in self.shapes:
glColor3fv(s.color)
if s.render and s.solid:
for f in s.facets:
for v in f:
glVertex3fv(s.vertices[v])
glEnd()
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
vals = []
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):
loc = len(self.dataPoints)
val = self.generate(x + xOff, y + yOff, z + zOff)
self.dataPoints.append(self.dataPoint())
self.dataPoints[loc].place(x + xOff, y + yOff, z + zOff)
self.dataPoints[loc].set(val)
loc2 = len(self.shapes)
self.shapes.append(self.cube())
self.shapes[loc2].config(x + xOff, y + yOff, z + zOff, solid = True, color = (0, (val + 1) / 2, (val + 1) / -2 + 1))
self.dataPoints[loc].setShape(self.shapes[loc2])
vals.append(val)
print(avg(vals))
def generate(self, xIn, yIn, zIn): #Function which produces semi-random values based on the supplied coordinates.
i = -(xIn * yIn * (10 + zIn))
j = xIn * yIn * (10 + zIn)
if i < j:
mixer = random.randint(i, j)
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)
app = QApplication([])
window = mainWindow()
window.setupUI()
window.show()
sys.exit(app.exec_())
Why doesn't the cube disappear? I have caught wind during my web searches on the subject that update does not always work as expected. Directly calling self.openGLWidget.paintGL() does not work either. What must I do to make the cube disappear?
EDIT:
If I make a call to rotate, rotate, or zoom, the screen refreshes and the meshpoints as well as the marching cube all disappear. I think I may end up making a workaround by calling one of those with a zero value.
To test, save the following code in a file named numerics.py in the same directory as the rest of the code.
from decimal import Decimal as dec
degrad = 'deg'
pi = 3.14159265358979323846
terms = dec(9) #number of terms used for the trig calculations
def version():
print('numerics.py version 1.0.0')
print('Packaged with the cubes project')
def mode(modeinput = ''): #switch between degrees and radians or check the current mode
global degrad
if modeinput == 'deg':
degrad = 'deg'
return 'deg'
if modeinput == 'rad':
degrad = 'rad'
return 'rad'
if modeinput == '':
return degrad
else:
return False
def accuracy(accinput = ''):
global terms
global pi
if accinput == '':
return terms
terms = dec(accinput)
PI = calculatePi(accinput)
print('Pi is: {}'.format(PI))
return terms
def calculatePi(placeIn = terms):
if placeIn > 15:
if input("Warning: You have chosen to calculate more than 20 digits of pi. This may take a LONG TIME and may be inacurate. Enter 'yes' if you wish to proceed. If you enter anything else, this function will revert to 10 digits.") == 'yes':
place = placeIn
else:
place = 10
else:
place = placeIn
print('Calculating Pi...\nPlease wait, as this may take a while.')
PI = dec(3)
addSub = True
for i in range(2, 2 * (int(place) ** 6) + 1, 2):
if addSub:
PI += dec(4) / (dec(i) * dec(i + 1) * dec(i + 2))
elif not addSub:
PI -= dec(4) / (dec(i) * dec(i + 1) * dec(i + 2))
addSub = not addSub
return rnd(PI, -(place), mode = 'cutoff')
def radToDeg(radin):
return (dec(radin) * dec(180 / pi))
def degToRad(degin):
return (dec(degin) * dec(pi / 180))
def avg(numsIn): #return the average of two numbers, specified as an integer or float
num1 = dec(0)
for i in numsIn:
num1 += dec(i)
return rnd(dec(num1 / dec(len(numsIn))))
def sin(anglein, dr = degrad): #return sine of the supplied angle using the predetermined mode or the supplied mode
if dr == 'deg':
while anglein > 180:
anglein -= 360
while anglein < -180:
anglein += 360
angle = degToRad(anglein)
if dr == 'rad':
while anglein > pi:
anglein -= (2 * pi)
while anglein < -pi:
anglein += (2 * pi)
angle = anglein
return rnd(rawsin(dec(angle)), -terms)
def arcsin(ratioin, dr = degrad): #return arcsine of the supplied ratio using the predetermined mode or the supplied mode
if ratioin > 1 or ratioin < -1: #if the input is illegal
return False
attempt = dec(0) #start at 0
target = rnd(dec(ratioin), -terms) #identify the target value
#print('target is: {}'.format(target))
for i in range(-1, int(terms) + 1): #for each place from 10s to terms decimal place (use -i, not i)
#print('Editing place {0}'.format(10 ** -i)) #debugging
for j in range(10): #for 10 steps
#print('current attempt: {}'.format(attempt), end = ' ')
if rnd(sin(attempt, dr), -terms) == target:
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: {0} final: {1}'.format(attempt, final))
return final
if rnd(sin(attempt, dr), -terms) < target:
#add some
attempt += (dec(10) ** -i)
#print('attempt: {}'.format(attempt), end = ' ')
if rnd(sin(attempt, dr), -terms) > target:
#subtract some
attempt -= (dec(10) ** -i)
#print('attempt: {}'.format(attempt), end = ' ')
#print('')
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: {0} final: {1}'.format(attempt, final))
return (final)
def cos(anglein, dr = degrad): #return cosine of the supplied angle
if dr == 'deg':
return rawsin(degToRad(90 - anglein))
else:
angle = anglein
return rnd(rawsin(90 - angle), -terms)
def arccos(ratioin, dr = degrad): #return arccosine of the supplied ratio
if ratioin > 1 or ratioin < -1:
return False
attempt = dec(0) #start at 0
target = rnd(dec(ratioin), -terms) #identify the target value
#print('target is: {}'.format(target))
for i in range(-1, int(terms) + 1): #for each place from 10s to terms decimal place (use -i, not i)
#print('Editing place {0}'.format(10 ** -i)) #debugging
for j in range(10): #for 10 steps
#print('current attempt: {}'.format(attempt), end = ' ')
if rnd(cos(attempt, dr), -terms) == target:
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: {0} final: {1}'.format(attempt, final))
return final
if rnd(cos(attempt, dr), -terms) < target:
#add some
attempt += (dec(10) ** -i)
#print('attempt: {}'.format(attempt), end = ' ')
if rnd(cos(attempt, dr), -terms) > target:
#subtract some
attempt -= (dec(10) ** -i)
#print('attempt: {}'.format(attempt), end = ' ')
#print('')
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: {0} final: {1}'.format(attempt, final))
return (final)
def tan(anglein, dr = degrad): #return tangent of the supplied angle
a = sin(anglein, dr)
b = cos(anglein, dr)
if (not a == 0) and (not b == 0):
return rnd((a / b), -terms)
else:
return False
def arctan(ratioin, dr = degrad): #return arctangent of the supplied ratio
if ratioin > 1 or ratioin < -1:
return False
attempt = dec(0) #start at 0
target = rnd(dec(ratioin), -terms) #identify the target value
#print('target is: {}'.format(target))
for i in range(-1, int(terms) + 1): #for each place from 10s to terms decimal place (use -i, not i)
#print('Editing place {0}'.format(10 ** -i)) #debugging
for j in range(10): #for 10 steps
#print('current attempt: {}'.format(attempt), end = ' ')
if rnd(tan(attempt, dr), -terms) == target:
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: {0} final: {1}'.format(attempt, final))
return final
if rnd(tan(attempt, dr), -terms) < target:
#add some
attempt += (dec(10) ** -i)
#print('attempt: {}'.format(attempt), end = ' ')
if rnd(tan(attempt, dr), -terms) > target:
#subtract some
attempt -= (dec(10) ** -i)
#print('attempt: {}'.format(attempt), end = ' ')
#print('')
if attempt < 0:
final = (attempt * dec(-1))
else:
final = attempt
#print('attempt: {0} final: {1}'.format(attempt, final))
return (final)
def rawsin(anglein): #return the result of sine of the supplied angle, using radians
#This is the taylor series used.
#final = x - (x^3 / 3!) + (x^5 / 5!) - (x^7 / 7!) + (x^9 / 9!) - (x^11 / 11!)...
angle = dec(anglein)
final = angle
add = False
for i in range(3, int(terms) * 3, 2):
if add:
final += dec(angle ** i) / fact(i)
elif not add:
final -= dec(angle ** i) / fact(i)
add = not add
return final
def fact(intin): #return the factorial of the given integer, return False if not given an int
if intin == int(intin):
intout = 1
for i in range(1, intin + 1):
intout *= i
return intout
else:
return False
def rnd(numIn, decPlcIn = -terms, mode = 'fiveHigher'): #return the given number, rounded to the given decimal place.
#use 1 to indicate 10s, 0 to indicate 1s, -2 to indicate 100ths, etc.
num1 = dec(numIn)
decPlc = dec(decPlcIn)
if mode == 'fiveHigher':
return dec(str(dec(round(num1 * (dec(10) ** -decPlc))) * (dec(10) ** decPlc)).rstrip('0'))
elif mode == 'cutoff':
return dec(str(dec(int(num1 * (dec(10) ** -decPlc))) * (dec(10) ** decPlc)).rstrip('0'))
def root(numIn, rootVal):
num = dec(numIn)
rt = dec(dec(1) / rootVal)
num1 = num ** rt
return rnd(num1, -terms)
def quad(aIn, bIn, cIn): #Plugin for the quadratic formula. Provide a, b, and c.
a = dec(aIn)
b = dec(bIn)
c = dec(cIn)
try:
posResult = (-b + root((b ** dec(2)) - (dec(4) * a * c), 2)) / (dec(2) * a)
except:
posResult = False
try:
negResult = (-b - root((b ** dec(2)) - (dec(4) * a * c), 2)) / (dec(2) * a)
except:
negResult = False
return (posResult, negResult)
You are missing 1 call to self.openGLWidget.update(). There is a return statement in the instruction block of the if. The function is terminated at this point and the self.openGLWidget.update() instruction at the end of the code is never executed.
Add self.openGLWidget.update() right before return, to solve the issue:
class mainWindow(QMainWindow):
# [...]
def marchStep(self):
if not self.marchActive:
# [...]
self.currentStep = 0
if self.currentStep == len(self.marchPoints):
# [...]
self.meshPoints.clear()
self.marchingCube.hide()
self.openGLWidget.update() # <--------- ADD
return
if self.currentStep == 0:
self.marchingCube.show()
# [...]

How to pretty print a quadtree in python?

I have some code that can make a quad tree from data points. I know how to print out binary tree with slashes, but I don't even know where to start to print/draw out a tree with 4 children instead of 2 each to be able to visualize my tree.
I've been testing it by using my search_pqtreee function. For example, to list all the points in the northeast quadrant, I can test it by making a list like: [search_pqtree(q.ne,p) for p in points]
#The point import is a class for points in Cartesian coordinate systems
from point import *
class PQuadTreeNode():
def __init__(self,point,nw=None,ne=None,se=None,sw=None):
self.point = point
self.nw = nw
self.ne = ne
self.se = se
self.sw = sw
def __repr__(self):
return str(self.point)
def is_leaf(self):
return self.nw==None and self.ne==None and \
self.se==None and self.sw==None
def search_pqtree(q, p, is_find_only=True):
if q is None:
return
if q.point == p:
if is_find_only:
return q
else:
return
dx,dy = 0,0
if p.x >= q.point.x:
dx = 1
if p.y >= q.point.y:
dy = 1
qnum = dx+dy*2
child = [q.sw, q.se, q.nw, q.ne][qnum]
if child is None and not is_find_only:
return q
return search_pqtree(child, p, is_find_only)
def insert_pqtree(q, p):
n = search_pqtree(q, p, False)
node = PQuadTreeNode(point=p)
if p.x < n.point.x and p.y < n.point.y:
n.sw = node
elif p.x < n.point.x and p.y >= n.point.y:
n.nw = node
elif p.x >= n.point.x and p.y < n.point.y:
n.se = node
else:
n.ne = node
def pointquadtree(data):
root = PQuadTreeNode(point = data[0])
for p in data[1:]:
insert_pqtree(root, p)
return root
#Test
data1 = [ (2,2), (0,5), (8,0), (9,8), (7,14), (13,12), (14,13) ]
points = [Point(d[0], d[1]) for d in data1]
q = pointquadtree(points)
print([search_pqtree(q.ne, p) for p in points])
What I'm trying to say is if I was pretty printing a binary tree, it might look like this:
(2, 2)
/ \
(0, 5) (8, 0)
/ \ / \
Is there a way to write a function that print out 4 lines each? Or maybe print it out sideways?
As you classified your question with GIS and spatial, this problem made me think of a map with north-east, north-west, south-east and south-west in each corner.
A single node quadtree would simply be :
(0,0)
A two node quadtree would be :
.|( 1, 1)
----( 0, 0)----
.|.
With 3 nodes in depth that would go to :
| .|( 2, 2)
|----( 1, 1)----
.| .|.
------------( 0, 0)------------
.|.
|
|
I've implemented this idea, with some changes to your code to make it easier:
I've added a trivial point class, with the __repr__ method I needed for number formatting
I made quadrants into a dictionary to be able to loop on them
I thought I would need the get_depth method, but it's not used...
I also think that search and insert functions should be methods of the class PQuadTreeNode, but I leave it to you as an exercise :)
The implementation works with the following steps:
If the quadtree is a leaf, its map is the central point
Get the maps of the 4 quadrants (if empty, it's a dot)
Normalize them using the size of the largest, and puts them near the center of the parent
Combine the 4 quadrants with the quadtree point at the center.
This is of course higly recursive, and I didn't made any attempt at optimization.
If numbers have a length greater than 2 (like 100 or -10), you can adjust the num_length variable.
num_length = 2
num_fmt = '%' + str(num_length) + 'd'
class Point():
def __init__(self,x=None,y=None):
self.x = x
self.y = y
def __repr__(self):
return '(' + (num_fmt % self.x) + ',' + (num_fmt % self.y) + ')'
def normalize(corner, quadmap, width, height):
old_height = len(quadmap)
old_width = len(quadmap[0])
if old_height == height and old_width == width:
return quadmap
else:
blank_width = width - old_width
if corner == 'nw':
new = [' '*width for i in range(height - old_height)]
for line in quadmap:
new.append(' '*blank_width + line)
elif corner == 'ne':
new = [' '*width for i in range(height - old_height)]
for line in quadmap:
new.append(line + ' '*blank_width)
elif corner == 'sw':
new = []
for line in quadmap:
new.append(' '*blank_width + line)
for i in range(height - old_height):
new.append(' '*width)
elif corner == 'se':
new = []
for line in quadmap:
new.append(line + ' '*blank_width)
for i in range(height - old_height):
new.append(' '*width)
return new
class PQuadTreeNode():
def __init__(self,point,nw=None,ne=None,se=None,sw=None):
self.point = point
self.quadrants = {'nw':nw, 'ne':ne, 'se':se, 'sw':sw}
def __repr__(self):
return '\n'.join(self.get_map())
def is_leaf(self):
return all(q == None for q in self.quadrants.values())
def get_depth(self):
if self.is_leaf():
return 1
else:
return 1 + max(q.get_depth() if q else 0 for q in self.quadrants.values())
def get_map(self):
if self.is_leaf():
return [str(self.point)]
else:
subquadmaps = {
sqn:sq.get_map() if sq else ['.']
for sqn, sq
in self.quadrants.items()
}
subheight = max(len(map) for map in subquadmaps.values())
subwidth = max(len(mapline) for map in subquadmaps.values() for mapline in map)
subquadmapsnorm = {
sqn:normalize(sqn, sq, subwidth, subheight)
for sqn, sq
in subquadmaps.items()
}
map = []
for n in range(subheight):
map.append(subquadmapsnorm['nw'][n] + '|' + subquadmapsnorm['ne'][n])
map.append('-' * (subwidth-num_length-1) + str(self.point) + '-' * (subwidth-num_length-1))
for n in range(subheight):
map.append(subquadmapsnorm['sw'][n] + '|' + subquadmapsnorm['se'][n])
return map
def search_pqtree(q, p, is_find_only=True):
if q is None:
return
if q.point == p:
if is_find_only:
return q
else:
return
dx,dy = 0,0
if p.x >= q.point.x:
dx = 1
if p.y >= q.point.y:
dy = 1
qnum = dx+dy*2
child = [q.quadrants['sw'], q.quadrants['se'], q.quadrants['nw'], q.quadrants['ne']][qnum]
if child is None and not is_find_only:
return q
return search_pqtree(child, p, is_find_only)
def insert_pqtree(q, p):
n = search_pqtree(q, p, False)
node = PQuadTreeNode(point=p)
if p.x < n.point.x and p.y < n.point.y:
n.quadrants['sw'] = node
elif p.x < n.point.x and p.y >= n.point.y:
n.quadrants['nw'] = node
elif p.x >= n.point.x and p.y < n.point.y:
n.quadrants['se'] = node
else:
n.quadrants['ne'] = node
def pointquadtree(data):
root = PQuadTreeNode(point = data[0])
for p in data[1:]:
insert_pqtree(root, p)
return root
#Test
data1 = [ (2,2), (0,5), (8,0), (9,8), (7,14), (13,12), (14,13) ]
points = [Point(d[0], d[1]) for d in data1]
q = pointquadtree(points)
print(q)
With your example data:
| | .|(14,13)
| |----(13,12)----
| ( 7,14)| .|.
|------------( 9, 8)------------
| .|.
| |
( 0, 5)| |
----------------------------( 2, 2)----------------------------
.|( 8, 0)
|
|
|
|
|
|
Tell me if you find it useful !

Filling a Triangle in Python

I have a python program where the object is to have the user enter the length of the sides of a triangle, if they would like to fill it, and what color. I keep getting an attribute error pertaining to GeometricObject and fill.
After a few fixes I am stuck in the same area again (fill), this is the error I keep getting:
> Traceback (most recent call last): File "Desktop/one.py", line 81,
> in <module>
> main() File "Desktop/one.py", line 78, in main
> "Is filled? : ", triangle.ColorFill()) File "Desktop/one.py", line 21, in ColorFill
> return self.__filled AttributeError: 'Triangle' object has no attribute '_GeometricObject__filled'
I am not sure where to untangle this, any help would be welcomed. Below is my code:
12.1 The Triangle class
import math
#class for the GeometricObject, must be defined before Trianble. includes fill
class GeometricObject:
#if true the color will be green
def __init__(self, color =" ", filled = True):
self.__color = color
self__filled = filled
#getting the fill color and setting it to green fill
def getColor(self):
return self.__color
def setColor(self, color):
self.__color = color
def ColorFill(self):
return self.__filled
def setColorFill(self, filled):
self__filled = filled
#string that tells you if it is filled or not
def __st__(self):
return "Color is: " + (self.__color) \
+ "Fill: " + str(self.__filled)
#setting class for triangle and calling on geometric objects
class Triangle(GeometricObject):
def __init__(self, side1 = 1.0, side2 = 1.0, side3 = 1.0):
self.__side1 = side1
self.__side2 = side2
self.__side3 = side3
#calling GeometricObject, this is what triangle is bound to
GeometricObject.__init__(self)
#getting length of sides
def getLengthA(self):
return side1
def getLengthB(self):
return side2
def getLengthC(self):
return side3
#setting and getting perimeter
def setPerimeter(self):
self.perimeter = perimeter
def getPerimeter(self):
perimeter = self.__side1 + self.__side2 + self.__side3
return perimeter
#setting and getting area
def setArea(self):
self.area = area
def getArea(self):
s = (self.__side1 + self.__side2 + self.__side3) / 2
area = math.sqrt(s * ((s - self.__side1) * (s - self.__side2) * (s - self.__side3)))
return area
#printing string, sides
def toString(self):
return "Triangle: Side 1: " + (self.__side1)\
+ "Side 2: " + (self.__side2)\
+ "Side 3: " + (self.__side3)
def main():
#user enters sides of a triangle
side1, side2, side3 = eval(input("Enter the length of the Tiangle's sides (A, B, C): "))
triangle = Triangle(side1, side2, side3)
#user enters color
color = input("Enter a color: ")
triangle.setColor(color)
#user enter if filled
filled = eval(input("Enter 1 to fill with color \n Enter 0 for no fill: "))
ColorFill = (filled == 1)
triangle.setColorFill(ColorFill)
print("The area of the triangle is: " , triangle.getArea(), \
"The perimeter is: " , triangle.getPerimeter(), \
"The color is: ", triangle.getColor(), \
"Is filled? : ", triangle.ColorFill())
main()

Python find closest turtle via mouse click

I'm in the process of creating a minesweeper style game using a turtle-based grid setup. I need to find the closest cell within the grid and reveal the icon located under it whether that be a bomb or a number icons. I'm not looking to make it exact, I just need the mouse click to find the nearest cell in the grid even if the click isn't directly on the board. Currently my code only reveals the icon of the last turtle created on the board and then does nothing else with further clicks.
What can I do to make it recognize the real closest click and do it multiple times until the last bomb is found?
import random
import turtle
import cell
class Game:
def __init__(self, size):
registershapes()
self.__boardsize = size
self.__boardlist = []
self.__bombnum = 0
self.__probe = 0
self.__probelist = []
offset = (size-1) * 17
for x in range(size):
for y in range(size):
t = cell.Cell(x,y)
t.up()
t.shape('question.gif')
t.goto(y*34-offset, offset-x*34)
self.__boardlist.append(t)
def hideMines(self, num):
if num > self.__boardsize ** 2:
return False
self.__bombnum = num
self.__rnums = []
i = 0
while i < self.__bombnum:
currentnum = random.randrange(0, (self.__boardsize**2) - 1)
if currentnum not in self.__rnums:
self.__rnums.append(currentnum)
i += 1
return True
def probe(self, x, y):
for t in self.__boardlist:
pos = t.position()
distx = abs(x - pos[0])
disty = abs(y - pos[1])
distfinal = (distx ** 2 + disty ** 2) ** 0.5
curdist = 0
if curdist < distfinal:
curdist = distfinal
closest = t
if closest in self.__probelist:
return (self.__probe, self.__bombnum)
elif closest in self.__rnums:
closest.shape("bomb.gif")
self.__bombnum -= 1
self.__probe += 1
self.__probelist.append(closest)
return (self.__probe, self.__bombnum)
else:
closest.shape("0.gif")
self.__probe += 1
self.__probelist.append(closest)
return (self.__probe, self.__bombnum)
def registershapes():
wn = turtle.Screen()
wn.register_shape('0.gif')
wn.register_shape('1.gif')
wn.register_shape('2.gif')
wn.register_shape('3.gif')
wn.register_shape('4.gif')
wn.register_shape('5.gif')
wn.register_shape('6.gif')
wn.register_shape('7.gif')
wn.register_shape('8.gif')
wn.register_shape('9.gif')
wn.register_shape('bomb.gif')
wn.register_shape('question.gif')
I believe you're approaching this problem the wrong way. You're activating screen.onclick() and trying to map it to a turtle. Instead, activate turtle.onclick() on the individual turtles, deactivating it when a turtle is selected. Then you don't have to search for the turtle in question, and not actively ignore turtles that have already been selected.
Below is my rework of your code from this and your previous question into an example you can run. I had to guess about the definition of the Cell class:
from turtle import Turtle, Screen
import random
class Cell(Turtle):
def __init__(self, number):
super().__init__("question.gif")
self.__number = number
self.penup()
def number(self):
return self.__number
class Game:
def __init__(self, size):
registershapes()
self.__boardsize = size
self.__boardlist = []
self.__bombnum = 0
self.__probe = 0
self.__rnums = None
offset = (size - 1) * 17
for y in range(size):
for x in range(size):
t = Cell(x + y * size)
t.goto(x * 34 - offset, offset - y * 34)
t.onclick(lambda x, y, self=t: closure(self))
self.__boardlist.append(t)
def hideMines(self, num):
if num > self.__boardsize ** 2:
return False
self.__bombnum = num
self.__rnums = []
i = 0
while i < self.__bombnum:
currentnum = random.randrange(0, self.__boardsize ** 2 - 1)
if currentnum not in self.__rnums:
self.__rnums.append(currentnum)
i += 1
return True
def probe(self, closest):
closest.onclick(None)
if closest.number() in self.__rnums:
closest.shape("bomb.gif")
self.__bombnum -= 1
else:
closest.shape("0.gif")
self.__probe += 1
return (self.__probe, self.__bombnum)
def registershapes():
screen.register_shape('0.gif')
# ...
screen.register_shape('bomb.gif')
screen.register_shape('question.gif')
def closure(closest):
_, rem = mine.probe(closest)
if rem == 0:
over = screen.textinput("Text Input", "Would you like to play again? (Y)es or (N)o")
if over.upper() == 'Y':
main()
else:
screen.bye()
def main():
global mine
board = screen.numinput("Numeric Input", "Enter desired board size: ")
mine = Game(int(board))
nummine = screen.numinput("Numeric Input", "Enter desired number of mines: ")
mine.hideMines(int(nummine))
screen = Screen()
mine = None
main()
screen.mainloop()

How to maintain step animation

I am trying to make a program:
simulates a random walk
animates the walk using cs1graphics
starts on center block which is black
takes random steps, never been stepped on turns red, repeat step turns blue.
import random
from cs1graphics import *
from time import sleep
def animationWalk(walk):
print("Animation of Random Walk: ", end = "\n")
window = Canvas(250, 250)
window.setTitle('Random Walk in Manhattan')
for y in range(10) :
for x in range(10) :
cue = Square()
cue.setSize(50)
cue.moveTo(x*25, y*25)
window.add(cue)
(x,y)= (6,6)
squares = Square()
squares.setSize(25)
squares.moveTo((x*25)-14, (y*25)-13)
squares.setFillColor('black')
window.add(squares)
been = Square()
been.setSize(25)
been.moveTo((x*25)-14, (y*25)-13)
window.add(been)
for direction in range (len(walk)):
if walk[direction] == 'N':
#y -= 1
(x,y)=(x,y-1)
elif walk[direction] == 'E':
#x += 1
(x,y)=(x+1,y)
elif walk[direction] == 'S':
#y += 1
(x,y) =(x,y+1)
elif walk[direction] == 'W':
#x -= 1
(x,y) = (x-1,y)
been.setSize(25)
been.moveTo((x*25)-14, (y*25)-13)
been.setFillColor('red')
cue.setSize(25)
sleep(0.25)
cue.moveTo((x*25)-14, (y*25)-13)
cue.setFillColor('blue')
def randomWalk(x,y):
block = []
for i in range (x):
block.append([])
for i in block:
for j in range(y):
i.append(0)
position = (x//2, y//2)
h = position[0]
v = position[1]
walk = ''
block[h][v] += 1
while (h != -1) and (h != (x-1)) and (v != -1) and (v != (y-1)):
directions = random.randrange(1,5)
if directions == 1:
v += 1
walk = walk + 'E'
elif directions == 2:
h += 1
walk = walk + 'S'
elif directions == 3:
v -= 1
walk = walk + 'W'
elif directions == 4:
h -= 1
walk = walk + 'N'
block[h][v] += 1
print("Starting at Center (", x//2, ",", y//2, ")")
print("Walking Directions: ", walk)
print("Track of Random Walk:", end = "\n")
for entry in block:
print(entry)
animationWalk(walk)
def main(): ## define main program
x = 10
y = 10
randomWalk(x,y)
main()
The color of a square is supposed to change to red when it is visited, blue when it is revisited and is changed back to red when it is passed. I cant get the blocks to maintain the color after it has been stepped off of.

Categories