Gráficos 3D: Primera parte

Generalidades

Para la realización de gráficos en tres dimensiones, SageMath dispone de una gama bastante amplia de funciones, detallada en la documentación. Vamos a ver en esta guía tan sólo algunas de ellas.

Los principios generales de los gráficos en 3D son similares a los de los gráficos bidimensionales. Los objetos gráficos se combinan mediante la suma o la función sum. Todos ellos admiten el método show, que también actúa como función. Sus opciones más relevantes son:

  • aspect_ratio: terna que indica la proporción entre las escalas usadas en cada una de las tres direcciones del espacio;
  • frame: controla si se traza o no una caja englobando la escena tridimensional.

Varias funciones, como point3d, line3d, arrow3d, polygon3d o text3d, son extensiones de sus homólogas en el caso bidimensional y tienen una sintaxis similar.

La siguiente celda ejemplifica estos principios y algunas de las funciones citadas; presenta además la función sphere para la representación de esferas:

In [1]:
# Ejes y planos coordenados
e0 = vector((0,0,0)); e1 = vector((3,0,0)); e2 = vector((0,3,0)); e3 = vector((0,0,3))
planos = polygon3d([e0,e1,e1+e3,e3], opacity=0.4, color="red")
planos += polygon3d([e0,e2,e2+e3,e3], opacity=0.4, color="green")
planos += polygon3d([e0,e1,e1+e2,e2], opacity=0.4, color="blue")
ejes = sum([arrow3d(e0, pt, color="dimgray", width=2) for pt in [e1,e2,e3]])
texto = text3d("x", 1.05*e1) + text3d("y", 1.05*e2) + text3d("z", 1.05*e3)

# Línea poligonal salpicada de esferas
vertices = [(1.5+cos(t),1.5+sin(t),1.5+sin(3*t)) for t in [0,pi/18..2*pi]]
esferas = sum(sphere(pt, 0.1, color="goldenrod") for pt in vertices)
linea = line3d(vertices, radius=0.05, color="silver")

# Representación de la escena
show(ejes + planos + texto + esferas + linea, frame=False)

La función plot3d

La misión principal de esta orden es trazar el grafo de una función bivariada $f:D\subset\mathbb{R}^2\to\mathbb{R}$ sobre un rectángulo $[a_1,b_1]\times[a_2,b_2]$. Se usa con la siguiente sintaxis:

plot3d(fun, rango_x, rango_y, opciones),

donde:

  • fun es una expresión o una función que proporciona la función $f$,
  • rango_x es un par ($a_1$, $b_1$) o una terna (x, $a_1$, $b_1$),
  • rango_y es un par ($a_2$, $b_2$) o una terna (y, $a_2$, $b_2$).

Indicamos las principales opciones:

  • mesh: opción booleana que indica si se dibuja o no una malla sobre la superficie,
  • plot_points: entero, o par de enteros, que indica el número de puntos en cada dirección en los que evaluar la función;
  • opacity: número entre $0$ y $1$ que controla el grado de opacidad de la superficie (0=transparente, 1=opaca);
  • color: determina el color de la superficie.

También es posible añadir las opciones de show.

Demos algunos ejemplos. La celda siguiente traza y superpone los grafos de las funciones $\cos(xy)$ y $\operatorname{sen}(x^2-y^2)$ en el rectángulo $[-\pi,\pi]\times[-\pi,\pi]$:

In [2]:
var("x,y")
s1 = plot3d(cos(x*y), (x,-pi,pi), (y,-pi,pi), color="green", opacity=0.5, plot_points=60, mesh=True)
s2 = plot3d(sin(x^2-y^2), (x,-pi,pi), (y,-pi,pi), color="brown", plot_points=80)
show(s1+s2)

En el ejemplo previo, cada superficie tiene un único color. Para policromar la superficie, se usa la opción color en la forma color=(col, gama), donde col es una función bivariada con recorrido en el intervalo $[0,1]$ y gama es una gama de colores. Dentro de la gama, cada color está indexado mediante un número entre $0$ y $1$. Por ello, el punto $(x,y,z)$ de la superficie recibe el color que corresponde al número $\mathtt{col}(x,y)$.

La orden sorted(colormaps) proporciona una lista ordenada alfabéticamente de las gamas predefinidas de colores. Si 'gama' es uno de los elementos de esta lista, entonces colormaps.gama es el nombre correcto de la gama que hay que usar en la opción color.

