from tkinter import * import math from R2Graph import * SCALEX = 40 SCALEY = SCALEX def main(): root = Tk() root.title("Triangle") root.geometry("800x600") panel = Frame(root) drawArea = Canvas(root, bg="white") panel.pack(side=TOP, fill=X) drawButton = Button(panel, text="Draw") clearButton = Button(panel, text="Clear") drawButton.pack(side=LEFT, padx=4, pady=4) clearButton.pack(side=LEFT, padx=4, pady=4) drawArea.pack(side=TOP, fill=BOTH, expand=True, padx=4, pady=4) root.update() points = [] objectIDs = [] # List of draphic objects drawn showTriangle = False bisectors = [] # Point of intersection of i-th bisector # with an edge of triangle center = R2Point() radius = 0.0 def map(t): '''Map mathematical coordinates into pixel coordinates R2Point --> (x, y)''' w = drawArea.winfo_width() h = drawArea.winfo_height() ox = w/2. oy = h/2. return (ox + t.x*SCALEX, oy - t.y*SCALEY) def invmap(p): '''Map pixel coordinates into mathematical coordinates (x, y) --> R2Point''' w = drawArea.winfo_width() h = drawArea.winfo_height() ox = w/2. oy = h/2. x = float(p[0] - ox)/SCALEX y = float(oy - p[1])/SCALEY return R2Point(x, y) def xmin(): w = drawArea.winfo_width() return (-w/2.)/SCALEX def xmax(): w = drawArea.winfo_width() return (w/2.)/SCALEX def ymin(): h = drawArea.winfo_height() return (-h/2.)/SCALEY def ymax(): h = drawArea.winfo_height() return (h/2.)/SCALEY def drawGrid(): '''Draw the coordinate system''' x0 = xmin(); x1 = xmax() y0 = ymin(); y1 = ymax() # Grid for x in range(int(x0), int(x1) + 1): if x == 0: continue drawArea.create_line( map(R2Point(x, y0)), map(R2Point(x, y1)), fill="lightGray" ) for y in range(int(y0), int(y1) + 1): if y == 0: continue drawArea.create_line( map(R2Point(x0, y)), map(R2Point(x1, y)), fill="lightGray" ) # Coordinate axes drawArea.create_line( map(R2Point(x0, 0.)), map(R2Point(x1, 0.)), fill="black", width=2 ) drawArea.create_line( map(R2Point(0., y0)), map(R2Point(0., y1)), fill="black", width=2 ) def drawTriangle(): '''Draw a triangle, its bisectors and inscribed circle''' # print("drawTriangle") if len(points) < 3 or not showTriangle: return line = [map(points[0])] line.append(map(points[1])) line.append(map(points[2])) line.append(map(points[0])) id = drawArea.create_line(line, fill="blue", width=3) objectIDs.append(id) # Draw bisectors for i in range(3): id = drawArea.create_line( map(points[i]), map(bisectors[i]), fill="darkGreen", width=2 ) objectIDs.append(id) # Draw inscribed circle id = drawArea.create_oval( map(center - R2Vector(radius, radius)), map(center + R2Vector(radius, radius)), fill=None, outline="red", width=3 ) objectIDs.append(id) # Draw center of the circle id = drawArea.create_oval( map(center - R2Vector(0.1, 0.1)), map(center + R2Vector(0.1, 0.1)), fill="red", outline="red", width=1 ) objectIDs.append(id) def drawPoints(): for t in points: drawPoint(t) def drawPoint(t): dx = R2Vector(0.2, 0.) id = drawArea.create_line( map(t - dx), map(t + dx), fill="red", width=3 ) objectIDs.append(id) dy = R2Vector(0., 0.2) id = drawArea.create_line( map(t - dy), map(t + dy), fill="red", width=3 ) objectIDs.append(id) def computeTriangle(): '''Compute bisectors and inscribed circle of triangle''' nonlocal bisectors, center, radius assert (len(points) >= 3) (bisectors, center, radius) = computeInscribedCircle(points) def onDraw(): '''Handle pressing the "Draw" button''' nonlocal showTriangle if len(points) >= 3: computeTriangle() showTriangle = True drawTriangle() def clearPicture(): '''Erase a picture''' nonlocal showTriangle for id in objectIDs: drawArea.delete(id) def onClear(): '''Handle pressing the "Clear" button''' nonlocal showTriangle points.clear() clearPicture() showTriangle = False drawButton.configure(command = onDraw) clearButton.configure(command = onClear) def onButtonRelease(e): p = (e.x, e.y) t = invmap(p) # print("Adding a new point") if len(points) >= 3: onClear() points.append(t) drawPoint(t) def onConfigure(e): drawArea.delete("all") drawGrid() drawPicture() def drawPicture(): drawPoints() if showTriangle: drawTriangle() def redrawPicture(): clearPicture() drawPicture() drawArea.bind("", onButtonRelease) drawArea.bind("", onConfigure) drawGrid() root.mainloop() def computeInscribedCircle(vertices): assert(len(vertices) >= 3) # Compute bisectors bisectors = [] for i in range(3): # Compute bisector of i-th vertex iPrev = (i - 1) if i > 0 else 2 iNext = (i + 1) if i < 2 else 0 vNext = vertices[iNext] - vertices[i] vNext.normalize() vPrev = vertices[iPrev] - vertices[i] vPrev.normalize() vBisector = vNext + vPrev (_, bisector) = intersectLines( vertices[i], vBisector, vertices[iNext], vertices[iPrev] - vertices[iNext] ) bisectors.append(bisector) (_, center) = intersectLines( vertices[0], bisectors[0] - vertices[0], vertices[1], bisectors[1] - vertices[1] ) radius = center.distanceToLine( vertices[0], vertices[1] - vertices[0] ) return (bisectors, center, radius) if __name__ == "__main__": main()