[ソースコード]↓「二次曲線・二次曲面の標準形の計算・グラフ描画プログラムソースコード(Python)_1」からの続き↓
[QuadraticSurfaceCalculation]
import numpy as np
import math
# 二次形式の行列のランク(階数)
def drank(dis):
return np.linalg.matrix_rank(dis)
# 二次曲面の判別式(行列式 ≠ 0:有心二次曲面・柱面(楕円面・双曲面・錐面)/行列式 = 0:無心二次曲面・柱面(放物面・柱面))
def ddet(dis):
return np.linalg.det(dis)
# 二次曲面の分類の行列のランク(階数)
def classrank(cla):
return np.linalg.matrix_rank(cla)
# 二次曲面の分類の行列の行列式
def classdet(cla):
return np.linalg.det(cla)
# 二次形式の行列の固有値・固有ベクトル
def quadraticeigen(dis):
return np.linalg.eig(dis)
# 二次式部分の座標変換(二次形式の行列の固有値)
def eigenvalue2(dis):
e = quadraticeigen(dis)
return [e[0][0], e[0][1], e[0][2]]
# 二次形式の行列の固有ベクトル
def eigenvector(dis):
e = quadraticeigen(dis)
return e[1]
# 二次行列の回転角度
def rotateangle(dis):
# 回転行列を表す行列:
# [[cosYcosZ, -cosYsinZ, sinY],
# [sinXsinYcosZ+cosXsinZ, -sinXsinYsinZ+cosXcosZ, -sinXcosY],
# [-cosXsinYcosZ+sinXsinZ, cosXsinYsinZ+sinXcosZ, cosXcosY] ]
e = eigenvector(dis)
siny = e[0][2]
ay = math.asin(siny)
cosx = e[2][2] / np.cos(ay)
cosz = e[0][0] / np.cos(ay)
ax = math.acos(cosx)
az = math.acos(cosz)
return [ax / np.pi, ay / np.pi, az / np.pi]
# 一次式部分の座標変換(一次式部分の行列と二次形式の行列の固有ベクトルとの積)
def eigenvalue1(cla, dis):
con1 = np.dot([cla[3][0]*2, cla[3][1]*2, cla[3][2]*2], eigenvector(dis))
return [con1[0], con1[1], con1[2]]
# 座標変換式の平方完成・標準形・平行移動量
def standardtype(cla, dis):
# 判別行列のランク・行列値
dr = drank(dis)
dd = ddet(dis)
cr = classrank(cla)
cd = classdet(cla)
# 座標変換
e2 = eigenvalue2(dis)
e1 = eigenvalue1(cla, dis)
cx2 = e2[0]
cy2 = e2[1]
cz2 = e2[2]
cx1 = e1[0]
cy1 = e1[1]
cz1 = e1[2]
d = cla[3][3]
# 二次曲面の分類(行列値・行列のランクにより分類)
# classrank - drank = 0:錐面
# classrank - drank = 1:有心二次曲面
# classrank - drank = 2:無心二次曲面
if dr == 1: # ddet = 0
# classrank = 1:平面(y^2 = 0)(classdet = 0)
# classrank = 2:平行2平面・虚の平行2平面(y^2 = 1)(classdet = 0)
# classrank = 3:放物柱面(y^2 = x)(classdet = 0)
if round(cx2, 8) != 0:
# 座標変換式の平方完成・標準形
sy2 = 0
sz2 = 0
qx1 = cx1/(2*cx2)
if cr == 1: # cx2*(x+qx1)^2 = 0
sx2 = cx2
qy1 = 0
qz1 = 0
elif cr == 2: # cx2*(x+qx1)^2 + qd = 0
qd = d - (cx2*((cx1/(2*cx2))**2))
sx2 = cx2 / (-qd)
qy1 = 0
qz1 = 0
elif cr == 3:
if round(cy1, 8) != 0: # y = sx2*(x+qx1)^2 + qy1
sx2 = -(cx2/cy1)
qy1 = -( (cx2/cy1) * ( ( cx1/(2*cx2) )**2) ) + (d/cy1)
qz1 = 0
elif round(cz1, 8) != 0: # z = sx2*(x+qx1)^2 + qz1
sx2 = -(cx2/cz1)
qy1 = 0
qz1 = -( (cx2/cz1) * ( ( cx1/(2*cx2) )**2) ) + (d/cz1)
if round(cy2, 8) != 0:
# 座標変換式の平方完成・標準形
sx2 = 0
sz2 = 0
qy1 = cy1/(2*cy2)
if cr == 1: # cy2*(y+qy1)^2 = 0
sy2 = cy2
qx1 = 0
qz1 = 0
elif cr == 2: # cy2*(y+qy1)^2 + qd = 0
qd = d - (cy2*((cy1/(2*cy2))**2))
sy2 = cy2 / (-qd)
qx1 = 0
qz1 = 0
elif cr == 3:
if round(cx1, 8) != 0: # x = sy2*(y+qy1)^2 + qx1
sy2 = -(cy2/cx1)
qx1 = -( (cy2/cx1) * ( ( cy1/(2*cy2) )**2) ) + (d/cx1)
qz1 = 0
elif round(cz1, 8) != 0: # z = sy2*(y+qy1)^2 + qz1
sy2 = -(cy2/cz1)
qx1 = 0
qz1 = -( (cy2/cz1) * ( ( cy1/(2*cy2) )**2) ) + (d/cz1)
if round(cz2, 8) != 0:
# 座標変換式の平方完成・標準形
sx2 = 0
sy2 = 0
qz1 = cz1/(2*cz2)
if cr == 1: # cz2*(z+qz1)^2 = 0
sz2 = cz2
qx1 = 0
qy1 = 0
elif cr == 2: # cz2*(z+qz1)^2 + qd = 0
qd = d - (cz2*((cz1/(2*cz2))**2))
sz2 = cz2 / (-qd)
qx1 = 0
qy1 = 0
elif cr == 3:
if round(cx1, 8) != 0: # x = sz2*(z+qz1)^2 + qx1
sy2 = -(cy2/cx1)
qx1 = -( (cz2/cx1) * ( ( cz1/(2*cz2) )**2) ) + (d/cx1)
qz1 = 0
elif round(cy1, 8) != 0: # y = sz2*(z+qz1)^2 + qy1
sy2 = -(cz2/cy1)
qx1 = 0
qz1 = -( (cz2/cy1) * ( ( cz1/(2*cz2) )**2) ) + (d/cy1)
elif dr == 2: # ddet = 0
# classrank = 2:交わる2平面・1直線(x^2 + y^2 = 0)(classdet = 0)
# classrank = 3:楕円柱面・虚楕円柱面・双曲柱面(x^2 + y^2 = 1)(classdet = 0)
# classrank = 4:(x^2 + y^2 = z)/classdet < 0:楕円放物面/classdet > 0:双曲放物面
if round(cx2, 8) == 0:
# 座標変換式の平方完成・標準形
sx2 = 0
qy1 = cy1/(2*cy2)
qz1 = cz1/(2*cz2)
if cr == 2: # cy2*(y+qy1)^2 + cz2*(z+qz1)^2 = 0
sy2 = cy2
sz2 = cz2
qx1 = 0
elif cr == 3: # cy2*(y+qy1)^2 + cz2*(z+qz1)^2 + qd = 0
qd = d - (cy2*((cy1/(2*cy2))**2)) - (cz2*((cz1/(2*cz2))**2))
sy2 = cy2 / (-qd)
sz2 = cz2 / (-qd)
qx1 = 0
elif cr == 4: # x = sy2*(y+qy1)^2 + sz2*(z+qz1)^2 + qx1
sy2 = -(cy2/cx1)
sz2 = -(cz2/cx1)
qx1 = -( (cy2/cx1)*( (cy1/(2*cy2) )**2) ) - ( (cz2/cx1)*( (cz1/(2*cz2) )**2) ) + (d/cx1)
elif round(cy2, 8) == 0:
# 座標変換式の平方完成・標準形
sy2 = 0
qx1 = cx1/(2*cx2)
qz1 = cz1/(2*cz2)
if cr == 2: # cx2*(x+qx1)^2 + cz2*(z+qz1)^2 = 0
sx2 = cx2
sz2 = cz2
qy1 = 0
elif cr == 3: # cx2*(x+qx1)^2 + cz2*(z+qz1)^2 + qd = 0
qd = d - (cx2*((cx1/(2*cx2))**2)) - (cz2*((cz1/(2*cz2))**2))
sx2 = cx2 / (-qd)
sz2 = cz2 / (-qd)
qy1 = 0
elif cr == 4: # y = sx2*(x+qx1)^2 + sz2*(z+qz1)^2 + qy1
sx2 = -(cx2/cy1)
sz2 = -(cz2/cy1)
qy1 = -( (cx2/cy1)*( (cx1/(2*cx2) )**2) ) - ( (cz2/cy1)*( (cz1/(2*cz2) )**2) ) + (d/cy1)
elif round(cz2, 8) == 0:
# 座標変換式の平方完成・標準形
sz2 = 0
qx1 = cx1/(2*cx2)
qy1 = cy1/(2*cy2)
if cr == 2: # cx2*(x+qx1)^2 + cy2*(y+qy1)^2 = 0
sx2 = cx2
sy2 = cy2
qz1 = 0
elif cr == 3: # cx2*(x+qx1)^2 + cy2*(y+qy1)^2 + qd = 0
qd = d - (cx2*((cx1/(2*cx2))**2)) - (cy2*((cy1/(2*cy2))**2))
sx2 = cx2 / (-qd)
sy2 = cy2 / (-qd)
qz1 = 0
elif cr == 4: # z = sx2*(x+qx1)^2 + sy2*(y+qy1)^2 + qz1
sx2 = -(cx2/cz1)
sy2 = -(cy2/cz1)
qz1 = -( (cx2/cz1)*( (cx1/(2*cx2) )**2) ) - ( (cy2/cz1)*( (cy1/(2*cy2) )**2) ) + (d/cz1)
elif dr == 3: # ddet ≠ 0
# 座標変換式の平方完成:cx2*(x+qx1)^2 + cy2*(y+qy1)^2 + cz2*(z+qz1)^2 + qd = 0
qx1 = cx1/(2*cx2)
qy1 = cy1/(2*cy2)
qz1 = cz1/(2*cz2)
qd = cd / dd
#qd = d - (cx2*((cx1/(2*cx2))**2)) - (cy2*((cy1/(2*cy2))**2)) - (cz2*((cz1/(2*cz2))**2))
if cr == 3: # 錐面・1点・虚の錐面(classdet = 0)
# 標準形:sx2*x^2 + sy2*y^2 + sz2*z^2 = 0
sx2 = cx2
sy2 = cy2
sz2 = cz2
elif cr == 4: # 楕円面・虚楕円面・二葉双曲面・一葉双曲面(classdet ≠ 0)
# 標準形:sx2*x^2 + sy2*y^2 + sz2*z^2 = 1
sx2 = cx2 / (-qd)
sy2 = cy2 / (-qd)
sz2 = cz2 / (-qd)
return [[sx2, sy2, sz2], [qx1, qy1, qz1]]
[QuadraticSurfaceDraw]
# IPython Notebookモジュールのインポート
import importnb
import numpy as np
import sympy
# グラフ描画モジュールのインポート
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# 別ファイルをインポート(jupyterモジュールインポート用)
with __import__('importnb').Notebook():
import QuadraticSurfaceCalculation
# グラフ描画(a2*x^2 + b2*y^2 + c2*z^2 + ab*xy + bc*yz + ca*zx + a1*x + b1*y + c1*z + d = 0)
def graphdraw(varlist):
# 変数名を定義
varname = ['a2', 'b2', 'c2', 'ab', 'bc', 'ca', 'a1', 'b1', 'c1', 'd']
# 動的に変数を定義
for i in range(len(varlist)):
globals()[varname[i]] = varlist[i]
# 二次形式の行列
dismatrix = [[a2, ab/2, ca/2], [ab/2, b2, bc/2], [ca/2, bc/2, c2]]
# 二次曲面の分類の行列
classmatrix = [[a2, ab/2, ca/2, a1/2], [ab/2, b2, bc/2, b1/2], [ca/2, bc/2, c2, c1/2], [a1/2, b1/2, c1/2, d]]
# 判別行列のランク・行列値
dr = QuadraticSurfaceCalculation.drank(dismatrix)
dd = QuadraticSurfaceCalculation.ddet(dismatrix)
cr = QuadraticSurfaceCalculation.classrank(classmatrix)
cd = QuadraticSurfaceCalculation.classdet(classmatrix)
# 座標変換
e2 = QuadraticSurfaceCalculation.eigenvalue2(dismatrix)
e1 = QuadraticSurfaceCalculation.eigenvalue1(classmatrix, dismatrix)
cx2 = e2[0]
cy2 = e2[1]
cz2 = e2[2]
cx1 = e1[0]
cy1 = e1[1]
cz1 = e1[2]
# 座標変換式の平方完成・標準形・平行移動量
st = QuadraticSurfaceCalculation.standardtype(classmatrix, dismatrix)
sx2 = st[0][0]
sy2 = st[0][1]
sz2 = st[0][2]
qx1 = st[1][0]
qy1 = st[1][1]
qz1 = st[1][2]
# 二次行列の回転角度
ra = QuadraticSurfaceCalculation.rotateangle(dismatrix)
# 文字を定義
x = sympy.Symbol('x')
y = sympy.Symbol('y')
z = sympy.Symbol('z')
# 数式設定
tos = float(a2)*x**2 + float(b2)*y**2 + float(c2)*z**2 + float(ab)*x*y + float(bc)*y*z + float(ca)*z*x + float(a1)*x + float(b1)*y + float(c1)*z
tor = tos + float(d)
tsq = round(cx2,2)*x**2 + round(cy2,2)*y**2 + round(cz2,2)*z**2 + round(cx1,2)*x + round(cy1,2)*y + round(cz1,2)*z + round(d,2)
if cr == 1:
if dr == 1: # 平面
# a2*x^2 + b2*y^2 + c2*z^2 + ab*xy + bc*yz + ca*zx + a1*x + b1*y + c1*z + d を因数分解
st = sympy.factor(tor)
elif cr == 2:
if dr == 1: # 平行2平面・虚の平行2平面
# a2*x^2 + b2*y^2 + c2*z^2 + ab*xy + bc*yz + ca*zx + a1*x + b1*y + c1*z を(-d)で割って因数分解
st = sympy.factor(tos/(-d))
elif dr == 2: # 交わる2平面・1直線
# a2*x^2 + b2*y^2 + c2*z^2 + ab*xy + bc*yz + ca*zx + a1*x + b1*y + c1*z + d を因数分解
st = sympy.factor(tor)
else: # 柱面・放物面・錐面・楕円面・一葉双曲面・二葉双曲面
# 数式設定
tsx = round(sx2,2)*x**2
tsy = round(sy2,2)*y**2
tsz = round(sz2,2)*z**2
# 描画領域の分割行列指定
fig, ax = plt.subplots(1, 1, figsize=(15, 15), subplot_kw=dict(projection='3d'))
# データを作成
x = np.linspace(-np.pi, np.pi, 200)
y = np.linspace(-2*np.pi, 2*np.pi, 100)
t = np.linspace(-2, 2, 100)
if round(dd, 8) == 0:
if round(cd, 8) == 0: # 柱面
pass
else: # classdet < 0:楕円放物面/classdet > 0:双曲放物面
x2,y2 = np.meshgrid(x,y)
if round(cx2, 8) == 0:
X = sy2 * (x2**2) + sz2 * (y2**2)
Y = x2
Z = y2
elif round(cy2, 8) == 0:
X = x2
Y = sx2 * (x2**2) + sz2 * (y2**2)
Z = y2
elif round(cz2, 8) == 0:
X = x2
Y = y2
Z = sx2 * (x2**2) + sy2 * (y2**2)
else:
if cd < 0:
if cx2 > 0 and cy2 > 0 and cz2 > 0: # 楕円面
x2,y2 = np.meshgrid(x,y)
X = 0 + sx2 * np.cos(y2) * np.sin(x2)
Y = 0 + sy2 * np.sin(y2) * np.sin(x2)
Z = 0 + sz2 * np.cos(x2)
else: # 二葉双曲面
y2,t2 = np.meshgrid(y,t)
r = (((t2**2)-1))**(1/2)
if cx2 < 0 and cy2 < 0 and cz2 > 0:
X = 0 + sx2 * r * np.cos(y2)
Y = 0 + sy2 * r * np.sin(y2)
Z = 0 + sz2 * t2
elif cx2 < 0 and cy2 > 0 and cz2 < 0:
X = 0 + sx2 * r * np.sin(y2)
Y = 0 + sy2 * t2
Z = 0 + sz2 * r * np.cos(y2)
elif cx2 > 0 and cy2 < 0 and cz2 < 0:
X = 0 + sx2 * t2
Y = 0 + sy2 * r * np.cos(y2)
Z = 0 + sz2 * r * np.sin(y2)
elif round(cd, 8) == 0:
if (cx2 > 0 and cy2 > 0 and cz2 >0) or (cx2 < 0 and cy2 < 0 and cz2 < 0): # 点楕円面・虚錐面
X = 0
Y = 0
Z = 0
else: # 錐面
y2,t2 = np.meshgrid(y,t)
if (cx2 < 0 and cy2 < 0 and cz2 > 0) or (cx2 > 0 and cy2 > 0 and cz2 < 0):
X = 0 + sx2 * t2 * np.cos(y2)
Y = 0 + sy2 * t2 * np.sin(y2)
Z = 0 + sz2 * t2
elif (cx2 < 0 and cy2 > 0 and cz2 < 0) or (cx2 > 0 and cy2 < 0 and cz2 > 0):
X = 0 + sx2 * t2 * np.sin(y2)
Y = 0 + sy2 * t2
Z = 0 + sz2 * t2 * np.cos(y2)
elif (cx2 > 0 and cy2 < 0 and cz2 < 0) or (cx2 < 0 and cy2 > 0 and cz2 > 0):
X = 0 + sx2 * t2
Y = 0 + sy2 * t2 * np.cos(y2)
Z = 0 + sz2 * t2 * np.sin(y2)
elif cd > 0:
if cx2 > 0 and cy2 > 0 and cz2 > 0: # 虚楕円面
pass
else: # 一葉双曲面
y2,t2 = np.meshgrid(y,t)
r = (((t2**2)+1))**(1/2)
if cx2 > 0 and cy2 > 0 and cz2 < 0:
X = 0 + sx2 * r * np.cos(y2)
Y = 0 + sy2 * r * np.sin(y2)
Z = 0 + sz2 * t2
elif cx2 > 0 and cy2 < 0 and cz2 > 0:
X = 0 + sx2 * r * np.sin(y2)
Y = 0 + sy2 * t2
Z = 0 + sz2 * r * np.cos(y2)
elif cx2 < 0 and cy2 > 0 and cz2 > 0:
X = 0 + sx2 * t2
Y = 0 + sy2 * r * np.cos(y2)
Z = 0 + sz2 * r * np.sin(y2)
# 縦と横の比を同じにする
ax.set_aspect('equal')
# 軸のラベルの設定
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
# 視点の設定
ax.view_init(elev=20, azim=20)
# タイトル
titleorigin = rf'${sympy.latex(tor)} = 0$'
if cr <= 2:
titlerotate = ''
titlesquare = ''
titlemove = ''
sl = sympy.latex(st)
if cr == 1:
if dr == 1: # 平面
titlestandard = rf'$→ {sl} = 0$'
elif cr == 2:
if dr == 1: # 平行2平面
titlestandard = rf'$→ {sl} = 1$'
elif dr == 2: # 1直線・交わる2平面
titlestandard = rf'$→ {sl} = 0$'
else:
titlerotate = rf'$→ rotateX = {round(ra[0],2)} π / rotateY = {round(ra[1],2)} π / rotateZ = {round(ra[2],2)} π$'
titlesquare = rf'$→ {sympy.latex(tsq)} = 0$'
titlemove = rf'$→ moveX = {round(qx1,2)} / moveY = {round(qy1,2)} / moveZ = {round(qz1,2)}$'
sl = sympy.latex(tsx + tsy + tsz)
if cr == 3:
if dr == 1: # 放物柱面
if round(cx1, 8) != 0:
titlestandard = rf'$→ x = {sl}$'
elif round(cy1, 8) != 0:
titlestandard = rf'$→ y = {sl}$'
elif round(cz1, 8) != 0:
titlestandard = rf'$→ z = {sl}$'
elif dr == 2: # 楕円柱面・虚楕円柱面・双曲柱面
titlestandard = rf'$→ {sl} = 1$'
elif dr == 3: # 錐面
titlestandard = rf'$→ {sl} = 0$'
elif cr == 4:
if dr == 2: # 楕円放物面・双曲放物面
if round(cx2, 8) == 0:
titlestandard = rf'$→ x = {sl}$'
elif round(cy2, 8) == 0:
titlestandard = rf'$→ y = {sl}$'
elif round(cz2, 8) == 0:
titlestandard = rf'$→ z = {sl}$'
elif dr == 3: # 楕円面・一葉双曲面・二葉双曲面
titlestandard = rf'$→ {sl} = 1$'
ax.set_title(titleorigin + '\n' + titlerotate + '\n' + titlesquare + '\n' + titlemove + '\n' + titlestandard,
color='black', loc='center', size=16)
if (round(dd, 8) == 0) and (round(cd, 8) == 0):
pass
else:
# 回転・平行移動した曲面を描画
ax.plot_surface(X, Y, Z, alpha=0.5, antialiased=True, shade=True, color='#cccccc')
# 軸を自動調整
ax.autoscale()
fig.show()