The logic in my code is not absolute. I tried to find the center coordinates and radius of bounding circle by taking the diameter end points as 1) the point farthest from origin and 2) the point farthest away from (1).
But this is does not give me an actual result in all cases.
pls dont mind indendation (I use tabs and copied my code.)
from tkinter import *
class point2D:
def __init__(self, x=0, y=0):
self.__x = x
self.__y = y
def x(self):
return self.__x
def y(self):
return self.__y
def setx(self, x):
self.__x = x
def sety(self, y):
self.__y = y
def __str__(self):
return f"({self.__x}, {self.__y})"
def distance(self, other):
return ((self.__x-other.__x)**2 + (self.__y-other.__y)**2) ** 0.5
class box:
def __init__(self):
root = Tk()
root.title("Bounding Circle")
width, height = 600, 600
root.geometry(f"{width}x{height}")
self.canvas = Canvas(root, width=width, height=height, bg="white")
self.canvas.pack()
self.canvas.bind("<Button-1>", self.addpoint)
self.origin = point2D()
self.points = []
root.mainloop()
def addpoint(self, event):
radius = 5
self.canvas.create_oval(event.x - radius, event.y - radius,
event.x + radius, event.y + radius, fill = "black")
self.points.append(point2D(event.x, event.y))
self.getboundingcircle()
def getboundingcircle(self):
if len(self.points) == (1 or 0):
return
#Finding the point farthest from origin (0,0 in canvas)
d, start = self.points[0].distance(self.origin), 0
for i in range(1, len(self.points)):
x = self.points[i].distance(self.origin)
if x > d:
d = x
start = i
#Finding the point farthest from points[start]
d, end = 0, 0
for i in range(len(self.points)):
if i == start:
continue
x = self.points[i].distance(self.points[start])
if x > d:
d = x
end = i
center_x = (self.points[start].x() + self. points[end].x()) / 2
center_y = (self.points[start].y() + self. points[end].y()) / 2
radius = self.points[start].distance(self.points[end]) / 2
self.canvas.delete("circle")
self.canvas.create_oval(center_x - radius, center_y - radius,
center_x + radius, center_y + radius, outline = "red", tags = "circle")
box()
Adwaidh Chandran A is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
3
Here is an example that you could adapt (I’ve based it to include your point2D
class, but I’ve also used numpy for simplicity with commented out equivalents if numpy can’t be used):
class point2D:
def __init__(self, x=0, y=0):
self.__x = x
self.__y = y
def x(self):
return self.__x
def y(self):
return self.__y
def setx(self, x):
self.__x = x
def sety(self, y):
self.__y = y
def __str__(self):
return f"({self.__x}, {self.__y})"
def distance(self, other):
return ((self.__x-other.__x)**2 + (self.__y-other.__y)**2) ** 0.5
class points:
"""
Class to store multiple point2D objects.
"""
def __init__(self):
self._points = []
def append(self, point: point2D):
if not isinstance(point, point2D):
raise TypeError("point must be a point2D object")
self._points.append(point)
@property
def x(self):
return np.array([p.x() for p in self._points])
# return [p.x() for p in self._points]
@property
def y(self):
return np.array([p.y() for p in self._points])
# return [p.y() for p in self._points]
@property
def xy(self):
return np.vstack((self.x, self.y)).T
# return [(x, y) for x, y in zip(self.x, self.y)]
@property
def mean(self):
# return x, y coordinates of the mean of all point
return self.xy.mean(axis=0)
# return [sum(self.x) / len(self), sum(self.y) / len(self)]
@property
def bounding_radius(self):
# return the maximum distance of all points from the mean
return np.max(np.linalg.norm(self.xy - self.mean, axis=1))
# m = self.mean
# r = 0
# for rv in [((xy[0] - m[0])**2 + (xy[1] - m[1])**2)**0.5 for xy in self.xy]:
# if rv > r:
# r = rv
# return r
def __len__(self):
return len(self._points)
from matplotlib import pyplot as plt
from matplotlib.patches import Circle
# add some points
p = points()
p.append(point2D(3, 4))
p.append(point2D(-1, -5))
p.append(point2D(-0.4, 3.2))
p.append(point2D(5.4, -2.1))
# get the mean and maximum distance from the mean
m = p.mean
r = p.bounding_radius
# in your code you could have
# center_x = m[0]
# center_y = m[1]
# radius = p.bounding_radius
fig, ax = plt.subplots()
ax.scatter(p.x, p.y)
ax.plot(m[0], m[1], 'kx')
ax.add_artist(Circle(m, r, ec="r", fc=None, fill=False))
ax.set_xlim([m[0] - r, m[0] + r])
ax.set_ylim([m[1] - r, m[1] + r])
ax.set_aspect("equal")
3
I think may be modifying the getboundingcircle
function inside the box class may do what you want. I don understand well if this is the expected behavior but it’s one possible behavior.
You should import math at the beginning:
from tkinter import *
import math
And then replace your current function:
def getboundingcircle(self):
if len(self.points) < 2:
return
# Find the two farthest points
max_dist = 0
start, end = 0, 1 # Initialize start and end
for i in range(len(self.points)):
for j in range(i + 1, len(self.points)):
dist = self.points[i].distance(self.points[j])
if dist > max_dist:
max_dist = dist
start = i
end = j
center_x = (self.points[start].x() + self.points[end].x()) / 2
center_y = (self.points[start].y() + self.points[end].y()) / 2
radius = self.points[start].distance(self.points[end]) / 2
self.canvas.delete("circle")
self.canvas.create_oval(center_x - radius, center_y - radius,
center_x + radius, center_y + radius,
outline="red", tags="circle")
1