Repetimos el ejemplo anterior. Una de las superficies se colorea con tonos verdes elegidos aleatoriamente; la otra superficie se pinta en tonos rojos siguiendo un patrón determinado (al punto $(x,y,z)$ se le asigna el tono de rojo que corresponde a (x^2+y^2)%1, esto es, la parte fraccionaria del número real $x^2+y^2$):

In [3]:
s1 = plot3d(cos(x*y), (x,-pi,pi), (y,-pi,pi), 
            color=(lambda x,y: random(), colormaps.Greens), 
            opacity=0.5, plot_points=60, mesh=True)
s2 = plot3d(sin(x^2-y^2), (x,-pi,pi), (y,-pi,pi), 
            color=(lambda x,y: (x^2+y^2)%1, colormaps.Reds), 
            plot_points=80)
show(s1+s2)

Dibujamos ahora la superficie $z=f(x,y)$, con $$ f(x,y) = \frac{2x+3y^2}{(x^2+y^2+1)^2} $$ en el rectángulo $[-2,2]\times[-2,2]$. Para hacernos una idea más cabal de la superficie, pintamos cada punto $(x,y,f(x,y))$ de acuerdo con el valor de $f$. Para definir la función que asigna el color, se necesita determinar previamente el rango de valores que toma $f$ en el rectángulo considerado, para lo cual basta con representar la superficie una vez, sin opciones añadidas, y observar el gráfico:

In [4]:
# Definición de f
def f(x,y): 
    return (2*x+3*y^2) / (x^2+y^2+1)^2
# Definición de col.
# Se necesita conocer previamente zmin y zmax tales que
# zmin <= f(x,y) <= zmax en el rectángulo considerado
zmin, zmax = -0.64, 0.84
def col(x,y): 
    return (f(x,y)-zmin)/(zmax-zmin)
gama = colormaps.jet
# Representación de la superficie
plot3d(f, (-2,2), (-2,2), color=(col,gama), mesh=True, aspect_ratio=[1,1,2])
Out[4]:

La función plot admite también la opción transformation, gracias a la cual se pueden representar fácilmente en el sistema de referencia cartesiano superficies dadas de forma explícita en otro sistema de coordenadas. Consideremos, por ejemplo, la superficie dada en coordenadas cilíndricas mediante la ecuación $$ r = 1 + e^{-z} \left(1+\cos 6\theta\right), $$ con $(\theta,z)\in[0,2\pi]\times[0,3]$. La siguiente celda muestra esta superficie:

In [5]:
# Cambio a coordenadas cilíndricas
var("r,θ,z")
Cilíndricas = (r*cos(θ), r*sin(θ), z)
# Representación de la superficie
plot3d(1+e^(-z)*(1+cos(6*θ)), (θ,0,2*pi), (z,0,3), transformation=Cilíndricas, 
       plot_points=[80,40], color="lightgreen", mesh=True)
Out[5]:

Pongamos otro ejemplo. Trazamos ahora la superficie $z=\operatorname{sen}(\pi r)\,\operatorname{sen}\theta$, con $(r,\theta)\in[0,5]\times[0,2\pi]$:

In [6]:
plot3d(sin(pi*r)*sin(θ), (r,0,5), (θ,0,2*pi), transformation=Cilíndricas, 
       plot_points=61, color="lightgreen", mesh=True, aspect_ratio=[1,1,1.75])
Out[6]:

Añadimos un último ejemplo en coordenadas esféricas. Representamos la superficie $\rho=(2-\varphi)(3+\cos6\theta)$, con $(\theta,\varphi)\in[0,2\pi]\times[-\pi/2,\pi/2]$:

In [7]:
var("ρ,θ,φ")
Esféricas = (ρ*cos(θ)*cos(φ), ρ*sin(θ)*cos(φ), ρ*sin(φ))
plot3d((2-φ)*(3+cos(6*θ)), (θ,0,2*pi), (φ,-pi/2,pi/2), transformation=Esféricas, 
       plot_points=[85,40], color="lightgreen", mesh=True)
Out[7]:

La función parametric_plot3d

La representación gráfica de curvas y superficies paramétricas se efectúa mediante la función parametric_plot3d.

Consideremos primero una curva dada en forma paramétrica mediante la ecuación $(x,y,z)=\boldsymbol{\varphi}(t)$, con $\boldsymbol{\varphi}=(\varphi_1,\varphi_2,\varphi_3):I\subset\mathbb{R}\to\mathbb{R}^3$ y $t\in[a,b]$. Para trazar esta curva, se usa parametric_plot3d con la siguiente sintaxis:

