Spaces:
Runtime error
Runtime error
Commit ·
c1ab7e8
1
Parent(s): 1c2de4d
Added Batman logo, transformation types and updated description (#2)
Browse files- Added Batman logo, transformation types and updated description (76079d11d9242949899984cbb7193a99bb72da7e)
Co-authored-by: Satyam Bhardwaj <bhardwajsatyam@users.noreply.huggingface.co>
- app.py +88 -24
- description.md +9 -1
- utils.py +104 -3
app.py
CHANGED
|
@@ -2,7 +2,11 @@ from matplotlib import pyplot as plt
|
|
| 2 |
import numpy as np
|
| 3 |
import streamlit as st
|
| 4 |
import pandas as pd
|
| 5 |
-
from utils import getSquareYVectorised, getCircle, transform, plotGridLines
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
np.set_printoptions(precision=3)
|
| 8 |
xlim = (-10,10)
|
|
@@ -13,50 +17,110 @@ st.write(
|
|
| 13 |
"This app shows the effect of a 2x2 linear transformation on simple shapes to understand the role of eigenvectors and eigenvalues in quantifying the nature of a transformation.")
|
| 14 |
|
| 15 |
with st.sidebar:
|
| 16 |
-
data = st.selectbox('Select type of dataset', ['Square', 'Circle'])
|
|
|
|
|
|
|
|
|
|
| 17 |
st.write("---")
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
st.write("---")
|
| 28 |
st.write("The transformation matrix A is:")
|
| 29 |
st.table(pd.DataFrame(t))
|
| 30 |
st.write("---")
|
| 31 |
-
showNormalSpace = st.checkbox(label= 'Show
|
| 32 |
|
| 33 |
-
|
| 34 |
|
| 35 |
-
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
-
|
| 39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
| 41 |
evl, evec = np.linalg.eig(t)
|
| 42 |
-
det = np.linalg.det(t)
|
| 43 |
fig, ax = plt.subplots()
|
| 44 |
|
| 45 |
if showNormalSpace:
|
| 46 |
-
|
| 47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
if not np.iscomplex(evec).any():
|
| 49 |
ax.quiver(0,0,evec[0,0],evec[1,0],scale=1,scale_units ='xy',angles='xy', facecolor='black', alpha=0.5)
|
| 50 |
ax.quiver(0,0,evec[0,1],evec[1,1],scale=1,scale_units ='xy',angles='xy', facecolor='black', alpha=0.5)
|
| 51 |
plotGridLines(xlim,ylim,np.array([[1,0], [0,1]]),'#9D9D9D','Normal Space',0.4)
|
| 52 |
|
| 53 |
-
|
| 54 |
-
ax.plot(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
if not (np.iscomplex(evl).any() or np.iscomplex(evec).any()):
|
| 56 |
ax.quiver(0,0,evec[0,0]*evl[0],evec[1,0]*evl[0],scale=1,scale_units ='xy',angles='xy', facecolor='cyan', label='$eigen\ vector_{\lambda_0}$')
|
| 57 |
ax.quiver(0,0,evec[0,1]*evl[1],evec[1,1]*evl[1],scale=1,scale_units ='xy',angles='xy', facecolor='blue', label='$eigen\ vector_{\lambda_1}$')
|
| 58 |
plotGridLines(xlim,ylim,t,'#403B3B','Transformed space',0.6)
|
| 59 |
-
ax.text(11,
|
|
|
|
|
|
|
|
|
|
| 60 |
|
| 61 |
ax.set_xlim(*xlim)
|
| 62 |
ax.set_ylim(*ylim)
|
|
@@ -70,7 +134,7 @@ st.pyplot(fig)
|
|
| 70 |
|
| 71 |
df = pd.DataFrame({'Eigenvalues': evl, 'Eigenvectors': [str(evec[:,0]), str(evec[:,1])],\
|
| 72 |
'Transformed Eigenvectors': [str(evec[:,0]*evl[0]), str(evec[:,1]*evl[1])]})
|
| 73 |
-
st.table(df)
|
| 74 |
|
| 75 |
if np.iscomplex(evl).any() or np.iscomplex(evec).any():
|
| 76 |
st.write("Due to complex eigenvectors and eigenvalues, the transformed eigenvectors are not\
|
|
|
|
| 2 |
import numpy as np
|
| 3 |
import streamlit as st
|
| 4 |
import pandas as pd
|
| 5 |
+
from utils import getSquareYVectorised, getCircle, getBatman, transform, plotGridLines, discriminant
|
| 6 |
+
|
| 7 |
+
minv = -5.0
|
| 8 |
+
maxv = 5.0
|
| 9 |
+
step = 0.1
|
| 10 |
|
| 11 |
np.set_printoptions(precision=3)
|
| 12 |
xlim = (-10,10)
|
|
|
|
| 17 |
"This app shows the effect of a 2x2 linear transformation on simple shapes to understand the role of eigenvectors and eigenvalues in quantifying the nature of a transformation.")
|
| 18 |
|
| 19 |
with st.sidebar:
|
| 20 |
+
data = st.selectbox('Select type of dataset', ['Square', 'Circle', 'Batman'])
|
| 21 |
+
if data == 'Batman':
|
| 22 |
+
black = st.checkbox(label='Black')
|
| 23 |
+
transform_type = st.selectbox('Select type of transformation', ['Custom', 'Stretch', 'Shear', 'Rotate'])
|
| 24 |
st.write("---")
|
| 25 |
+
if transform_type == 'Custom':
|
| 26 |
+
st.markdown("Select elements of transformation matrix $A$")
|
| 27 |
+
a_00 = st.slider(label = '$a_{00}$', min_value = minv, max_value=maxv, value=1.0, step=step)
|
| 28 |
+
a_01 = st.slider(label = '$a_{01}$', min_value = minv, max_value=maxv, value=0.0, step=step)
|
| 29 |
+
a_10 = st.slider(label = '$a_{10}$', min_value = minv, max_value=maxv, value=0.0, step=step)
|
| 30 |
+
a_11 = st.slider(label = '$a_{11}$', min_value = minv, max_value=maxv, value=1.0, step=step)
|
| 31 |
+
t = np.array([[a_00, a_01], [a_10, a_11]], dtype=np.float64)
|
| 32 |
+
elif transform_type == 'Stretch':
|
| 33 |
+
both = st.checkbox('Set equal')
|
| 34 |
+
if not both:
|
| 35 |
+
stretch_x = st.slider(label = 'Stretch in x-direction', min_value = minv, max_value=maxv, value=1.0, step=step)
|
| 36 |
+
stretch_y = st.slider(label = 'Stretch in y-direction', min_value = minv, max_value=maxv, value=1.0, step=step)
|
| 37 |
+
t = np.array([[stretch_x, 0], [0, stretch_y]], dtype=np.float64)
|
| 38 |
+
else:
|
| 39 |
+
stretch = st.slider(label = 'Scale', min_value = minv, max_value=maxv, value=1.0, step=step)
|
| 40 |
+
t = np.array([[stretch, 0], [0, stretch]], dtype=np.float64)
|
| 41 |
+
elif transform_type == 'Shear':
|
| 42 |
+
left, right = st.columns(2)
|
| 43 |
+
with left:
|
| 44 |
+
both = st.checkbox('Set equal')
|
| 45 |
+
if not both:
|
| 46 |
+
shear_x = st.slider(label = 'Shear in x-direction', min_value=minv, max_value=maxv, value=0.0, step=step)
|
| 47 |
+
shear_y = st.slider(label = 'Shear in y-direction', min_value=minv, max_value=maxv, value=0.0, step=step)
|
| 48 |
+
t = np.array([[1, shear_x], [shear_y, 1]], dtype=np.float64)
|
| 49 |
+
else:
|
| 50 |
+
with right:
|
| 51 |
+
sign = st.checkbox('Opposite sign')
|
| 52 |
+
shear = st.slider(label = 'Shear in both directions', min_value=minv, max_value=maxv, value=0.0, step=step)
|
| 53 |
+
t = np.array([[1, -shear], [shear, 1]], dtype=np.float64) if sign else np.array([[1, shear], [shear, 1]], dtype=np.float64)
|
| 54 |
+
else:
|
| 55 |
+
st.markdown("Rotate by $\\theta$ in anti-clockwise\ndirection")
|
| 56 |
+
min_theta = -180.0
|
| 57 |
+
max_theta = 180.0
|
| 58 |
+
theta = st.slider(label = '$\\theta$', min_value=min_theta, max_value=max_theta, value=0.0, step=step, format="%f°")
|
| 59 |
+
rtheta = np.pi * theta/180.0
|
| 60 |
+
t = np.array([[np.cos(rtheta), -np.sin(rtheta)], [np.sin(rtheta), np.cos(rtheta)]], dtype=np.float64)
|
| 61 |
st.write("---")
|
| 62 |
st.write("The transformation matrix A is:")
|
| 63 |
st.table(pd.DataFrame(t))
|
| 64 |
st.write("---")
|
| 65 |
+
showNormalSpace = st.checkbox(label= 'Show original space (without transform)', value=False)
|
| 66 |
|
|
|
|
| 67 |
|
| 68 |
+
if data == 'Square':
|
| 69 |
+
x = np.linspace(-1,1,1000)
|
| 70 |
+
y = getSquareYVectorised(x)
|
| 71 |
+
elif data == 'Circle':
|
| 72 |
+
x = np.linspace(-1,1,1000)
|
| 73 |
+
y = getCircle(x)
|
| 74 |
+
else:
|
| 75 |
+
X, Y = getBatman(s=2)
|
| 76 |
|
| 77 |
+
if data != 'Batman':
|
| 78 |
+
x_dash_up, y_dash_up = transform(x,y,t)
|
| 79 |
+
x_dash_down, y_dash_down = transform(x,-y,t)
|
| 80 |
+
else:
|
| 81 |
+
tmp = [transform(x, y, t) for x, y in zip(X, Y)]
|
| 82 |
+
X_dash = [t[0] for t in tmp]
|
| 83 |
+
Y_dash = [t[1] for t in tmp]
|
| 84 |
|
| 85 |
evl, evec = np.linalg.eig(t)
|
|
|
|
| 86 |
fig, ax = plt.subplots()
|
| 87 |
|
| 88 |
if showNormalSpace:
|
| 89 |
+
if data != 'Batman':
|
| 90 |
+
ax.plot(x, y, 'r', alpha=0.5)
|
| 91 |
+
ax.plot(x, -y, 'g', alpha=0.5)
|
| 92 |
+
else:
|
| 93 |
+
for i, (x, y) in enumerate(zip(X, Y)):
|
| 94 |
+
if black:
|
| 95 |
+
ax.plot(x, y, 'k-', alpha=0.5, linewidth=1)
|
| 96 |
+
elif i < 3:
|
| 97 |
+
ax.plot(x, y, 'g-', alpha=0.5, linewidth=1)
|
| 98 |
+
else:
|
| 99 |
+
ax.plot(x, y, 'r-', alpha=0.5, linewidth=1)
|
| 100 |
if not np.iscomplex(evec).any():
|
| 101 |
ax.quiver(0,0,evec[0,0],evec[1,0],scale=1,scale_units ='xy',angles='xy', facecolor='black', alpha=0.5)
|
| 102 |
ax.quiver(0,0,evec[0,1],evec[1,1],scale=1,scale_units ='xy',angles='xy', facecolor='black', alpha=0.5)
|
| 103 |
plotGridLines(xlim,ylim,np.array([[1,0], [0,1]]),'#9D9D9D','Normal Space',0.4)
|
| 104 |
|
| 105 |
+
if data != 'Batman':
|
| 106 |
+
ax.plot(x_dash_up,y_dash_up,'r')
|
| 107 |
+
ax.plot(x_dash_down,y_dash_down, 'g')
|
| 108 |
+
else:
|
| 109 |
+
for i, (x, y) in enumerate(zip(X_dash, Y_dash)):
|
| 110 |
+
if black:
|
| 111 |
+
ax.plot(x, y, 'k-', linewidth=1)
|
| 112 |
+
elif i < 3:
|
| 113 |
+
ax.plot(x, y, 'g', linewidth=1)
|
| 114 |
+
else:
|
| 115 |
+
ax.plot(x, y, 'r', linewidth=1)
|
| 116 |
if not (np.iscomplex(evl).any() or np.iscomplex(evec).any()):
|
| 117 |
ax.quiver(0,0,evec[0,0]*evl[0],evec[1,0]*evl[0],scale=1,scale_units ='xy',angles='xy', facecolor='cyan', label='$eigen\ vector_{\lambda_0}$')
|
| 118 |
ax.quiver(0,0,evec[0,1]*evl[1],evec[1,1]*evl[1],scale=1,scale_units ='xy',angles='xy', facecolor='blue', label='$eigen\ vector_{\lambda_1}$')
|
| 119 |
plotGridLines(xlim,ylim,t,'#403B3B','Transformed space',0.6)
|
| 120 |
+
ax.text(11,3,'|A|={:.2f}'.format(np.linalg.det(t)), fontdict={'fontsize':11})
|
| 121 |
+
ax.text(11,2,'D = {:.2f}'.format(discriminant(t)), fontdict={'fontsize':11})
|
| 122 |
+
if discriminant(t) < 0:
|
| 123 |
+
ax.text(13,1,'Negative!'.format(discriminant(t)), fontdict={'fontsize':8})
|
| 124 |
|
| 125 |
ax.set_xlim(*xlim)
|
| 126 |
ax.set_ylim(*ylim)
|
|
|
|
| 134 |
|
| 135 |
df = pd.DataFrame({'Eigenvalues': evl, 'Eigenvectors': [str(evec[:,0]), str(evec[:,1])],\
|
| 136 |
'Transformed Eigenvectors': [str(evec[:,0]*evl[0]), str(evec[:,1]*evl[1])]})
|
| 137 |
+
st.table(df.style.format({'Eigenvalues':'{:.2f}'}))
|
| 138 |
|
| 139 |
if np.iscomplex(evl).any() or np.iscomplex(evec).any():
|
| 140 |
st.write("Due to complex eigenvectors and eigenvalues, the transformed eigenvectors are not\
|
description.md
CHANGED
|
@@ -39,4 +39,12 @@ When the matrix $A$ is singular, then the transformed space collapses to a line.
|
|
| 39 |
|
| 40 |
Solving for $\lambda$, we get $$ \frac{1}{2} \left(a_{00}+a_{11}\pm \sqrt{a_{00}^2-2 a_{11} a_{00}+a_{11}^2+4 a_{01} a_{10}}\right) $$
|
| 41 |
|
| 42 |
-
The quantity under the square root sign is called the Discriminant, denoted by $D$. When $D < 0$, the eigenvalues and consequently the eigenvectors are complex. On the other hand, when $A$ is symmetric $a_{01} = a_{10}$, then the discriminant is always positive and the eigendecomposition is real.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
Solving for $\lambda$, we get $$ \frac{1}{2} \left(a_{00}+a_{11}\pm \sqrt{a_{00}^2-2 a_{11} a_{00}+a_{11}^2+4 a_{01} a_{10}}\right) $$
|
| 41 |
|
| 42 |
+
The quantity under the square root sign is called the Discriminant, denoted by $D$. When $D < 0$, the eigenvalues and consequently the eigenvectors are complex. On the other hand, when $A$ is symmetric $a_{01} = a_{10}$, then the discriminant is always positive and the eigendecomposition is real.
|
| 43 |
+
|
| 44 |
+
Some common transformations include
|
| 45 |
+
|
| 46 |
+
| Name | Matrix | Explanation |
|
| 47 |
+
|:----:|:--------------------:|:-----:|
|
| 48 |
+
|Stretch |$\begin{bmatrix} s_{x} & 0 \\ 0 & s_{y} \end{bmatrix}$| Streches by $s_x$ in $x$-direction and by $s_y$ in the $y$-direction. When $s_x = s_y = s$, this is equivalent to scaling by $s$ |
|
| 49 |
+
|Shear| $\begin{bmatrix} 1 & s_{x} \\ s_{y} & 1 \end{bmatrix}$| Shears simultaneously by $s_x$ in $x$-direction and by $s_y$ in the $y$-direction. When $s_x = -s_y$, this is equivalent to rotate and scale. |
|
| 50 |
+
|Rotate| $\begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix}$| Rotation by $\theta$ in the anti-clockwise direction. Since all vectors rotate under this transformation, the eigenvalues and eigenvectors are complex. |
|
utils.py
CHANGED
|
@@ -10,10 +10,10 @@ def getSquareY(x):
|
|
| 10 |
getSquareYVectorised = np.vectorize(getSquareY)
|
| 11 |
|
| 12 |
def getCircle(x):
|
| 13 |
-
return np.sqrt(1-np.square(x))
|
| 14 |
|
| 15 |
def transform(x,y,t):
|
| 16 |
-
points = np.array([x,y])
|
| 17 |
result = t @ points
|
| 18 |
return result[0,:], result[1,:]
|
| 19 |
|
|
@@ -30,4 +30,105 @@ def plotGridLines(xlim,ylim,t,color,label,linewidth):
|
|
| 30 |
y = [i,i]
|
| 31 |
x = [xlim[0]-20,xlim[1]+20]
|
| 32 |
x,y = transform(x,y,t)
|
| 33 |
-
plt.plot(x,y, color=color,linestyle='dashed',linewidth=linewidth)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
getSquareYVectorised = np.vectorize(getSquareY)
|
| 11 |
|
| 12 |
def getCircle(x):
|
| 13 |
+
return np.sqrt(1 - np.square(x))
|
| 14 |
|
| 15 |
def transform(x,y,t):
|
| 16 |
+
points = np.array([x, y])
|
| 17 |
result = t @ points
|
| 18 |
return result[0,:], result[1,:]
|
| 19 |
|
|
|
|
| 30 |
y = [i,i]
|
| 31 |
x = [xlim[0]-20,xlim[1]+20]
|
| 32 |
x,y = transform(x,y,t)
|
| 33 |
+
plt.plot(x,y, color=color,linestyle='dashed',linewidth=linewidth)
|
| 34 |
+
|
| 35 |
+
def discriminant(t):
|
| 36 |
+
return t[0,0]**2 - 2*t[1,1]*t[0,0] + t[1,1]**2 + 4*t[0,1]*t[1,0]
|
| 37 |
+
|
| 38 |
+
def getBatman(s=2):
|
| 39 |
+
X = []
|
| 40 |
+
Y = []
|
| 41 |
+
|
| 42 |
+
# lower
|
| 43 |
+
x = np.linspace(-4, 4, 1600)
|
| 44 |
+
y = np.zeros((0))
|
| 45 |
+
for px in x:
|
| 46 |
+
y = np.append(y,abs(px/2)- 0.09137*px**2 + np.sqrt(1-(abs(abs(px)-2)-1)**2) -3)
|
| 47 |
+
X.append(x/s)
|
| 48 |
+
Y.append(y/s)
|
| 49 |
+
|
| 50 |
+
# lower left
|
| 51 |
+
x = np.linspace(-7., -4, 300)
|
| 52 |
+
y = np.zeros((0))
|
| 53 |
+
for px in x:
|
| 54 |
+
y = np.append(y, -3*np.sqrt(-(px/7)**2+1))
|
| 55 |
+
X.append(x/s)
|
| 56 |
+
Y.append(y/s)
|
| 57 |
+
|
| 58 |
+
# lower right
|
| 59 |
+
x = np.linspace(4, 7, 300)
|
| 60 |
+
y = np.zeros((0))
|
| 61 |
+
for px in x:
|
| 62 |
+
y = np.append(y, -3*np.sqrt(-(px/7)**2+1))
|
| 63 |
+
X.append(x/s)
|
| 64 |
+
Y.append(y/s)
|
| 65 |
+
|
| 66 |
+
# top left
|
| 67 |
+
x = np.linspace(-7, -2.95, 300)
|
| 68 |
+
y = np.zeros((0))
|
| 69 |
+
for px in x:
|
| 70 |
+
y = np.append(y, 3*np.sqrt(-(px/7)**2+1))
|
| 71 |
+
X.append(x/s)
|
| 72 |
+
Y.append(y/s)
|
| 73 |
+
|
| 74 |
+
# top right
|
| 75 |
+
x = np.linspace(2.95, 7, 300)
|
| 76 |
+
y = np.zeros((0))
|
| 77 |
+
for px in x:
|
| 78 |
+
y = np.append(y, 3*np.sqrt(-(px/7)**2+1))
|
| 79 |
+
X.append(x/s)
|
| 80 |
+
Y.append(y/s)
|
| 81 |
+
|
| 82 |
+
# left ear left
|
| 83 |
+
x = np.linspace(-1, -.77, 2)
|
| 84 |
+
y = np.zeros((0))
|
| 85 |
+
for px in x:
|
| 86 |
+
y = np.append(y, 9-8*abs(px))
|
| 87 |
+
X.append(x/s)
|
| 88 |
+
Y.append(y/s)
|
| 89 |
+
|
| 90 |
+
# right ear right
|
| 91 |
+
x = np.linspace(.77, 1, 2)
|
| 92 |
+
y = np.zeros((0))
|
| 93 |
+
for px in x:
|
| 94 |
+
y = np.append(y, 9-8*abs(px))
|
| 95 |
+
X.append(x/s)
|
| 96 |
+
Y.append(y/s)
|
| 97 |
+
|
| 98 |
+
# mid
|
| 99 |
+
x = np.linspace(-.43, .43, 100)
|
| 100 |
+
y = np.zeros((0))
|
| 101 |
+
for px in x:
|
| 102 |
+
y = np.append(y,2)
|
| 103 |
+
X.append(x/s)
|
| 104 |
+
Y.append(y/s)
|
| 105 |
+
|
| 106 |
+
x = np.linspace(-2.91, -1, 100)
|
| 107 |
+
y = np.zeros((0))
|
| 108 |
+
for px in x:
|
| 109 |
+
y = np.append(y, 1.5 - .5*abs(px) - 1.89736*(np.sqrt(3-px**2+2*abs(px))-2) )
|
| 110 |
+
X.append(x/s)
|
| 111 |
+
Y.append(y/s)
|
| 112 |
+
|
| 113 |
+
x = np.linspace(1, 2.91, 100)
|
| 114 |
+
y = np.zeros((0))
|
| 115 |
+
for px in x:
|
| 116 |
+
y = np.append(y, 1.5 - .5*abs(px) - 1.89736*(np.sqrt(3-px**2+2*abs(px))-2) )
|
| 117 |
+
X.append(x/s)
|
| 118 |
+
Y.append(y/s)
|
| 119 |
+
|
| 120 |
+
x = np.linspace(-.7,-.43, 10)
|
| 121 |
+
y = np.zeros((0))
|
| 122 |
+
for px in x:
|
| 123 |
+
y = np.append(y, 3*abs(px)+.75)
|
| 124 |
+
X.append(x/s)
|
| 125 |
+
Y.append(y/s)
|
| 126 |
+
|
| 127 |
+
x = np.linspace(.43, .7, 10)
|
| 128 |
+
y = np.zeros((0))
|
| 129 |
+
for px in x:
|
| 130 |
+
y = np.append(y, 3*abs(px)+.75)
|
| 131 |
+
X.append(x/s)
|
| 132 |
+
Y.append(y/s)
|
| 133 |
+
|
| 134 |
+
return X, Y
|