Skip to content

Instantly share code, notes, and snippets.

@PM2Ring
Created October 2, 2017 12:27
Show Gist options
  • Save PM2Ring/c5cceea0115b593148758174b8472153 to your computer and use it in GitHub Desktop.
Save PM2Ring/c5cceea0115b593148758174b8472153 to your computer and use it in GitHub Desktop.
Fitting an ellipse into an isoceles triangle
#! /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()
@PM2Ring
Copy link
Author

PM2Ring commented Oct 2, 2017

Click anywhere to draw a triangle, then use the slider to select the point where the ellipse touches the sides of the triangle.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment