Created
October 2, 2017 12:27
-
-
Save PM2Ring/c5cceea0115b593148758174b8472153 to your computer and use it in GitHub Desktop.
Fitting an ellipse into an isoceles triangle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python3 | |
''' iso_ellipse. An ellipse that touches an isoceles triangle at the | |
midpoint of its base and at a given distance on the sides. | |
The apex of the triangle is at the origin, with the triangle's axis | |
on the +X axis. The slopes of the equal sides are m and -m, | |
the "height" is V, so the other 2 vertices are at (V, mV) and (V, -mV). | |
We want an ellipse that's tangential to the triangle at (X, Y) and (X, -Y) | |
where Y = mX. We also want the right vertex of the ellipse at (V, 0) | |
Let the parametric equations of the ellipse be | |
x = c + a cos t | |
y = b sin t | |
The right vertex is at t = 0, so V = c + a, i.e., | |
a = V - c | |
For all t, dy/dx = (b cos t) / (-a sin t) | |
Let t = T at the point of tangency. | |
When t = T, y / x = dy / dx = Y / X = m | |
Thus y * dx = x * dy | |
(b sin T)(-a sin T) = (c + a cos T)(b cos T) | |
-ab sin^2 T = bc cos T + ab cos^2 T | |
-bc cos T = ab(sin^2 T + cos^2 T) | |
b == 0 is a degenerate solution: a horizontal line on the X axis. | |
So assuming b > 0, | |
-c cos T = a | |
cos T = -a / c | |
sin^2 T = 1 - cos^2 | |
= (c^2 - a^2) / c^2 | |
Let g^2 = c^2 - a^2. Then | |
sin T = g / c | |
m = dy / dx = (b cos T) / (-a sin T) | |
= (b * -a / c) / (-a * g / c) | |
m = b / g | |
b = mg | |
X = c + a cos T = c - a^2 / c | |
= (c^2 - a^2) / c | |
X = g^2 / c | |
Y = b sin T = bg / c = mX | |
c^2 = a^2 + g^2 | |
c^2 = (V - c)^2 + Xc | |
c^2 = V^2 + c^2 -2Vc + Xc | |
(2V - X)c = V^2 | |
c = V^2 / (2V - X) | |
c = V / (2 - X / V) | |
Written by PM 2Ring 2017.09.25 | |
''' | |
import tkinter as tk | |
from math import sqrt | |
class Gui: | |
def __init__(self): | |
self.root = root = tk.Tk() | |
w, h = root.winfo_screenwidth(), root.winfo_screenheight() | |
root.geometry(f'{w}x{h}') | |
root.title('Iso Ellipse') | |
self.canvas = canvas = tk.Canvas(root) | |
canvas.pack(fill='both', expand=True, anchor='n') | |
canvas.bind("<Button-1>", self.on_button) | |
# A Scale to control the point of tangency | |
res = 0.001 | |
self.scale = tk.Scale(root, from_=res, to=1 - res, resolution=res, | |
relief=tk.SUNKEN, orient=tk.HORIZONTAL, command=self.on_scale) | |
self.scale.pack(fill='x', anchor='s') | |
self.scale_value = 0.5 | |
self.scale.set(self.scale_value) | |
# Draw axes | |
self.ox, self.oy = 0, h // 2 | |
canvas.create_line((0, self.oy, w, self.oy)) | |
canvas.create_line((self.ox, 20, self.ox, h - 20)) | |
self.wx = self.wy = self.triangle = self.ellipse = None | |
root.mainloop() | |
def on_scale(self, value): | |
value = float(value) | |
if value == self.scale_value: | |
return | |
self.scale_value = value | |
self.draw() | |
def on_button(self, event): | |
self.wx, self.wy = event.x, event.y | |
self.draw() | |
def draw_ellipse(self, x, y, v): | |
ox, oy = self.ox, self.oy | |
canvas = self.canvas | |
if self.ellipse: | |
canvas.delete(self.ellipse) | |
# Compute ellipse parameters. See above for details | |
c = v / (2 - x / v) | |
a = v - c | |
b = sqrt(x * c) * y / x | |
bbox = ox + c - a, oy - b, ox + c + a, oy + b | |
self.ellipse = canvas.create_oval(bbox, outline='blue', fill='') | |
def draw_triangle(self, v, h): | |
ox, oy = self.ox, self.oy | |
canvas = self.canvas | |
if self.triangle: | |
canvas.delete(self.triangle) | |
points = ox, oy, ox + v, oy + h, ox + v, oy - h | |
self.triangle = canvas.create_polygon(points, outline='red', fill='') | |
def draw(self): | |
if self.wx is None: | |
return | |
v = self.wx - self.ox | |
if v <= 0: | |
return | |
h = abs(self.wy - self.oy) | |
self.draw_triangle(v, h) | |
r = self.scale_value | |
self.draw_ellipse(r * v, r * h, v) | |
Gui() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Click anywhere to draw a triangle, then use the slider to select the point where the ellipse touches the sides of the triangle.