Fish-Eye Lens Effect with ggplot2
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
Fascination with “Warping” …
The “fish-eye” lens has always fascinated me. It was my gateway into photography—its quirky, distorted charm never failed to add whimsy to my shots. Some of my favourite memories are capturing our furry friends🐶🐽🐄.
Recently, I started wondering why not bring that same fish-eye🐟👁️ magic into my data visualizations. While it may not suit business dashboards, it’s perfect for artistic visualizations. With ggplot2’s coord_trans()
and its "pseudo_log"
transformation as inspiration, I decied to push the boundaries and create my first custom transformation.
Digression : The Seigaha Motif as a Canvas 🎨
To test this idea, I used a Seigaha motif I created the other day. It’s a geometric design with overlapping circles that naturally lends itself to experimentation. I introduced randomness to the colors using irrational numbers as denominators, leveraging R’s modulo operation for real numbers. Just another attempt at creating “pseudo-randomness” in the design.
Famous “Irrational” Numbers in Math
Golden Ratio
The Golden Ratio is often represented by the Greek letter and is defined as:
Silver Ratio
The lesser-known Silver Ratio, represented by , is defined as:
Euler’s Number
Euler’s number, represented by , is a fundamental constant in mathematics:
It is approximately
.
Code
library(tidyverse) # Easily Install and Load the 'Tidyverse' library(ggforce) # Accelerating 'ggplot2' library(cowplot) # Streamlined Plot Theme and Plot Annotations for 'ggplot2' library(gt) # Easily Create Presentation-Ready Display Tables ### just declaring the colour palette here col10 <- str_split("001219-005f73-0a9396-94d2bd-e9d8a6-ee9b00-ca6702-bb3e03-ae2012-9b2226","-") col10_pal <-str_c("#",col10 |> unlist()) ### Creating data frame that is basis of layout df <- expand_grid(x = seq(-12, 12, by = 2), y = seq(-8, 8, by = 1)) |> arrange(y,x) |> mutate(y_odd=(y%%2==1)) |> mutate(idx=row_number()-1) |> mutate(x=if_else(y_odd,x+1,x)) ### For each grid location, I want 6 concentric circles r_values <- seq(0.1,1,length.out=6) ### Just defining some "Irregular Numbers" that I can use with Modulo gr <- (1 + sqrt(5)) / 2 ## the golden ratio sr <- (1 + sqrt(2)) ##. lesser known silver ratio euler_num <- exp(1) df_long <- df |> expand_grid(r=r_values) |> mutate(r_var=r*(idx%%sr)+1) ## I'm experimenting here with modulo with pi
Cartesian Coordinates: The Baseline
Here’s the unaltered design using coord_fixed
, maintaining regular Cartesian coordinates.
Code
df_long |> arrange(-y,idx, desc(r)) |> ggplot() + geom_circle(aes(x0=x,y0=y,r=r,fill=r_var), linewidth=0.1,color="#fffff3de", linetype=1) + coord_fixed(clip="on", xlim=c(-11,11),ylim=c(-7,7)) + theme_nothing() + scale_fill_gradientn(colors=col10_pal) + scale_color_gradientn(colors=col10_pal) + #coord_trans(x="pseudo_log",y="pseudo_log",clip="on", xlim=c(-11,11),ylim=c(-7,7)) + theme(plot.margin=unit(c(0,0,0,0),"mm"))
Entering the World of Distortion
Pseudo-Log Transformation
Warping begins with using pseudo_log
transformation with coord_trans
creates an intriguing distortion:
Code
df_long |> arrange(-y,idx, desc(r)) |> ggplot() + geom_circle(aes(x0=x,y0=y,r=r,fill=r_var), linewidth=0.1,color="#fffff3de", linetype=1) + #coord_fixed(clip="on", xlim=c(-11,11),ylim=c(-7,7)) + theme_nothing() + scale_fill_gradientn(colors=col10_pal) + scale_color_gradientn(colors=col10_pal) + coord_trans(x="pseudo_log",y="pseudo_log",clip="on", xlim=c(-11,11),ylim=c(-7,7)) + theme(plot.margin=unit(c(0,0,0,0),"mm"))
Custom Fisheye Transformation
Inspired by radial distortion formula, I’ve attempt to write a custom fisheye transformation.
k controls the intensity of the distortion.
- Positive k = Barrel Distortion (like Fisheye)
- Negative k = Pincusion Distortion (Inverse Fisheye).
- r’ is pronouced as “r prime”
Here’s the implementation:
Code
library(scales) # Define a fisheye transformation using trans_new fisheye_trans <- function(k = 0.01) { trans_new( name = "fisheye", transform = function(r) r * (1 + k * r^2), # Forward transformation inverse = function(r_prime) r_prime / (1 + k * r_prime^2) # Inverse transformation ) } # Create the fisheye transformation object fisheye <- fisheye_trans(k = 0.03) df_long |> arrange(-y,idx, desc(r)) |> ggplot() + geom_circle(aes(x0=x,y0=y,r=r,fill=r_var), linewidth=0.1,color="#fffff3de", linetype=1) + #coord_fixed(clip="on", xlim=c(-11,11),ylim=c(-7,7)) + theme_nothing() + scale_fill_gradientn(colors=col10_pal) + scale_color_gradientn(colors=col10_pal) + #coord_trans(x="pseudo_log",y="pseudo_log",clip="on", xlim=c(-11,11),ylim=c(-7,7)) + coord_trans(x=fisheye, y=fisheye) + theme(plot.margin=unit(c(0,0,0,0),"mm"))
The result didn’t quite meet my expectations.. 😅🥹, but it’s a start…!
Experimenting with Modulus Transformation in scales
package 📦
I also explored transformations like modulus, which yielded effects closer to the fish-eye look I envisioned:
Code
# Define the modulus transformation with a specific parameter (e.g., p = 0.5) mod_trans_y <- modulus_trans(p = 0.01) df_long |> arrange(-y,idx, desc(r)) |> ggplot() + geom_circle(aes(x0=x,y0=y,r=r,fill=r_var), linewidth=0.1,color="#fffff3de", linetype=1) + #coord_fixed(clip="on", xlim=c(-11,11),ylim=c(-7,7)) + theme_nothing() + scale_fill_gradientn(colors=col10_pal) + scale_color_gradientn(colors=col10_pal) + coord_trans(x=mod_trans_y ,y=mod_trans_y ,clip="on", xlim=c(-11,11),ylim=c(-7,7)) + theme(plot.margin=unit(c(0,0,0,0),"mm"))
Code
mod_trans_x <- modulus_trans(p = 1.5) df_long |> arrange(-y,idx, desc(r)) |> ggplot() + geom_circle(aes(x0=x,y0=y,r=r,fill=r_var), linewidth=0.1,color="#fffff3de", linetype=1) + #coord_fixed(clip="on", xlim=c(-11,11),ylim=c(-7,7)) + theme_nothing() + scale_fill_gradientn(colors=col10_pal) + scale_color_gradientn(colors=col10_pal) + coord_trans(x=mod_trans_x ,y=mod_trans_y ,clip="on", xlim=c(-11,11),ylim=c(-7,7)) + theme(plot.margin=unit(c(0,0,0,0),"mm"))
Final Thoughts
Experimenting with warping in ggplot2 opens up a playful avenue for some quirky visualizations. I’m now thinking what to distort next!
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.