Pues bien, reunir la información relativa a la teoría básica aquí y la bonita explicación aquí en cuanto a la proyección, pude construir mi propia versión del teseracto, y sí es posible para mostrar un punto dentro del teseracto, y Me equivoqué en las suposiciones que hice respecto a la metodología aplicada para visualizar el punto 4D. Básicamente, si queremos mostrar un punto dentro del teseracto, tenemos que proyectar primero el teseracto y luego proyectar también el punto deseado, siguiendo las mismas reglas de proyección.
- La definición del teseracto (créditos a draks... )
El teseracto es un cubo de cuatro dimensiones. Tiene 16 puntos de aristas $v=(a,b,c,d)$ con $a,b,c,d$ ya sea igual a $+1$ o $-1$ . Dos puntos están conectados, si su distancia es $2$ . Dada una proyección $P(x,y,z,w)=(x,y,z)$ del espacio cuatridimensional al espacio tridimensional, podemos visualizar el cubo como un objeto en el espacio familiar. El efecto de una transformación lineal como una rotación $$ R(t)=\pmatrix{1&0&0&0\\0&1&0&0&\\0&0&\cos(t)&\sin(t)\\0&0&-\sin(t)&\cos(t)} $$ en $4D$ espacio se puede visualizar en $3D$ viendo los puntos $v(t) = P R(t) v$ en $\mathbb R^3$ .
- La definición de la proyección (créditos a Andrew D. Hwang )
$$ P(x, y, z, w) = \frac{h}{h - w}(x, y, z). $$
- Y por último, la definición de la distancia entre dos puntos cuatridimensionales se calcula de la siguiente manera (esto se utiliza para mostrar los bordes del teseracto correctamente, haciendo líneas entre los vértices proyectados correctos):
$$d=\sqrt{(x_0-x'_0)^2+(x_1-x'_1)^2+(x_2-x'_2)^2+(x_3-x'_3)^2}$$
- He preparado un fragmento de código Python que crea los fotogramas (jpg) de una animación de un teseracto que incluye un punto interno. En este caso, la longitud de la arista es $1000$ por lo que la distancia entre los vértices no es $2$ pero $1000$ . Para la proyección he utilizado una fuente de luz situada a tres veces la longitud del borde, esto es $h=3000$ . Por último, he aplicado una rotación como la definida anteriormente. La estrella marca la ubicación del punto $(\frac{3}{4} \cdot \frac{1000}{2}, \frac{3}{4} \cdot \frac{1000}{2}, \frac{3}{4} \cdot \frac{1000}{2}, \frac{3}{4} \cdot \frac{1000}{2})$ mientras giramos el teseracto. Ten en cuenta que la posición de la cámara en la animación es lateral. La ubicación típica de la cámara es desde arriba, que es la vista habitual de "cuadrado dentro de un cuadrado". Pero por motivos de visualización (queremos ver claramente el movimiento de la proyección del punto debido a la rotación del teseracto) la cámara en este caso se situó en una posición lateral. Por favor, utilícela y modifíquela libremente (por ejemplo, en lugar de un punto es posible mostrar un conjunto de puntos y verificar si hay simetría, etc.):
from math import pi, sin , cos, sqrt
import matplotlib.pyplot as plt
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
edge_length=1000
edge_half_length= int(edge_length/2)
lotuples=[]
list_of_loxt_lists=[]
list_of_loyt_lists=[]
list_of_lozt_lists=[]
rotation_accuracy=100
filled_once=False
for ratio in range(0,rotation_accuracy):
angle= ((2*pi)*ratio)/rotation_accuracy
loxt=[]
loyt=[]
lozt=[]
#t=edge_half_length (positive)
a=-edge_half_length
b=edge_half_length
ret0=-edge_half_length
ret1=edge_half_length
finala=a
finalb=b
finalret0=(ret0*cos(angle))+(ret1*sin(angle))
finalret1=(ret0*(-sin(angle)))+(ret1*cos(angle))
light_projection_factor = ((edge_length*3)/((edge_length*3)-(finalret1)))
loxt.append(light_projection_factor*finala)
loyt.append(light_projection_factor*finalb)
lozt.append(light_projection_factor*finalret0)
if filled_once==False:
lotuples.append([a,b,ret0,ret1])
a=-edge_half_length
b=-edge_half_length
ret0=-edge_half_length
ret1=edge_half_length
finala=a
finalb=b
finalret0=(ret0*cos(angle))+(ret1*sin(angle))
finalret1=(ret0*(-sin(angle)))+(ret1*cos(angle))
light_projection_factor = ((edge_length*3)/((edge_length*3)-(finalret1)))
loxt.append(light_projection_factor*finala)
loyt.append(light_projection_factor*finalb)
lozt.append(light_projection_factor*finalret0)
if filled_once==False:
lotuples.append([a,b,ret0,ret1])
a=edge_half_length
b=edge_half_length
ret0=-edge_half_length
ret1=edge_half_length
finala=a
finalb=b
finalret0=(ret0*cos(angle))+(ret1*sin(angle))
finalret1=(ret0*(-sin(angle)))+(ret1*cos(angle))
light_projection_factor = ((edge_length*3)/((edge_length*3)-(finalret1)))
loxt.append(light_projection_factor*finala)
loyt.append(light_projection_factor*finalb)
lozt.append(light_projection_factor*finalret0)
if filled_once==False:
lotuples.append([a,b,ret0,ret1])
a=edge_half_length
b=-edge_half_length
ret0=-edge_half_length
ret1=edge_half_length
finala=a
finalb=b
finalret0=(ret0*cos(angle))+(ret1*sin(angle))
finalret1=(ret0*(-sin(angle)))+(ret1*cos(angle))
light_projection_factor = ((edge_length*3)/((edge_length*3)-(finalret1)))
loxt.append(light_projection_factor*finala)
loyt.append(light_projection_factor*finalb)
lozt.append(light_projection_factor*finalret0)
if filled_once==False:
lotuples.append([a,b,ret0,ret1])
a=edge_half_length
b=edge_half_length
ret0=edge_half_length
ret1=edge_half_length
finala=a
finalb=b
finalret0=(ret0*cos(angle))+(ret1*sin(angle))
finalret1=(ret0*(-sin(angle)))+(ret1*cos(angle))
light_projection_factor = ((edge_length*3)/((edge_length*3)-(finalret1)))
loxt.append(light_projection_factor*finala)
loyt.append(light_projection_factor*finalb)
lozt.append(light_projection_factor*finalret0)
if filled_once==False:
lotuples.append([a,b,ret0,ret1])
a=edge_half_length
b=-edge_half_length
ret0=edge_half_length
ret1=edge_half_length
finala=a
finalb=b
finalret0=(ret0*cos(angle))+(ret1*sin(angle))
finalret1=(ret0*(-sin(angle)))+(ret1*cos(angle))
light_projection_factor = ((edge_length*3)/((edge_length*3)-(finalret1)))
loxt.append(light_projection_factor*finala)
loyt.append(light_projection_factor*finalb)
lozt.append(light_projection_factor*finalret0)
if filled_once==False:
lotuples.append([a,b,ret0,ret1])
a=-edge_half_length
b=edge_half_length
ret0=edge_half_length
ret1=edge_half_length
finala=a
finalb=b
finalret0=(ret0*cos(angle))+(ret1*sin(angle))
finalret1=(ret0*(-sin(angle)))+(ret1*cos(angle))
light_projection_factor = ((edge_length*3)/((edge_length*3)-(finalret1)))
loxt.append(light_projection_factor*finala)
loyt.append(light_projection_factor*finalb)
lozt.append(light_projection_factor*finalret0)
if filled_once==False:
lotuples.append([a,b,ret0,ret1])
a=-edge_half_length
b=-edge_half_length
ret0=edge_half_length
ret1=edge_half_length
finala=a
finalb=b
finalret0=(ret0*cos(angle))+(ret1*sin(angle))
finalret1=(ret0*(-sin(angle)))+(ret1*cos(angle))
light_projection_factor = ((edge_length*3)/((edge_length*3)-(finalret1)))
loxt.append(light_projection_factor*finala)
loyt.append(light_projection_factor*finalb)
lozt.append(light_projection_factor*finalret0)
if filled_once==False:
lotuples.append([a,b,ret0,ret1])
#t=-edge_half_length (negative)
a=-edge_half_length
b=edge_half_length
ret0=-edge_half_length
ret1=-edge_half_length
finala=a
finalb=b
finalret0=(ret0*cos(angle))+(ret1*sin(angle))
finalret1=(ret0*(-sin(angle)))+(ret1*cos(angle))
light_projection_factor = ((edge_length*3)/((edge_length*3)-(finalret1)))
loxt.append(light_projection_factor*finala)
loyt.append(light_projection_factor*finalb)
lozt.append(light_projection_factor*finalret0)
if filled_once==False:
lotuples.append([a,b,ret0,ret1])
a=-edge_half_length
b=-edge_half_length
ret0=-edge_half_length
ret1=-edge_half_length
finala=a
finalb=b
finalret0=(ret0*cos(angle))+(ret1*sin(angle))
finalret1=(ret0*(-sin(angle)))+(ret1*cos(angle))
light_projection_factor = ((edge_length*3)/((edge_length*3)-(finalret1)))
loxt.append(light_projection_factor*finala)
loyt.append(light_projection_factor*finalb)
lozt.append(light_projection_factor*finalret0)
if filled_once==False:
lotuples.append([a,b,ret0,ret1])
a=edge_half_length
b=edge_half_length
ret0=-edge_half_length
ret1=-edge_half_length
finala=a
finalb=b
finalret0=(ret0*cos(angle))+(ret1*sin(angle))
finalret1=(ret0*(-sin(angle)))+(ret1*cos(angle))
light_projection_factor = ((edge_length*3)/((edge_length*3)-(finalret1)))
loxt.append(light_projection_factor*finala)
loyt.append(light_projection_factor*finalb)
lozt.append(light_projection_factor*finalret0)
if filled_once==False:
lotuples.append([a,b,ret0,ret1])
a=edge_half_length
b=-edge_half_length
ret0=-edge_half_length
ret1=-edge_half_length
finala=a
finalb=b
finalret0=(ret0*cos(angle))+(ret1*sin(angle))
finalret1=(ret0*(-sin(angle)))+(ret1*cos(angle))
light_projection_factor = ((edge_length*3)/((edge_length*3)-(finalret1)))
loxt.append(light_projection_factor*finala)
loyt.append(light_projection_factor*finalb)
lozt.append(light_projection_factor*finalret0)
if filled_once==False:
lotuples.append([a,b,ret0,ret1])
a=edge_half_length
b=edge_half_length
ret0=edge_half_length
ret1=-edge_half_length
finala=a
finalb=b
finalret0=(ret0*cos(angle))+(ret1*sin(angle))
finalret1=(ret0*(-sin(angle)))+(ret1*cos(angle))
light_projection_factor = ((edge_length*3)/((edge_length*3)-(finalret1)))
loxt.append(light_projection_factor*finala)
loyt.append(light_projection_factor*finalb)
lozt.append(light_projection_factor*finalret0)
if filled_once==False:
lotuples.append([a,b,ret0,ret1])
a=edge_half_length
b=-edge_half_length
ret0=edge_half_length
ret1=-edge_half_length
finala=a
finalb=b
finalret0=(ret0*cos(angle))+(ret1*sin(angle))
finalret1=(ret0*(-sin(angle)))+(ret1*cos(angle))
light_projection_factor = ((edge_length*3)/((edge_length*3)-(finalret1)))
loxt.append(light_projection_factor*finala)
loyt.append(light_projection_factor*finalb)
lozt.append(light_projection_factor*finalret0)
if filled_once==False:
lotuples.append([a,b,ret0,ret1])
a=-edge_half_length
b=edge_half_length
ret0=edge_half_length
ret1=-edge_half_length
finala=a
finalb=b
finalret0=(ret0*cos(angle))+(ret1*sin(angle))
finalret1=(ret0*(-sin(angle)))+(ret1*cos(angle))
light_projection_factor = ((edge_length*3)/((edge_length*3)-(finalret1)))
loxt.append(light_projection_factor*finala)
loyt.append(light_projection_factor*finalb)
lozt.append(light_projection_factor*finalret0)
if filled_once==False:
lotuples.append([a,b,ret0,ret1])
a=-edge_half_length
b=-edge_half_length
ret0=edge_half_length
ret1=-edge_half_length
finala=a
finalb=b
finalret0=(ret0*cos(angle))+(ret1*sin(angle))
finalret1=(ret0*(-sin(angle)))+(ret1*cos(angle))
light_projection_factor = ((edge_length*3)/((edge_length*3)-(finalret1)))
loxt.append(light_projection_factor*finala)
loyt.append(light_projection_factor*finalb)
lozt.append(light_projection_factor*finalret0)
if filled_once==False:
lotuples.append([a,b,ret0,ret1])
filled_once=True
list_of_loxt_lists.append(loxt)
list_of_loyt_lists.append(loyt)
list_of_lozt_lists.append(lozt)
list_of_loxi_lists=[]
list_of_loyi_lists=[]
list_of_lozi_lists=[]
list_of_loxi_lists_axis=[]
list_of_loyi_lists_axis=[]
list_of_lozi_lists_axis=[]
for ratio in range(0,rotation_accuracy):
angle= ((2*pi)*ratio)/rotation_accuracy
loxi=[]
loyi=[]
lozi=[]
finala=int((3/4)*edge_half_length)
finalb=int((3/4)*edge_half_length)
finalret0=(int((3/4)*edge_half_length)*cos(angle))+(int((3/4)*edge_half_length)*sin(angle))
finalret1=(int((3/4)*edge_half_length)*(-sin(angle)))+(int((3/4)*edge_half_length)*cos(angle))
light_projection_factor = ((edge_length*3)/((edge_length*3)-finalret1))
loxi.append(light_projection_factor*finala)
loyi.append(light_projection_factor*finalb)
lozi.append(light_projection_factor*finalret0)
list_of_loxi_lists.append(loxi)
list_of_loyi_lists.append(loyi)
list_of_lozi_lists.append(lozi)
# Show projection of refence axes BEGIN
loxi=[]
loyi=[]
lozi=[]
finala_axis=finala
finalb_axis=0
finalret0_axis=0
finalret1_axis=0
light_projection_factor = ((edge_length*3)/((edge_length*3)-finalret1_axis))
loxi.append(light_projection_factor*finala_axis)
loyi.append(light_projection_factor*finalb_axis)
lozi.append(light_projection_factor*finalret0_axis)
finala_axis=0
finalb_axis=finalb
finalret0_axis=0
finalret1_axis=0
light_projection_factor = ((edge_length*3)/((edge_length*3)-finalret1_axis))
loxi.append(light_projection_factor*finala_axis)
loyi.append(light_projection_factor*finalb_axis)
lozi.append(light_projection_factor*finalret0_axis)
finala_axis=0
finalb_axis=0
finalret0_axis=finalret0
finalret1_axis=0
light_projection_factor = ((edge_length*3)/((edge_length*3)-finalret1_axis))
loxi.append(light_projection_factor*finala_axis)
loyi.append(light_projection_factor*finalb_axis)
lozi.append(light_projection_factor*finalret0_axis)
finala_axis=0
finalb_axis=0
finalret0_axis=0
finalret1_axis=finalret1
light_projection_factor = ((edge_length*3)/((edge_length*3)-finalret1_axis))
loxi.append(light_projection_factor*finala_axis)
loyi.append(light_projection_factor*finalb_axis)
lozi.append(light_projection_factor*finalret0_axis)
list_of_loxi_lists_axis.append(loxi)
list_of_loyi_lists_axis.append(loyi)
list_of_lozi_lists_axis.append(lozi)
# Show projection of refence axes END
for ratio in range(0,rotation_accuracy):
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.view_init(elev=17., azim=-152)
for i in range(0,len(lotuples)):
for j in range(i+1,len(lotuples)):
distance = int(sqrt(((lotuples[i][0]-lotuples[j][0])**2)+((lotuples[i][1]-lotuples[j][1])**2)+((lotuples[i][2]-lotuples[j][2])**2)+((lotuples[i][3]-lotuples[j][3])**2)))
if distance<=edge_length:
ax.plot([list_of_loxt_lists[ratio][i],list_of_loxt_lists[ratio][j]],[list_of_loyt_lists[ratio][i],list_of_loyt_lists[ratio][j]],[list_of_lozt_lists[ratio][i],list_of_lozt_lists[ratio][j]],"r")
ax.plot([-edge_length],[edge_length],[-edge_length],"w")
ax.plot([-edge_length],[-edge_length],[-edge_length],"w")
ax.plot([edge_length],[edge_length],[-edge_length],"w")
ax.plot([edge_length],[-edge_length],[-edge_length],"w")
ax.plot([edge_length],[edge_length],[edge_length],"w")
ax.plot([edge_length],[-edge_length],[edge_length],"w")
ax.plot([-edge_length],[edge_length],[edge_length],"w")
ax.plot([-edge_length],[-edge_length],[edge_length],"w")
ax.plot([list_of_loxi_lists[ratio][0]], [list_of_loyi_lists[ratio][0]], [list_of_lozi_lists[ratio][0]], "r*")
#projection of refence axes around the point
ax.plot([0,list_of_loxi_lists_axis[ratio][0]],[0,list_of_loyi_lists_axis[ratio][0]],[0,list_of_lozi_lists[ratio][0]],"b")
ax.plot([0,list_of_loxi_lists_axis[ratio][1]],[0,list_of_loyi_lists_axis[ratio][1]],[0,list_of_lozi_lists_axis[ratio][1]],"b")
ax.plot([0,list_of_loxi_lists_axis[ratio][2]],[0,list_of_loyi_lists_axis[ratio][2]],[0,list_of_lozi_lists_axis[ratio][2]],"b")
ax.plot([0,list_of_loxi_lists_axis[ratio][3]],[0,list_of_loyi_lists_axis[ratio][3]],[0,list_of_lozi_lists_axis[ratio][3]],"b")
ax.dist=8
mpl.pyplot.savefig("tesseract_movie_"+str(ratio)+".png")
print("End ratio "+str(ratio))
(El gif animado se generó uniendo los archivos jpg con VirtualDub).
ACTUALIZACIÓN 2017/02/06 : He incluido tanto en la imagen como en el código Python en color azul los ejes de referencia del punto (la proyección del $(x_0,0,0,0),(0,x_1,0,0),(0,0,x_2,0),(0,0,0,x_3)$ puntos y los ejes de referencia generados con ellos).
0 votos
He empezado a encontrar algunas pistas: (1) es.wikipedia.org/wiki/ y (2) math.harvard.edu/archive/21b_fall_03/4dcube
0 votos
Lo que yo entiendo por un diagrama de Schlegel es que muestra puntos que no están en el interior del politopo (es decir, todos los puntos de un diagrama de Schlegel están en una faceta del politopo). Un diagrama de hipercubos muestra 8 cubos de 3; todos los puntos en las facetas e interiores de estos cubos de 3 pueden ser señalados. Los puntos del interior del hipercubo no están en el diagrama. Además, la faceta más externa está al revés; sus puntos interiores están en el "exterior". Esto es análogo al diagrama de Schlegel bidimensional de un 3-cubo.
0 votos
@DanMoore ¡gracias por el comentario! tienes razón, como también confirmé en la respuesta de abajo, el lenguaje escrito a veces no es perfecto, tratando de expresar ideas.El diagrama como dices muestra la proyección de la superficie del teseracto, no los puntos internos, por lo que mi primer intento, mostrado en la pregunta, era erróneo. Para ello, según la respuesta, primero proyecte la superficie del teseracto mediante un diagrama de Schlegel, y luego utilizando las mismas reglas de proyección, proyecte un $4D$ punto que se encuentra dentro del teseracto. Eso hace posible la visualización.