parametric_plot3d(fun, rango_t, opciones),

donde:

  • fun es una expresión o función que proporciona la parametrización $\boldsymbol{\varphi}$ como lista, tupla o vector,
  • rango_t es un par ($a$, $b$) o una terna (t, $a$, $b$).

Asimismo, para dibujar una superficie de ecuación paramétrica $(x,y,z)=\boldsymbol{\varphi}(u,v)$, con $(u,v)\in D=[a_1,b_1]\times[a_2,b_2]$ y $\boldsymbol{\varphi}=(\varphi_1,\varphi_2,\varphi_3):D\to\mathbb{R}^3$, la sintaxis que ahora adopta parametric_plot3d es

parametric_plot3d(fun, rango_u, rango_v, opciones).

Las opciones que admite parametric_plot3d son similares a las de plot3d. Demos algunos ejemplos. Representamos primero una hélice de ecuación $\boldsymbol{x}=(10\cos t, 10\operatorname{sen} t, t)$, con $t\in[0,6\pi]$:

In [8]:
var("t")
g(t) = (10*cos(t), 10*sin(t), t)
helice = parametric_plot3d(g(t), (t,0,6*pi), radius=0.5, plot_points=150, color="brown")
helice
Out[8]:

La hélice anterior, en realidad, está contenida en el helicoide de ecuación paramétrica $$ \left\{ \begin{aligned} &x=v\cos u, \\ &y=v\operatorname{sen} u, \\ &z=u, \end{aligned} \right. \qquad (u,v)\in[0,6\pi]\times[5,15] $$ Lo mostramos en la siguiente celda:

In [9]:
var("u,v")
def φ(u,v):
    return (v*cos(u), v*sin(u), u)
helicoide = parametric_plot3d(φ(u,v), (u,0,6*pi), (v,5,15), 
                              plot_points=[120,15], mesh=True, color="lightgreen")
show(helice + helicoide)

Pongamos otro ejemplo. Con el auxilio de la función plot3d, hemos representado antes la superficie de ecuación $z=\operatorname{sen}(\pi r)\,\operatorname{sen}\theta$ en coordenadas cilíndricas, con $(r,\theta)\in[0,5]\times[0,2\pi]$. En este caso, es obvio que tal superficie se expresa en forma paramétrica mediante la ecuación $$ (x,y,z) = \left(r\cos\theta, r\operatorname{sen}\theta, \operatorname{sen}(\pi r)\,\operatorname{sen}\theta\right). $$ Por ello, para trazar esta superficie se puede usar también la función parametric_plot3d:

In [10]:
var("r,θ")
g = (r*cos(θ), r*sin(θ), sin(pi*r)*sin(θ))
parametric_plot3d(g, (r,0,5), (θ,0,2*pi), plot_points=61, color="lightgreen", mesh=True, aspect_ratio=[1,1,1.75])
Out[10]:

Concluimos dibujando el toro de ecuación $$ (x,y,z) = \bigl((2+\cos v)\cos u, (2+\cos v)\operatorname{sen}u, \operatorname{sen}v\bigr), $$ con $(u,v)\in[0,2\pi]\times[0,2\pi]$:

In [11]:
var("u,v")
toro = ((2+cos(v))*cos(u), (2+cos(v))*sin(u), sin(v))
parametric_plot3d(toro, (u,0,2*pi), (v,0,2*pi), mesh=True, plot_points=[80,40],
                  color=(lambda u,v: sin(u/2)*(1+sin(v))/2,colormaps.Oranges_r))
Out[11]:
Celda de configuración. La evaluación de la celda siguiente cambia el formato por omisión de este cuaderno.
In [12]:
%%html
<style>
h1{text-align: center; color: rgb(185,74,72);}
h2{text-align: center; color: rgb(0,102,0); padding: 0.25em 0;
   border: 2px solid rgb(0,191,0); border-width: 2px 0;}
h3{border-bottom: 2px solid rgb(153,153,153);} 
h4{color: rgb(58,135,173); font-size: 115%!important;
   font-weight: bold!important;}
.text_cell_render{font-family: "Trebuchet MS",Geneva,sans-serif;
                  font-size: 110%; line-height: 1.5;}
.MathJax_Display{margin: 0.5em 0;}
</style>
Realizado por Juan José Torrens para la asignatura de Cálculo II
Grados de Ciencias y de Ciencia de Datos
Universidad Pública de Navarra
Última versión: 14-1-2021