我正在研究黎曼球立体投影几何以创建莫比乌斯变换,目的是在有限的表面内“存储”整个复平面的信息。
立体投影基本上是 n 球体的“全息图”——我们通过它的坐标追踪轨迹线并分析“投影”到表面上的最终图案。
黎曼球是一种特殊类型的球体,其中这些坐标被定义为具有初始值(即当球体未被操纵时),这些初始值满足以下属性:如果你在球体的顶部(在其方位角)举起一个隐喻的“灯”,并追踪与这些点相交的光子的路径,你会得到复平面上均匀分布的坐标 - 本质上,你会得到它的笛卡尔表示。
当你操纵球体时,事情就会变得很酷,就像在这个视频中一样:https://www.youtube.com/watch?v=0z1fIsUNhO4,并从与这些“保留点”相交的球体方位线投影,直到它们与复平面相交。这可以让你做一些很酷的非欧几里得几何,并引出一些更高级的主题。基本上,它看起来像这样;
本质上,虽然在“标准位置”(即对于未操纵的黎曼球)绘制此图很容易,但对球体进行操纵所需的数学运算处于 TeX 无法实现的水平。因此,我的问题是如何做到这一点 - 在这种情况下,使用 TikZ - 对黎曼球进行任意操纵。这个问题是一般问题的一个特例:如何让 TikZ 制作基于 TeX 无法实现的数学运算的图表?我想到的答案是构建一个算法 - 使用一种编程语言是专为这种数学而构建 - 为您生成 TikZ 代码。
正如我提到的,初始图表很容易在 TeX 的功能范围内,因此这里有一个 MWE:
\documentclass{article}
\usepackage{tikz}
\begin{document}
% The resize box is to prevent the picture from going off the page. I have chosen the parameters \textwidth and !, with the intention of preserving its original dimensions.
\resizebox{\textwidth}{!}{
\begin{tikzpicture}
% The common name for the top of a Riemann sphere - in this case a 2-sphere - is its "Azimuth."
\coordinate(Azimuth) at (0,8);
% The draw paths with thearrow heads are the coordinate axes.
\draw[black,-latex,thick] (0,-1) -- (0,9);
\draw[black,-latex,thick] (-10,0) -- (10,0);
% This circle is the Riemann 2-sphere. I have chosen a radius of two for reasons of accessibility.
\draw[] (0,6) circle [radius=2];
% This is the meat of the code. Here, we project equidistant points on the x-axis onto the Riemann 2-sphere. This basically "stores" all the information on the x-axis in a finite space. In 3D, this concept allows us to map the entire complex plane onto a unit-sphere - a useful tool in Complex Analysis.
% The points on the 2-sphere through which these lines intersect are preserved when the 2-sphere later undergoes geometric manipulations, such as rotations and translations. We can manipulate the Riemann sphere and then reproject - from its Azimuth - lines which intersect all of these "stored" points to create one-to-one re-representations of the x-axis. This is called a Conformal Transformation - more specifically, a Mobius Transformation.
% These manipulations appear quite difficult to achieve in plain LaTeX, due to their heavy-duty arithmetic nature.
\foreach \x in {-10,-9,...,10}{
\draw[] (\x,0) -- (Azimuth);}
\end{tikzpicture}}
\end{document}
答案1
在 LaTeX 中实现此功能的解决方案是使用外部软件“为您编写代码”。本质上,我利用了 Python 编程语言的计算机算术功能以及算法功能来做到这一点。
下面您可以看到我的实现(即在代码块中)。
对于未来的读者来说,了解与此相关的一些数学知识将会很有用,因此我也会将其中的一些内容包括在内。
我创建了这个图来帮助我弄清楚如何通过非原点旋转线条。下面是相应的数学。
我需要这张图来帮助推导与 n 球面方位角相交的每条线的通用公式(由于域限制,x=0 的情况除外)。同样,下面是相应的数学。
虽然最后发现这是多余的,但当球体定义为函数时,这幅图让我们深入了解了球体的分段性质。请参阅下面的数学。
这是我画的另一张图。关于它对应的数学,我认为我可能在某个地方犯了错误——所以要小心,自己思考。
这次操作的最终目标——不久后我会在一篇文章中介绍——将基于这个 3D 概念。我的实现可能会使用 Asymptote,但这是使用 TikZ 绘制的概念图:
现在进入正题;这是二维产品,以及可用于生成每一帧的代码。我期待在 3D 中实现它 - 这项工作非常有趣。
这可以通过 Python 实现,使用的过程与我下面开发的算法类似:
import numpy as np
r, L, linspace_amount=2.0, 8.0, 38
def x_input_function(x_input):
return (2*x_input*r*np.abs((L**2)/(x_input**2+L**2))**(1/2))/np.abs(x_input**2+L**2)**(1/2)
x_input_list = [x_input_function(x_input) for x_input in np.linspace(-10,10,linspace_amount)]
def y_input_function_upper(x_input):
return (np.abs(r**2-x_input**2)**(1/2)+L-r)
def y_input_function_lower(x_input):
return (-np.abs(r**2-x_input**2)**(1/2)+L-r)
y_input_list = [y_input_function_upper(x_input) for x_input in x_input_list[:int(0.1*linspace_amount)]]
y_input_list.extend([y_input_function_lower(x_input) for x_input in x_input_list[int(0.1*linspace_amount):int(0.9*linspace_amount)]])
y_input_list.extend([y_input_function_upper(x_input) for x_input in x_input_list[int(0.9*linspace_amount):]])
# https://stackoverflow.com/questions/34372480/rotate-point-about-another-point-in-degrees-python
import math
def rotate_x(x,y,theta): #rotate x,y around xo,yo by theta (rad)
xr=math.cos(theta)*(x-0)-math.sin(theta)*(y-(L-r)) + 0
return xr
def rotate_y(y,x,theta):
yr=math.sin(theta)*(x-0)+math.cos(theta)*(y-(L-r)) + (L-r)
return yr
rotated_x_list = []
rotated_y_list = []
def rotate(angle):
global rotated_x_list,rotated_y_list
rotated_x_list.clear()
rotated_y_list.clear()
for pos in range(len(x_input_list)):
rotated_x_list.append(rotate_x(x_input_list[pos], y_input_list[pos],angle))
rotated_y_list.append(rotate_y(y_input_list[pos], x_input_list[pos],angle))
start= r'''
\documentclass{beamer}
\usepackage{tikz}
\begin{document}
\vspace*{\fill}
\begin{center}
\begin{tikzpicture}[scale=0.5]
\clip[] (-10,-1) rectangle (10,9);
\draw[black,-latex,thick] (0,-1) -- (0,9); % y-axis
\draw[black,-latex,thick] (-10,0) -- (10,0); % x-axis
\draw[] (0,6) circle [radius=2];
'''
end = r'''
\end{tikzpicture}
\end{center}
\vspace*{\fill}
\end{document}
'''
def draw_path(x,y):
if np.abs((-L*x)/(y-L)) < 40:
return r'''\draw[] (0,8) -- ({},0);
\fill[] ({},{}) circle [radius=0.08];
'''.format((-L*x)/(y-L),x,y)
else:
return r'''\draw[] (0,8) -- (0,0);
'''
def make_tikz_code():
string = r''
string += start
for pos in range(len(x_input_list)):
try:
string += draw_path(rotated_x_list[pos], rotated_y_list[pos])
except:
pass
string += end
return string
rotate(-10*np.pi/180)
print(make_tikz_code())