Setup¶
In [1]:
from matplotlib.animation import FuncAnimation as FA
from matplotlib import colormaps as cm
import matplotlib.colors as mCm
from matplotlib.patches import Polygon as mpP
from matplotlib.patches import Rectangle as mpR
import matplotlib.pyplot as plt
from math import inf as INF
from math import pi as pi
from math import sqrt as mS
import numpy as np
import pandas as pd
import seaborn as sns
Constants & Functions¶
In [6]:
#Height of unit equilateral triangle.
H = mS(3) / 2
#Equilateral Triangle at origin.
T = [[0, 0], [1, 0], [0.5, H], [0, 0]]
#Area of equilateral triangle.
A = H / 2
#'Random' internal point.
R = [[0.68, 0.28]]
def getViridis(n):
"""
Get a viridis color sequence.
Parameters:
n - Number of colors.
Returns:
List of n hex codes as strings.
"""
return([mCm.to_hex(cm['viridis_r'].resampled(n).colors[v]) for v in range(n)])
colors = getViridis(7)
def clearAx(ax):
"""
Clear axes.
Parameters:
ax - Current ax.
Returns:
ax - Updated ax.
"""
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.spines["bottom"].set_visible(False)
ax.spines["left"].set_visible(False)
ax.set_xticks([])
ax.set_yticks([])
return(ax)
def drawTriangle(xy) :
"""
Draw an equilateral triangle.
Parameters:
xy - List of 3 tuples that correspond to points (x,y).
Returns:
Polygon patch.
"""
return(mpP(xy,
edgecolor = 'k',
facecolor = 'None',
lw = 2,
zorder = 2))
def plotFiddler() :
"""
Plot a quadrilateral from an equilateral triangle.
Parameters:
<None>
Returns:
<None> - Plot.
"""
fig = plt.figure(figsize = (12, 4.5))
ax = fig.add_subplot(xlim = (-0.25, 3.75),
ylim = (-0.317, 1.183))
fig.subplots_adjust(left = 0, bottom = 0, right = 1, top = 1)
#Remove axes and ticks.
ax = clearAx(ax)
triangle = T.copy()
XYs = [triangle[0]] + R + triangle[1:]
for i in [1,2,3] :
#Negative from original triangle.
ax.add_patch(mpP(triangle,
ec = None,
fc = '#CCCCCCCC',
zorder = 1))
#Quadrilateral.
ax.add_patch(mpP(XYs,
ec = None,
fc = colors[2*i-1],
zorder = 2))
#Shift next triangle horizontally.
triangle = [[t[0]+1.25, t[1]] for t in triangle]
XYs = [[xy[0]+1.25, xy[1]] for xy in XYs]
#Shift where internal point is in order.
XYs = XYs[0:i] + [XYs[i+1]] + [XYs[i]] + XYs[i+2:]
fig.savefig("2026.10.23Fiddler1.png", bbox_inches = 'tight')
def plotEC(o) :
"""
Analysis.
Parameters:
o - Option.
A or B for analysis.
0 - 900 for animation
Returns:
<None> - Plot.
"""
#fig = plt.figure(figsize = (7, 7))
ax = fig.add_subplot(xlim = (-0.25, 1.25),
ylim = (-0.317, 1.183))
fig.subplots_adjust(left = 0, bottom = 0, right = 1, top = 1)
#Remove axes and ticks.
ax = clearAx(ax)
if o == 'A' :
for i in [0,1,2] :
triangle = [T[i]] + R + [T[i+1]] + [T[i]]
ax.add_patch(mpP(triangle,
ec = None,
fc = colors[2*i+1],
zorder = 1))
fig.savefig("2026.10.23Fiddler2.png", bbox_inches = 'tight')
elif o == 'B' :
ax.add_patch(mpP(T,
ec = None,
fc = colors[4],
zorder = 1))
ax.scatter(0.5, H/3,
c = 'k',
lw = 5)
for xy in [[[1/6, 5/6], [H/3, H/3]],
[[1/3, 2/3], [0, 2*H/3]],
[[2/3, 1/3], [0, 2*H/3]]]:
ax.plot(xy[0],
xy[1],
c = 'k',
lw = 2,
ls = ":",
zorder = 2)
fig.savefig("2026.10.23EC.png", bbox_inches = 'tight')
else :
x = (o+225) / 1350
#Triangle sides.
a = 1
b = 1
c = 1
#Vertices to random internal point.
dA = getDist(x, H/3, 0.5, H)
dB = getDist(0, 0, x, H/3)
dC = getDist(x, H/3, 1, 0)
#Areas.
areaA = heron(a, dB, dC)
areaB = heron(b, dC, dA)
areaC = heron(c, dA, dB)
tt = [[areaA, (0.5, -0.1), 'center', 0],
[areaB, (0.75, H/2), 'left', -60],
[areaC, (0.25, H/2), 'right', 60]]
for i in [0,1,2] :
triangle = [T[i]] + [[x, H/3]] + [T[i+1]] + [T[i]]
ax.add_patch(mpP(triangle,
ec = None,
fc = colors[2*i+1],
zorder = 1))
ax.annotate(text = "{:.4f}".format(tt[i][0]),
xy = tt[i][1],
c = colors[2*i+1],
size = 36,
ha = tt[i][2],
va = 'center',
rotation = tt[i][3])
def getDist(x1, y1, x2, y2) :
return(mS((x2-x1)**2 + (y2-y1)**2))
def heron(a, b, c) :
s = (a+b+c)/2
A2 = s * (s-a) * (s-b) * (s-c)
if A2 < 0 :
return(0)
else :
return(mS(A2))
In [3]:
plotFiddler()
In [4]:
plotEC('A')
In [5]:
plotEC('B')
In [7]:
#Animate
sns.set()
fig = plt.figure(figsize = (7, 7))
def animate(i):
plt.clf()
plotEC(i)
#Run animation.
anim = FA(fig, animate, frames = 900, interval = 15)
#Save animation.
anim.save('2026.10.23EC.mp4');