Visualizando secciones cónicas con ggplot

Rafa · 2018/08/16 · 4 minute read

En este post exploro un poco algunas secciones cónicas usando ggplot y LaTeX.

Círculo

La ecuación genérico de un círculo es

\[ (x-h)^2+(y-k)^2=r^2 \]

donde \((h, k)\) es el centro y \(r\) el radio.

Si quiero dibujar un círculo que pasa por (1, 4) y tiene centro en (-2, -3).

# distancia euclideana
dist <- function(x, y) { sqrt(sum((x-y)^2)) }
centro <- c(-2, -3)
punto <- c(1, 4)
#  Hallar el radio como la distancia entre el punto y el centro
radio <- dist(centro, punto)
radio^2
## [1] 58

La ecuación del círculo es: \[(x+2)^2 + (y+3)^2 = 58\]

Este post en Stack Overflow explica como graficar una función en forma paramétrica usando annotate.

library(ggplot2)
library(latex2exp)

center <- c(x=3, y=1)
#pt1 <- c(x=1, y=1)
#pt2 <- c(x=5, y=1)
pt3 <- c(x=3, y=3)
pt4 <- c(x=3, y=-1)
pts <- dplyr::bind_rows(center,pt3, pt4)
radius <- 2
t <- seq(0, 2*pi, length.out = 100)

g <- ggplot() + 
  geom_segment(aes(x=-1, xend = 6, y=0, yend=0),
               size = 0.5,
               arrow = arrow(length = unit(0.2, "cm"))) + # Eje X
  geom_segment(aes(x=0, xend=0, y=-1, yend=4),
               size = 0.5,
               lineend = "butt",
               arrow = arrow(length = unit(0.2, "cm"))) + # Eje Y
  annotate("path", 
           x = center[1] + radius * cos(t),
           y = center[2] + radius * sin(t)) + 
  geom_point(data = pts, aes(x=x, y =y)) + 
  geom_text(data = pts,
            parse = TRUE,
            nudge_y = 0.2,
            aes(x=x, y=y, label = paste0("list(", x ,",", y, ")"))) +
  annotate("point", x=1, y=1) + 
  annotate("text", x=1.25, y=1, label="1,1") + 
  annotate("point", x=5, y=1) + 
  annotate("text", x=4.75, y=1, label="5, 1") + 
  labs(x="", y="") + 
  ggtitle(TeX("(x-3)^2+(y-1)^2=4")) + 
  coord_fixed()
g

Parábola

Una parábola es un conjunto de puntos que equidista de una línea (directriz) y un punto (foco). Estos puntos satisfacen:

\((x-h)^2=4p(y-k),\, (p \neq 0)\)

El vértice de la parábola está en \((h,k)\) y la directriz \(y=k-p\). El foco está en \((h, k+p)\) si la parábola es abierta hacia arriba.

Ejemplo

\(x^2=16y\) es una parábola con vértice en \((0,0)\), \(p = 4\) y foco en \((0,4)\).

focus <- list(x=0, y=4, lab = "Foco")
vertex <- list(x=0, y=0, lab = "Vértice")
notable_points <- bind_rows(focus, 
                            vertex)
pt <- data_frame(x=5, y=round(5^2/16, 2))

p <- ggplot(data_frame(x=0), aes(x=x)) +
  geom_segment(aes(x=-9, xend = 9, y=0, yend=0),
               size = 0.5,
               arrow = arrow(length = unit(0.2, "cm"))) + # Eje X
  geom_segment(aes(x=0, xend=0, y=-5, yend=10),
               size = 0.5,
               lineend = "butt",
               arrow = arrow(length = unit(0.2, "cm"))) + # Eje Y
  stat_function(fun = function(x) { x^2 / 16}) + 
  xlim(-9, 9) + 
  ylim(-5, 10)

x <- 5

g_2 <- p +
  geom_point(data = notable_points, aes(x=x, y=y)) + 
  geom_text(data=notable_points, 
            size = 3,
            nudge_y = 1.25,
            nudge_x = 1.25,
            aes(label = paste0(lab, "\n(", x, ",", y, ")"), 
                x = x, y = y)) + 
  geom_hline(yintercept = -4, linetype = "dashed") + 
  annotate("text", x= 3, y= -3.3, label = "Directriz", size = 3) +
  ggtitle(TeX('$y=x^2/16$')) + 
  theme(
    plot.title = element_text(hjust = 0.5)
  ) + geom_point(data = pt, aes(x=x, y=y)) + 
  geom_segment(aes(x= focus$x, y=focus$y, xend=pt$x, yend=pt$y),
               linetype = "dashed") + 
  geom_segment(aes(x=pt$x, y=pt$y, xend=pt$x, yend = -4),
               linetype = "dashed") + 
  annotate("text", 
           label = "phantom(0) == phantom(0)", 
           parse = TRUE,
           angle = 60,
           size = 5,
           x = 2.5, y= 2.85) + 
  annotate("text", 
           label = "phantom(0) == phantom(0)", 
           parse = TRUE,
           size = 5,
           x = 5, y= -2)
g_2

Todos los puntos de la parábola estan a la misma distancia del foco que de la directriz. Sería interesanter armar una app de shiny para mover los puntos notables.

Una parábola con eje horziontal

Dibujar una parábola con eje vertical es simple porque podemos usar stat_fun. Pero si queremos dibujarlas con eje horizontal no podemos escibir los puntos de la parábola como un función y pasarsela a stat_function.

La ecuación \(y^2+6y+8x+25 = 0\) no parece una parábola, pero si completamos el cuadrado:

\[\begin{align} y^2+6y+8x+25 &= 0\\ y^2+6y+9+8x+25-9 &= 0\\ (y+3)^2 &= -16-8x\\ (y+3)^2 &= 4(-2)(x+2) \end{align}\]

Es una parábola con vértice \((-2, -3)\), p es \(-2\), el foco \((-4,-3)\) la directriz es \(x=0\).

Pero como la orientación de esta parábola es horizontal, no es una función. Cómo la graficamos con ggplot?

Si despejamos \(y\) en función de \(x\), obtenemos dos funciones: \[\begin{align} (y+3)^2 &= (-8)(x+2)\\ y &= -3 \pm \sqrt{-8(x+2)}\\ y_1 &=-3 + \sqrt{-8(x+2)}\\ y_2 &=-3 - \sqrt{-8(x+2)}\\ \end{align}\]

Podemos graficar \(y1\) y \(y2\):

x <- seq(-5, 0, length.out=1e3)
lbs <- list(bquote(-3 + sqrt(-8*(x+2))), # investigar bquote!
            bquote(-3 - sqrt(-8*(x+2))))

g_3 <- ggplot(data_frame(x=x), 
            aes(x=x, color = color)) + 
  stat_function(data = data_frame(x=x, color = factor(1)),
                fun = function(x) { -3 + sqrt(-8*(x+2)) }) + 
  stat_function(data = data_frame(x=x, color = factor(2)),
                fun = function(x) { -3 - sqrt(-8*(x+2)) }) + 
  scale_color_manual(labels = lbs,
                     name = "",
                     values = c(scales::hue_pal()(2)[1], 
                                scales::hue_pal()(2)[2])) + 
  geom_hline(yintercept = 0) + 
  geom_vline(xintercept = 0)
g_3