https://github.com/cj-holmes/gglcd
Programmatic production of Liquid Crystal alignment diagrams
https://github.com/cj-holmes/gglcd
Last synced: 5 months ago
JSON representation
Programmatic production of Liquid Crystal alignment diagrams
- Host: GitHub
- URL: https://github.com/cj-holmes/gglcd
- Owner: cj-holmes
- Created: 2020-07-22T19:32:15.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2020-07-26T19:38:52.000Z (over 4 years ago)
- Last Synced: 2024-07-28T15:34:55.611Z (9 months ago)
- Language: R
- Size: 29 MB
- Stars: 2
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.Rmd
Awesome Lists containing this project
- awesome-r-dataviz - gglcd - About (ggplot / Domain-specific)
README
---
output: github_document
---```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
gganimate = list(nframes = 50, fps = 25, duration = 8, end_pause = 40)
)
```# gglcd
**`gglcd` is a work in progress - it may not be working properly, so use it at your own risk!**
The goal of `gglcd` is to programmatically create schematic liquid crystal (LC) alignment diagrams.
`gglcd` has two features:
* A ggplot2 geom and stat (`geom_lc()` and `stat_lc()`) for fine control
* A convenient wrapper function `lcd()` for simple alignment diagrams## Example use
```{r message=FALSE, warning=FALSE}
library(gglcd)
library(tidyverse)
```For quick LC alignment diagrams you can use `lcd()` and specify the bottom and top anlgle arguments. Under the hood, this is creating an `angle_function` (the third argument to `lcd()`) that varies the angle linearly from the bottom of the diagram to the top of the diagram. Note that all themeing can be removed by setting `themeing = FALSE`. Setting a seed ensures that the same diagram can be recreated.
```{r fig.width=5, fig.height=5}
lcd(45, -45, seed=1)
lcd(45, -45, seed=1, themeing = FALSE)
```The vertical y-scale of the plotting area always ranges from 0 to 1 (so the plotting area always has a height of 1). The width of the diagram can be varied by specifying the `diagram_aspect` argument which is defined as width/length. `lcd()` returns a ggplot2 object, so further layers cab be added as normal
```{r fig.width=5, fig.height=5}
lcd(0, 90) +
geom_hline(yintercept = 0.5, col="orange", size=2)+
labs(title = "Title", subtitle = "Subtitle")
```There are a whole load of other arguments to `lcd()` to control the format of the output
```{r}
args(lcd)
```For example, the aspect of the diagram can be changed, bounding surfaces applied and colour elements edited.
```{r fig.width=8, fig.height=4}
lcd(0, 360,
diagram_aspect = 2,
surface_b = 0.1, surface_t = 0.05,
bg_fill = "aquamarine", lc_fill="white")
```Non-linear changes in angle can also be specified by the `angle_function` argument. Here I create a sinusoidally varying angle between 0.2 and 0.8 (the vertical scale of the plot is always 0 to 1).
```{r fig.width=5, fig.height=5}
x <- seq(0.2,0.8,l=1000)
y <- sin(seq(0,2*pi,l=1000))*90
af <- approxfun(x,y, rule=2)lcd(angle_function = af, seed=1, show_function = TRUE, angle_n=2)
```## Geoms and stats
The `lcd()` function described above is a wrapper aronud `geom_lc()` and `stat_lc()`. These functions can be used as normal with a call to `ggplot()` for full control over the diagram creation, including the ability to make additional aesthetic mappings.A particularly useful argument to `lcd()` is `return_df`. If set to `TRUE` (default is `FALSE`), a dataframe of the required aesthetic mappings (x, y, angle, length, width) is returned, rather than a plot. This is a convenient way to produce a dataframe that can be passed to `ggplot()` (example below).
This example also makes use of the ability to map non-required aesthetics. The fill is mapped to angle.
```{r}
lcd(-180, 180, return_df = T, seed=1) %>%
print() %>% # view dataframe
ggplot(aes(x, y, angle=angle, length=length, width=width))+
geom_lc(aes(fill=angle))+
coord_fixed()+
scale_fill_gradient2()
```As length is a required aesthetic for `geom_lc()`, it can be varied per LC molecule. Here I have created a function that returns the length as a function of the position in y for the 'twist' category (approximating the projection of the rotated molecule onto the x-y plane). This is then mutated onto the dataframe before plotting.
```{r fig.width=10}
# Define inputs
lc_length <- 0.05
lc_width <- lc_length/3# Approximate the length projection on to the x-y plane
twist_proj <- approxfun(seq(0,1,l=1000), cos(seq(0, pi/2, l=1000))*lc_length)bind_rows(
lcd(50, -50, lc_length=lc_length, lc_width=lc_width, return_df = TRUE) %>% mutate(label = "Splay"),
lcd(50, 130, lc_length=lc_length, lc_width=lc_width, return_df = TRUE) %>% mutate(label = "Bend"),
lcd(0, 0, lc_length = lc_length, lc_width=lc_width, return_df = TRUE) %>%
mutate(length = twist_proj(y),
length = case_when(length < lc_width ~ lc_width,
TRUE ~ length),
label = "Twist")
) %>%
ggplot(aes(x, y, angle=angle, length=length, width=width))+
geom_lc()+
facet_wrap(~label)+
coord_fixed()+
theme(legend.position = "none")
```### Using ellipse polygons
If using the `lc_shape = 'ellipse'` option, a further argument `ellipse_res` can be used to control the number of equally spaced points (in angle) that make up each individual ellipse. The default is `ellipse_res = 35````{r}
tibble(x=0.5, y=0.5, angle=45, length=1, width=1/4) %>%
ggplot(aes(x, y, angle=angle, length=length, width=width))+
geom_polygon(stat="lc", aes(col="rectangle"), fill=NA, size=1)+
geom_polygon(stat="lc", lc_shape='ellipse', ellipse_res = 60, aes(col="ellipse_res = 60"), fill=NA, size=1)+
geom_polygon(stat="lc", lc_shape='ellipse', ellipse_res = 35, aes(col="ellipse_res = 35"), fill=NA, size=1)+
geom_polygon(stat="lc", lc_shape='ellipse', ellipse_res = 10, aes(col="ellipse_res = 10"), fill=NA, size=1)+
geom_polygon(stat="lc", lc_shape='ellipse', ellipse_res = 5, aes(col="ellipse_res = 05"), fill=NA, size=1)+
coord_fixed()
```## Animations
With the super useful `gganimate` package it is also possible to create custom animations using `geom_lc()`.
```{r echo=FALSE, cache = TRUE}
library(EricksenLeslie)
library(gganimate)k <- c(.62,.39,.89) * 1e-11
alpha <- c(-0.006,-0.0824,-0.0036,0.0652,0.0640,0)
epsilon <- c(7,11.5)
depth <- 27.3e-6
A <- 45
pgx <- seq(0, -13e+4, l=100)n <- 45
lc_length <- 0.05lc <- lc_switching_sequence(nz = n,
times = seq(0, 10*length(pgx), l=length(pgx)),
voltages = rep(0, length(pgx)),
freq = rep(1,length(pgx)),
depth = depth,
k = k ,
epsilon = epsilon,
tilt_easy_angle = c(90*pi/180, 90*pi/180),
twist_easy_angle = c(45*pi/180, 45*pi/180),
pgx = pgx,
pgy = rep(0,length(pgx)),
alpha = alpha,
shear = TRUE,
niter=20,
ntol=1e-6,
ttol=1e-5)df <-
crossing(x = seq(0,1,l=n), y=seq(0,1,l=n)) %>%
mutate(xj = x + runif(n^2, -lc_length/2, lc_length/2),
yj = y + runif(n^2, -lc_length/2, lc_length/2))anim_df <-
purrr::map_dfr(seq(1, 100, by=1), function(b){
af <- approxfun(x = seq(0,1,l=n), y = lc$tilt[,b]*(180/pi) + 90, rule = 2)
df %>% mutate(angle = af(yj)) %>% mutate(step=as.character(b))
})anim_df %>%
mutate(length=lc_length, width=0.05/4) %>%
mutate(step = forcats::fct_inorder(step)) %>%
ggplot(aes(xj, yj, angle=angle, width=width, length=length, fill=angle))+
geom_lc()+
coord_fixed()+
scale_fill_gradient2(midpoint = 180)+
transition_states(step)
``````{r eval=F, echo=FALSE}
n <- 35
lc_length <- 0.05crossing(x = seq(0,1,l=n), y=seq(0,1,l=n)) %>%
arrange(y) %>%
mutate(xj = x + runif(n^2, -lc_length/2, lc_length/2),
yj = y + runif(n^2, -lc_length/2, lc_length/2)) %>%
mutate(length=lc_length, width=0.05/4, angle=seq(0, 360, l=n^2)) %>%
ggplot(aes(x, y, angle=angle, width=width, length=length, fill=angle))+
geom_lc()+
# geom_point(aes(x, y), col=2)+
coord_fixed()+
scale_fill_gradient2(midpoint = 180, breaks=seq(0,360,by=90))lcd(0, 90, 1000, return_df = T)
```