class: split-square2, dark <!-- From Some Summer Long Removed Biting Back When Reality Bites Can Get You a Loose Truth Climbing Down from the Shoulders of Giants --> .column.right[ .vmiddle[ # Schneirographs ## Three Numbers & One Equation ## by W. Joel Schneider ] ] .column.p-4[ <!-- --> <img src="tintedrainbow.svg" type="image/svg+xml"> <!-- --> ] --- class: split-square2, dark .fixed-bottom.left.p-4.w-1.light[Press `f` on your keyboard to see in full screen mode. Press `o` to see an overview of all the slides. To see the code that generated each spirograph, press `p`. Other commands can be seen by pressing `h` for help.] .column.light[ .vmiddle.center[ # Welcome, # Everyone! ] ] .column[ <!-- --> <img src="viridis_weave.svg" type="image/svg+xml"> <!-- --> ] ??? ```r library(spiro) library(tidyverse) library(LaCroixColoR) library(scico) library(dutchmasters) library(ochRe) library(scales) myfont <- "Cormorant" bg_light <- "#f0f2f5" bg_dark <- "#282829" spiro( fixed_radius = 1231, cycling_radius = 529, pen_radius = 1233, colors = viridis::viridis(67), color_groups = 67, color_cycles = 59, windings = 96, points_per_polygon = 50, file = "viridis_weave.svg" ) %>% add_background(color = bg_dark) ``` --- class: middle, left, split-three .column.font2.p-2.light[.vmiddle.center[ As a kid, I *loved* making spirographs. I still do. ]] .column.font2.p-2.dark[.vmiddle.center[ Making them feels more like discovery than creativity, like finding hidden wings in the Mathematical Museum of Art. ]] .column.font2.p-2.light[.vmiddle.center[ I have not yet found the point where spirographs do not surprise me. ] ] --- class: split-square2 .column.dark[ .vmiddle.p-4[ The surprising variety of forms generated by spirographs are manifestations of just one equation, the [circular path troichoid](https://www.wikiwand.com/en/Hypotrochoid). The shape of the spirograph depends on the radius of a fixed circle, radius of a cycling circle, and the distance of the pen from the center of the the cycling circle. $$ `\begin{align} x (\theta) &= (R - r)\cos\theta + d\cos\left({R - r \over r}\theta\right)\\ y (\theta) &= (R - r)\sin\theta - d\sin\left({R - r \over r}\theta\right) \end{align}` $$ * *R* is the radius of the fixed circle * *r* is the radius of the cycling circle * *d* is the distance of the pen from the center of the cycling circle * *θ* is the number of radians the cycling circle travels around the fixed circle * *x*(*θ*) is the position of *x* after the cycling circle travels *θ* radians * *y*(*θ*) is the position of *y* after the cycling circle travels *θ* radians ] ] .column[ <!-- --> <img src="spiro_parameters.svg" type="image/svg+xml"> <!-- --> ??? ```r cycling_radius <- 1 fixed_radius <- 3 pen_radius <- 2 d_circle <- tibble( x0 = c(0, fixed_radius - cycling_radius), y0 = c(0, 0), radius = c(fixed_radius, cycling_radius), r_y = c(0, 0), r_x = c(-fixed_radius / 2, fixed_radius - 1.5 * cycling_radius), r_lab = c("Fixed\nRadius", "Cycling\nRadius"), color = c("black", "royalblue") ) d_segment <- tibble( x = c(0, fixed_radius - cycling_radius, fixed_radius - cycling_radius), y = c(0, 0, 0), xend = c(-fixed_radius + 0.04, fixed_radius - cycling_radius * 2 + 0.04, fixed_radius - cycling_radius + pen_radius - 0.04), yend = c(0, 0, 0), color = c("black", "royalblue", "firebrick") ) ggplot(data = d_circle) + theme_void() + ggforce::geom_circle( aes( x0 = x0, y0 = y0, r = radius, color = color), n = 1000) + coord_equal() + geom_text( aes( x = r_x, y = r_y, label = r_lab, color = color), family = myfont, vjust = 0.5, nudge_y = 0.015, angle = 0) + annotate( x = fixed_radius - cycling_radius + pen_radius / 2, y = 0.015, geom = "label", family = myfont, color = "firebrick", label = "Pen\nDistance", label.size = 0, label.padding = unit(3, "pt")) + geom_segment( data = d_segment, aes(x = x, y = y, xend = xend, yend = yend, color = color), geom = "segment", linejoin = "mitre", arrow = arrow( length = unit(0.025, "npc"), type = "closed", angle = 15)) + annotate( x = fixed_radius - cycling_radius, y = 0, geom = "point", color = "royalblue") + scale_color_identity() + theme(legend.position = "none", plot.background = element_rect(colour = "#f0f2f5")) ggsave("spiro_parameters.svg", width = 7, height = 7) ``` ] --- class: split-square1, dark .column.bg-white[ ![](animation_spiro.gif) ] .column.dark[ .vmiddle.font2.p-5[ In this spirograph,<br>the fixed radius *R* is 3,<br>the cycling radius *r* is 1,<br>and the pen radius *d* is 2. ] ] --- class: split-square2 .column.dark[ .vmiddle.font2.p-5.pr-0.m-2[ Setting the cycling radius to a negative value will make the cycling circle rotate around the outside of the fixed circle. ] ] .column[ ![](animationsvg.gif) ] --- class: split-square2, dark .column.light[.vmiddle.p-5.font_large[ Although I still like making spirographs by hand, I wanted to extend what could be done with the traditional spirograph. I wrote the [spiro ](https://wjschne.github.io/spiro/articles/HowToUse/spiro.html) package in R to make images that would be impossible to create on paper. ]] .column.dark.font-medium[.vmiddle.p-5.font_large[ I cannot usually predict what will happen when I play with the three primary numbers of the equation. However, once a certain combination strikes me as interesting, I play with cutting it into different color segments to see if something of further interest happens. Sometimes I merge many spirographs and spin them to see if the emerging patterns are pleasing. ]] --- class: middle, split-square1, dark .column.light.font3.center[.vmiddle.p-5[ Here I demonstrate what can be done with [spiro ](https://wjschne.github.io/spiro/articles/HowToUse/spirograph.html) package. ]] .column.dark.font3.center[.vmiddle.p-5[ I would love to see what you can do with it. ]] --- class: split-square1 .column[ <!-- --> <img src="my_non_canonical_backstory.svg" type="image/svg+xml" width="100%", height="100%"> <!-- --> ] .column.p-2.light[ .vmiddle.center[ # My Non- # Canonical # Backstory ] ] ??? ```r k <- 8 crossing(cycling_radius = 1:k, fixed_radius = k * 2 + 1) %>% rowid_to_column("id") %>% mutate( colors = lacroix_palette("Coconut", n = k , "continuous"), file = paste0("sdfds.", id, ".svg") ) %>% select(-id) %>% pmap( spiro, points_per_polygon = 300, draw_fills = F, transparency = 0.9) %>% image_merge( output = "my_non_canonical_backstory.svg") %>% add_background() ``` --- class: split-square2 .column.dark[ .vmiddle.center[ # Licorice Donut # Vivisection ] ] .column.p-3[ <!-- --> <img src="licorice_donut_vivisection.svg" type="image/svg+xml", > <!-- --> ] ??? ```r rainbow_colors <- hsv( h = seq(1 / 16, 1, length.out = 16), s = 0.7, v = 0.7) spiro( fixed_radius = 16, cycling_radius = 5, pen_radius = 5, file = "licorice_donut_vivisection.svg", color_groups = 16, color_cycles = 2, points_per_polygon = 50, colors = rainbow_colors, transparency = 0.7) %>% add_background_gradient( colors = c("white", "black", "black", "white"), stops = c(.27, .34, .93, 1), rounding = 1, radius = 1) ``` --- class: split-square1, dark .column.p-3.bg-white[ <!-- --> <object id="osloaster" class="spiro" data="oslo_aster.svg" type="image/svg+xml"></object> <!-- --> <!-- --> <button id="osloAsterButton" type="button" class="playbtn btn btn-light position-absolute bottom-0 start-0 m-2" onclick='playpause("osloaster", this);'>Play</button> <!-- --> ] .column.p-5.dark[ .vmiddle.center[ # When We # Meet Again, # Will We Be # .small[As] We Were? ] ] ??? ```r n <- 10 oslo_colors <- scico(n = n, palette = "oslo", alpha = 0.9) %>% rev() spiro(file = "oslo_aster.svg", rotation = pi / 6, points_per_polygon = 100) %>% image_merge(output = "oslo_aster.svg", copies = n) %>% add_fills(colors = oslo_colors) %>% image_scale(scale = sqrt(0.75 ^ (seq(0, n - 1)))) %>% image_spin(rpm = 1:n + 1) %>% add_background(color = "black", rounding = 1) %>% pause_onload() ``` --- class: split-square2, dark .column.p-5.light[ .vmiddle.center[ # But for the # Darkness, # Nothing # Shimmers ] ] .column[ <!-- --> <img src="but_for_the_darkness_nothing_shimmers.svg" type="image/svg+xml"> <!-- --> ] ??? ```r set.seed(105) k <- 15 bg_colors <- paste0("gray", sample(1:k, k)) bg_stops <- sort(runif(k)) spiro( fixed_radius = 2 * 13 * 17, cycling_radius = 3 * 11 * 19, pen_radius = 171, file = "but_for_the_darkness_nothing_shimmers.svg", draw_fills = F, line_width = 3, color_groups = 380, color_cycles = 31, points_per_polygon = 100, colors = c( scico(60 * 2, palette = "lisbon", 0.8), scico(40 * 2, palette = "cork", 0.25), scico(20 * 2, palette = "lisbon", 1))) %>% add_background_gradient(rounding = 0, colors = bg_colors) ``` --- class: split-square1, dark .column.p-4.bg-white[ <!-- --> <img src="youre_my_favorite.svg" type="image/svg+xml"> <!-- --> ] .column.p-5.dark[ .vmiddle.center[ # You're My # Favorite ] ] ??? ```r k <- 36 files <- paste0("s", 1:k, ".svg") pen_radii <- seq(3.8, 1.5, length.out = k) alphas <- rep_len(c(0.85, rep(0.2, 4)), k) colors <- rep_len(scico(6, palette = "devon"), k) %>% alpha(., alpha = alphas) tibble::tibble( file = files, pen_radius = pen_radii, colors = colors) %>% purrr::pmap_chr( spiro, fixed_radius = 7, cycling_radius = 4, rotation = -pi / 10, points_per_polygon = 500, draw_fills = T, xlim = c(-7, 7), ylim = c(-7, 7)) %>% image_merge( output = "youre_my_favorite.svg") %>% add_lines(colors = c(rep(NA,k - 1), "gray")) %>% image_rotate(degrees = (1:k / 2.5)) %>% add_background_gradient( colors = c( "#FFFFFF", "#26588E", "#E5E3F9", "#283568", bg_light), radius = 1, rounding = 1, stops = c(0.42,0.93,0.96,0.97,1)) ``` --- class: split-square2, dark .column.p-4.light[ .vmiddle.center[ # Ride Ahead # .small[.small[to]] Make the Fire ] ] .column[ <!-- --> <img src="ride_ahead_to_make_the_fire.svg" type="image/svg+xml"> <!-- --> ] ??? ```r set.seed(23) k <- 20 low <- 5 high <- 10 bg_colors <- paste0("gray", sample(low:high, k, replace = T)) bg_stops <- sort(runif(k, min = 0, max = .77)) spiro( fixed_radius = 359, cycling_radius = 261, pen_radius = 40, color_groups = 36, color_cycles = 36, draw_fills = F, points_per_polygon = 20, line_width = 3.5, file = "ride_ahead_to_make_the_fire.svg", colors = c( div_gradient_pal( low = "royalblue4", mid = "black", high = "firebrick4")(seq(0, 1, length.out = 18)), div_gradient_pal( low = "royalblue", mid = "white", high = "firebrick")(seq(0, 1, length.out = 18)))) %>% add_background_gradient(rounding = 0, colors = bg_colors, stops = bg_stops) %>% add_circle(color = "gray10", r = c(0.429, 0.48, 0.559,0.669,0.786, 0.886), line_width = 1.5) ``` --- class: split-square1, dark .column[ <object id="suspensionofdisbelief" class="spiro" data="suspension_of_disbelief.svg" type="image/svg+xml"></object> <!-- --> <button type="button" class="playbtn btn btn-light position-absolute bottom-0 start-0 m-2" onclick='playpause("suspensionofdisbelief", this);'>Play</button> <!-- --> ] .column.p-5.dark[ .vmiddle.center[ # Suspension # of Disbelief ] ] ??? ```r n <- 20 spiro( fixed_radius = 4, cycling_radius = 5, points_per_polygon = 500, pen_radius = 1, file = "suspension_of_disbelief.svg") %>% image_merge( copies = n, output = "suspension_of_disbelief.svg") %>% add_fills( transparency = 1 / n, colors = "blue") %>% image_scale(scale = seq(1,0.1,length.out = n)) %>% image_spin(rpm = seq(0.5,10, length.out = n)) %>% add_restart(fill = bg_light) %>% add_background(color = bg_light) ``` --- class: split-square2, dark .column.p-2.light[ .vmiddle.center[ # Nanoscale # Predictions ] ] .column[ <!-- --> <img src="nanoscale_predictions.svg" type="image/svg+xml"> <!-- --> ] ??? ```r spiro( fixed_radius = 800, cycling_radius = 677, pen_radius = 100, color_groups = 10, color_cycles = 61, windings = 677 * 0.5, transparency = 1, start_angle = 0, points_per_polygon = 300, colors = scico(n = 10, palette = "cork"), draw_fills = F, file = "nanoscale_predictions.svg" ) %>% add_background_gradient(colors = c("black", "black", "gray40")) ``` --- class: split-square1 .column[ <object id="violetblackout" class="spiro" data="violet_blackout.svg" type="image/svg+xml"></object> <!-- --> <button type="button" style="background-color:black" class="playbtn btn btn-dark position-absolute bottom-0 start-0 m-2" onclick='playpause("violetblackout", this);'>Play</button> !-- --> ] .column.p-5.light[ .vmiddle.center[ # Violet # Blackout ] ] ??? ```r my_purple <- scales::muted(scales::alpha("purple",alpha = 0.8)) tibble( colors = c(my_purple, "black"), fixed_radius = c(16, 15), cycling_radius = c(15, 14), file = c("purple.svg", "black.svg")) %>% pmap( spiro, points_per_polygon = 1000, pen_radius = 1.5, draw_fills = FALSE, line_width = 4) %>% image_merge(output = "violet_blackout.svg") %>% image_spin(rpm = c(0.5, -0.5)) %>% add_restart( color = my_purple, fill = "black") %>% add_background() ``` --- class: split-square2 .column.p-2.dark.center[ .vmiddle[ # Some Strata # Still Remember # the Sun ] ] .column[ <!-- --> <img src="some_strata_still_remember_the_sun.svg" type="image/svg+xml"> <!-- --> ] .fixed-bottom.left.pb-4.pl-4.smaller.dark.w-1[ <!-- --> <p>Color palette derived from Emily Kngwarreye’s <a href="https://www.khanacademy.org/humanities/ap-art-history/global-contemporary/a/kngwarreye-earths-creation" class="demo_img" title="" target="_blank"> <em>Emu Woman</em></a> (1988)</p> <!-- --> ] ??? ```r n <- 25 cc <- ochre_palettes$emu_woman_paired[c(6, 11, 2, 7, 9)] %>% rep(5) tibble::tibble( fixed_radius = n + 2, cycling_radius = 1:n, pen_radius = 1:n + 0, transparency = 0.85, rotation = pi / 6, colors = cc, file = paste0("asdf", 1:n, ".svg")) %>% purrr::pmap(spiro, points_per_polygon = 500) %>% image_merge( output = "some_strata_still_remember_the_sun.svg") %>% image_scale(scale = seq(1, 0.1, length.out = n) ^ 0.85) %>% add_background(color = bg_light) ``` --- class: split-square1, dark .column[ <!-- --> <img src="when_time_reverses.svg" type="image/svg+xml"> <!-- --> ] .column.p-2.dark[ .vmiddle.center[ # When Time # Reverses, # You'll Know # What's Coming ] ] ??? ```r spiro( fixed_radius = 919, cycling_radius = 367, pen_radius = 509, windings = 403, color_groups = 17, color_cycles = 6, points_per_polygon = 400, transparency = 0.5, file = "when_time_reverses.svg", colors = scico(17, palette = "tofino") ) %>% add_background_gradient(colors = c(bg_light)) ``` --- class: split-square2, dark .column.p-2.light[ .vmiddle.center[ # Purple # Midnight ] ] .column[ <!-- --> <img src="purple_midnight.svg" type="image/svg+xml"> <!-- --> ] ??? ```r spiro( file = "purple_midnight.svg", fixed_radius = 800, cycling_radius = 751, pen_radius = 40, color_groups = 4, color_cycles = 2, points_per_polygon = 5000, colors = c( "midnightblue", "white", "purple4", "white")) %>% add_lines( colors = "black", line_width = 0.15) %>% add_background_gradient( stops = c(0,0.25,0.63,0.67,0.70,1), colors = c( "black", "purple4", "black", "midnightblue", "black", "gray20")) ``` --- class: split-square1, dark .column.bg-white[ <object id="illusively" class="spiro" data="illusively_elusive_allusion.svg" type="image/svg+xml"></object> <!-- --> <button type="button" class="playbtn btn btn-light position-absolute bottom-0 start-0 m-2" onclick='playpause("illusively", this);'>Play</button> <!-- --> ] .column.p-2.dark[ .vmiddle.center[ # Illusively # Elusive # Allusion ] ] ??? ```r k <- 80 spiro( 4, 3, 3, file = "asdf.svg", color_groups = 4, colors = rgb( c(0, .1418, .2118, .7012), c(.0039, .1608, .6392, 1), c(.3059, 0.9569, .9922, .9647), ) , transparency = 0.5, points_per_polygon = 50 ) %>% image_merge( copies = k, output = "illusively_elusive_allusion.svg") %>% image_scale(scale = seq(1, 0.2, length.out = k)) %>% image_spin(rpm = rep(c(1, -1), k / 2) * seq(1, 10, length.out = k)) %>% add_restart() ``` --- class: split-square2, dark .column.p-2.light[ .vmiddle.center[ # Skyscraper # Sunrise ] ] .column[ <!-- --> <img src="skyscraper_sunrise.svg" type="image/svg+xml"> <!-- --> ] ??? ```r set.seed(365) k <- 12 bg_colors <- scico(31, palette = "vik") %>% rev() %>% scales::muted(., l = 7, c = 7) %>% `[`(sample(1:31, k, replace = T)) bg_stops <- sort(runif(k)) spiro( fixed_radius = 1231, cycling_radius = 529, pen_radius = 1233, color_groups = 67, color_cycles = 59, windings = 101, points_per_polygon = 50, transparency = 1, colors = rev(scico(67, palette = "vik")), file = "skyscraper_sunrise.svg") %>% add_background_gradient(bg_colors, stops = bg_stops) ``` --- class: split-square1, dark .column.p-3.light[ <!-- --> <img src="blurry_metal_eye.svg" type="image/svg+xml"> <!-- --> ] .column.p-2.dark[ .vmiddle.center[ # Blurry # Metal # Eye ] ] ??? ```r spiro( fixed_radius = 167, cycling_radius = 173, pen_radius = 14, windings = 173, color_groups = 52, color_cycles = 72 , points_per_polygon = 50, file = "blurry_eye.svg", colors = scico(n = 52, alpha = 0.9, palette = "roma") ) %>% image_merge(copies = 6, output = "blurry_metal_eye.svg") %>% image_scale(seq(1, 0.95, length.out = 6)) %>% add_background_gradient(colors = c("black", bg_light, bg_light), stops = c(0.66, 0.69, 1)) ``` --- class: split-square2, dark .column.p-2.light[ .vmiddle.center[ # Maelstrom # Sunset ] ] .column[ <!-- --> <img src="maelstrom_sunset.svg" type="image/svg+xml"> <!-- --> ] ??? ```r spiro( fixed_radius = 12 , cycling_radius = 121, pen_radius = 100, file = "maelstrom_sunset.svg", color_groups = 36, color_cycles = 48, points_per_polygon = 50, colors = scico(18,palette = "roma")) %>% add_background(rounding = 0, color = bg_dark) ``` --- class: split-square1, dark .column[ <!-- --> <img src="not_forgotten_bg.svg" type="image/svg+xml"> <!-- --> ] .column.p-2.dark[ .vmiddle.center[ # Can We # Open It, # Grandma? ] ] ??? ```r c(spiro( fixed_radius = 21, cycling_radius = -20, pen_radius = 35, transparency = 0.2, colors = "black", file = "not_forgotten1.svg") %>% add_lines(colors = "#FFFFFFAA", line_width = .25), spiro( fixed_radius = 21, cycling_radius = -20, pen_radius = 35, transparency = .75, colors = "black", rotation = pi / 21, file = "not_forgotten2.svg") %>% add_lines(colors = "#FFFFFFAA", line_width = .5)) %>% image_merge(output = "not_forgotten.svg") %>% add_background_gradient( rounding = 1, radius = 1, colors = c("lightcyan2", rep(c("royalblue4", "lightcyan2"), 13),"royalblue4", rep(bg_light,2)), stops = c(0, 0.05, 0.07, 0.11, 0.1578947, 0.2105263, 0.28, 0.3395, 0.39, 0.441, 0.485, 0.53, 0.571, 0.609, 0.6415, 0.682, 0.711, 0.745, 0.77, 0.80, 0.823, 0.84, 0.86, 0.88, 0.895, 0.905, 0.91, 0.92, 0.93, 1)) ``` --- class: split-square2, dark .column.p-2.light[ .vmiddle.center[ # D'Ya Wanna # Roller Skate? ] ] .column[ <!-- --> <img src="wanna_rollerskate.svg" type="image/svg+xml"> <!-- --> ] ??? ```r k <- 40 tibble( fixed_radius = 5, cycling_radius = 3, pen_radius = seq(12, 3, length.out = k), colors = viridis::viridis(k, option = "D", alpha = 0.15), rotation = seq(5 * pi / k,0, length.out = k)) %>% pmap_chr(spiro, points_per_polygon = 100) %>% image_merge( output = "wanna_rollerskate.svg") %>% image_rotate(degrees = -360 / 20) %>% # image_spin(1:k / k) %>% add_background(color = bg_dark) ``` --- class: split-square1, dark .column.p-2.light[ <object id="counterspintriangles" class="spiro" data="counterspin_triangles.svg" type="image/svg+xml"></object> <!-- --> <button type="button" class="playbtn btn btn-light position-absolute bottom-0 start-0 m-2" onclick='playpause("counterspintriangles", this);'>Play</button> <!-- --> ] .column.p-2.dark[ .vmiddle.center[ # Remembering # .small[.small[the]] Merger .small[.small[of]] # Reemerging # Memories ] ] ??? ```r k <- 80 spiro(3,1,0.5, file = "asdf.svg", color_groups = 3, transparency = 0.5, colors = c("#000000", bg_light, "#8f0a7f"), points_per_polygon = 100) %>% image_merge( copies = k, output = "counterspin_triangles.svg") %>% image_scale(scale = seq(1, 0.1, length.out = k)) %>% image_spin(rpm = 3 * rep(c(1, -1), k / 2) * seq(1, 3, length.out = k)) %>% add_background(rounding = 1, color = bg_dark) %>% pause_onload() ``` --- class: split-square2, dark .column.p-2.light[ .vmiddle.center[ # Checkered # Future ] ] .column[ <!-- --> <img src="checkered_future.svg" type="image/svg+xml"> <!-- --> ] ??? ```r ngroups <- 4 * 6 cc <- c( rainbow(ngroups / 4, s = 0.9, v = 0.3), rev(rainbow(ngroups / 4, s = 0.9, v = 0.3)), rainbow(ngroups / 4, s = 0.3, v = .9), rev(rainbow(ngroups / 4, s = 0.3, v = .9))) cc[seq(2, ngroups, 2)] <- "black" spiro( fixed_radius = 199, cycling_radius = 120, pen_radius = 43, color_groups = ngroups, color_cycles = 1440 %/% ngroups, points_per_polygon = 50, transparency = .3, draw_fills = T, colors = cc, file = "checkered_future.svg") %>% add_background_gradient( colors = c( "black", "black", "gray90", "black", "gray95", "black", "gray50"), stops = c( .00, .18, .20, .3, .50, .60, 1)) ``` --- class: split-square1 .column[ <!-- --> <img src="expensive_plaid.svg" type="image/svg+xml"> <!-- --> ] .column.p-2.light[ .vmiddle.center[ # Expensive # Plaid ] ] ??? ```r spiro( fixed_radius = 1, cycling_radius = sqrt(7), pen_radius = sqrt(7), windings = 450, draw_fills = F, colors = c( scico(11, palette = "tofino", direction = 1), scico(4, palette = "nuuk")), color_groups = 16, points_per_polygon = 100, color_cycles = 450 * 8, line_width = 0.75, lend = 1, file = "expensive_plaid.svg") %>% add_background(color = bg_dark) ``` --- class: split-square2, dark .column.p-2.light[ .vmiddle.center[ # Groovy # Church ] ] .column[ <!-- --> <img src="groovy_church.svg" type="image/svg+xml"> <!-- --> ] ??? ```r cc <- hcl(h = seq(0, 1, length.out = 52) * 220 + 70, c = 50, l = 28) cc[1:14] <- hcl(h = seq(0, 1, length.out = 14) * 250 + 80, c = 60, l = 42) # cc <- scales::muted(cc, l = 40) spiro( fixed_radius = 44, cycling_radius = 13, pen_radius = 13, file = "asdf.svg", colors = cc, color_cycles = 4, draw_fills = F, color_groups = 4 * 13, points_per_polygon = 15, line_width = 0.3 ) %>% spiro::image_merge(., output = "groovy_church.svg", copies = 38) %>% image_rotate(1:36 * 0.21) %>% add_background_gradient( colors = c("gray15", "black", "black", "gray10", "black", "black", "gray15"), stops = c(0, 0.27, 0.271, 0.5, .655, 0.656, 1) ) ``` --- class: split-square1, dark .column.p-2.light[ <!-- --> <img src="hawaiian_snow_salad.svg" type="image/svg+xml"> <!-- --> ] .column.p-2.dark[ .vmiddle.center[ # Hawaiian # Snow Salad ] ] ??? ```r spiro( fixed_radius = 200, cycling_radius = 89, pen_radius = 175, color_groups = 200, color_cycles = 5, points_per_polygon = 200, file = "hawaiian_snow_salad.svg", colors = alpha(lacroix_palette( name = "Coconut", n = 200, type = "continuous"), alpha = 0.8)) ``` --- class: split-square2 .column.p-2.dark[ .vmiddle.center[ # Was It # This Obvious # All Along? ] ] .column.p-2.bg-white[ <!-- --> <img src="was_it_this_obvious_all_along.svg" type="image/svg+xml"> <!-- --> ] ??? ```r tibble::tibble( points_per_polygon = 1000, fixed_radius = 17, cycling_radius = 3:8, colors = c( "dodgerblue4", "white", "dodgerblue3", "white", "dodgerblue2", "white")) %>% pmap_chr(spiro) %>% image_merge( output = "was_it_this_obvious_all_along.svg") %>% add_background_gradient(colors = c("#58A5F2", "#58A5F2", "black", "white"), stops = c(0,.48,.67,0.68)) ``` --- class: split-square1, dark .column.p-2[ <object id="secondeventhorizon" class="spiro" data="second_event_horizon.svg" type="image/svg+xml"></object> <!-- --> <button type="button" class="playbtn btn btn-dark position-absolute bottom-0 start-0 m-2" onclick='playpause("secondeventhorizon", this);'>Play</button> <!-- --> ] .column.dark.pr-4[ .vmiddle.center[ # Second # Event # Horizon ] ] ??? ```r spiro( file = "asdf.svg", fixed_radius = sqrt(7), cycling_radius = sqrt(5), pen_radius = 0.55, color_groups = 1, color_cycles = 1, windings = 49, points_per_polygon = 2000 ) %>% image_merge(copies = 2, output = "second_event_horizon.svg") %>% add_fills(c(rep(c("#282829", "gray95"), 9), rep(c("gray95", "#282829"), 9))) %>% add_lines( line_width = c(0.8, 0.4), colors = c("firebrick2", "royalblue2"), transparency = 0.45 ) %>% image_scale(scale = c(.95, 0.96)) %>% image_rotate(degrees = c(-1,1)) %>% image_spin(rpm = 1 * c(-0.25, 0.125)) %>% add_restart(color = "#bbb", fill = "#282829") %>% add_background_gradient( colors = c( "gray95", tinter::darken("royalblue", amount = .2), "#282829", "#282829", tinter::darken("firebrick", amount = .3), "#282829" ), stops = c(0.61, 0.65, 0.66, 0.665, 0.685, 0.69) ) %>% add_circle(color = NA, fill = "#282829", r = 0.127) ``` --- class: split-square2 .column.p-2.dark[ .vmiddle.center[ # The Past Was # Never Simple ] ] .column[ <!-- --> <img src="the_past_was_never_simple.svg" type="image/svg+xml"> <!-- --> ] ??? ```r spiro( fixed_radius = 800, cycling_radius = 749, pen_radius = 51, color_groups = 4, color_cycles = 2, transparency = 0.25, start_angle = 0, points_per_polygon = 7000, rotation = -pi / 8, windings = 749, file = "the_past_was_never_simple.svg", colors = c( "firebrick4", "purple4", "midnightblue", "black")) %>% add_background_gradient( rounding = 0, stops = c(0, .08, 0.083, .6, 0.7, 1), colors = c( "white", "gray95", "gray40", "white", "white", "white")) ``` --- class: split-square1, dark .column.p-2.light[ <!-- --> <img src="yes_you_may.svg" type="image/svg+xml"> <!-- --> ] .column.p-2.dark[ .vmiddle.center[ # Yes You May ] ] ??? ```r k <- 12 crossing( fixed_radius = k, cycling_radius = seq(1,k), pen_radius = k) %>% rowid_to_column("file") %>% mutate( file = paste0(file,".svg"), colors = c( scico( n = nrow(.) / 2, palette = "devon", alpha = .9), scico( n = nrow(.) / 2, palette = "devon", alpha = .7))) %>% pmap( spiro, draw_fills = T) %>% rev() -> p p[-1:-3] %>% rev %>% map2( seq(1,0.5, length.out = length(.)) ^ 0.4 , image_scale) %>% image_merge( output = "yes_you_may.svg") %>% add_lines(line_width = 0.2) %>% add_background(color = bg_light) ``` --- class: split-square2, dark .column.p-2.light[ .vmiddle.center[ # The Foreglow.small[.small[ of]] # .small[.small[a ]]Simple Insight ] ] .column[ <!-- --> <img src="foreglow.svg" type="image/svg+xml"> <!-- --> ] ??? ```r l <- 27 crossing( fixed_radius = 17, cycling_radius = 1:9, pen_radius = 11 ) %>% mutate(colors = LaCroixColoR::lacroix_palette("Coconut", n = 9) %>% scales::alpha(0.9)) %>% pmap( spiro, draw_fills = T, xlim = c(-l, l), ylim = c(-l, l), color_cycles = 17 * 6, points_per_polygon = 150 ) %>% image_merge(output = "foreglow.svg") %>% add_background_gradient( colors = rep( LaCroixColoR::lacroix_palette("Coconut", 9) %>% colorspace::darken(.1), 3 ), stops = c(0.7 * seq(0, 1, length.out = l - 3), 0.78 , 0.88, 0.98) ) ``` --- class: split-square1 .column[ <!-- --> <img src="bending_is_not_a_compromise.svg" type="image/svg+xml"> <!-- --> ] .column.p-2.light[ .vmiddle.center[ # Bending Is Not # a Compromise ] ] ```r n <- 36 tibble( fixed_radius = 13, cycling_radius = 11, pen_radius = seq(1, n) / 2, file = paste0("sp", 1:n, ".svg"), colors = rep(scico( n = n / 4, alpha = 0.6, palette = "tofino"), times = 4)) %>% pmap(spiro, points_per_polygon = 400, xlim = c(-20, 20), ylim = c(-20, 20)) %>% rev() %>% image_merge( output = "bending_is_not_a_compromise.svg") %>% image_rotate(sqrt(1:n * 50)) %>% add_background(color = bg_dark) ``` --- class: split-square2, dark .column.p-2.light[ .vmiddle.center[ # Value-Added # Fragility ] ] .column[ <!-- --> <img src="value_added_fragility.svg" type="image/svg+xml"> <!-- --> ] ??? ```r spiro( fixed_radius = 121, cycling_radius = 131, pen_radius = 11, file = "value_added_fragility.svg", color_groups = 121, color_cycles = 11, points_per_polygon = 100, colors = scico(11,palette = "cork")) %>% add_background(rounding = 0, color = bg_dark) ``` --- class: split-square1 .column[ <!-- --> <img src="restoration_agreement.svg" type="image/svg+xml"> <!-- --> ] .column.p-2.light[ .vmiddle.center[ # Restoration # Agreement ] ] ??? ```r c( spiro( file = "restoration_agreement1.svg", fixed_radius = 800, cycling_radius = 677, pen_radius = 101, color_groups = 8, color_cycles = 677 * 3 , windings = 677 * 0.5, transparency = 1, rotation = pi / 13, points_per_polygon = 3, colors = paste0("gray", c(92,93,94,95,12,13,14,15)) ), spiro( file = "restoration_agreement2.svg", fixed_radius = 800, cycling_radius = 677, pen_radius = 101, color_groups = 8, color_cycles = 677 * 3 , windings = 677 * 0.5, transparency = 1, points_per_polygon = 3, colors = scico(n = 8, palette = "cork") )) %>% image_merge(output = "restoration_agreement.svg") %>% add_background(color = bg_dark) ``` --- class: split-square2 .column.p-2.dark[ .vmiddle.center[ # Convoluted # Candor ] ] .column.pt-4.light[ <!-- --> <img src="convoluted_candor.svg" type="image/svg+xml"> <!-- --> ] ??? ```r lim <- 5 k <- 36 tibble( pen_radius = seq(3, 1, length.out = k), colors = scico(n = k, palette = "cork") %>% scales::muted(., l = 50, c = 40), transparency = rep_len(c(rep(0.4, 3), 1), k), line_width = rep_len(c(rep(0.5, 3), 1), k) ) %>% pmap( spiro, fixed_radius = 5, points_per_polygon = 200, cycling_radius = 3, rotation = -pi / 2, xlim = c(-lim, lim), ylim = c(-lim, lim), draw_fill = F ) %>% image_merge("convoluted_candor.svg", delete_input = T) %>% add_background(color = bg_light) ``` --- class: split-square1 .column[ <object id="reconciliation491" class="spiro" data="reconciliation491.svg" type="image/svg+xml"></object> <!-- --> <button type="button" class="playbtn btn btn-dark position-absolute bottom-0 start-0 m-2" style="background-color: #000526;" onclick='playpause("reconciliation491", this);'Play</button> <!-- --> ] .column.p-2.light[ .vmiddle.center[ # Reconciliation # Reset 490 ] ] ??? ```r n <- 36 spiro(fixed_radius = 3, cycling_radius = 1, file = "reconciliation491.svg", colors = "#5262CA10", points_per_polygon = 300) %>% image_scale(scale = 0.7) %>% image_merge(copies = n, output = "reconciliation491.svg") %>% add_lines(colors = "#5262CAAA", line_width = 0.5) %>% image_spin(rpm = seq(1,n) / 12, rotation_point = c(0.6135,0.6135)) %>% image_shift(x = -81.5, y = 81.5) %>% add_restart(color = colorspace::lighten("#5262CA", .5), fill = colorspace::darken("#5262CA", .85)) %>% add_background_gradient(colors = map(c(0.98, 0.98, 0.95, 0.8), colorspace::darken, col = "#5262CA")) ``` --- class: split-square2 .fixed-bottom.left.p-4.w-1.dark[Color palette derived from Vermeer’s <a href="https://www.wikiwand.com/en/The_Milkmaid_(Vermeer)" class="demo_img" title="" target="_blank"> <em>Milkmaid</em></a>] .column.p-2.dark[ .vmiddle.center[ # Epistemological # Modesty ] ] .column[ <!-- --> <img src="epistemological_modesty.svg" type="image/svg+xml"> <!-- --> ] ??? ```r spiro( fixed_radius = 8, cycling_radius = 7, pen_radius = 1.069, color_groups = 8, color_cycles = 2, start_angle = 0, rotation = -pi / 8 , transparency = .5, points_per_polygon = 500, windings = 7 * 6, file = "epistemological_modesty.svg", colors = dutchmasters_pal( palette = "milkmaid", reverse = T)(8)) %>% add_background(color = bg_light) ``` --- class: split-square1 .column.p-2.dark[ <object id="webwindows" class="spiro" data="web_windows.svg" type="image/svg+xml"></object> <!-- --> <button type="button" class="playbtn btn btn-dark position-absolute bottom-0 start-0 m-2" onclick='playpause("webwindows", this);'>Play</button> <!-- --> ] .column.p-2.light[ .vmiddle.center[ # Web Windows ] ] ??? ```r n <- 10 spiro( fixed_radius = 6, cycling_radius = 11, pen_radius = -6 , colors = "white", file = "web_windows.svg", draw_fills = F, transparency = 0.9, line_width = 0.75, rotation = pi / 6, points_per_polygon = 1000) %>% image_merge( output = "web_windows.svg", copies = n) %>% add_fills(viridis_pal( end = 0.97, alpha = 0.9)(n)) %>% image_scale(scale = 1.07 * sqrt(0.8 ^ (seq(0, n - 1)))) %>% image_spin(rpm = 1:n * 0.15) %>% add_restart( fill = "#00000000", color = bg_light ) %>% add_background(color = bg_dark) ``` --- class: split-square2 .column.p-2.dark[ .vmiddle.center[ # Happy # Birthday, # Whenever # It Was ] ] .column.bg-white[ <!-- --> <img src="christmas_wreath.svg" type="image/svg+xml"> <!-- --> ] ??? ```r spiro( fixed_radius = sqrt(13), cycling_radius = sqrt(11), pen_radius = sqrt(0.5), windings = 401, color_groups = 5, points_per_polygon = 40, color_cycles = 906, colors = c("darkred","gray90","darkgreen","gray20","forestgreen"), transparency = 0.55, file = "christmas_wreath.svg") %>% add_background_gradient( rounding = 1, radius = 1, colors = c("white", rep(c("gray30","black"), 5),"white"), stops = c(0.4,seq(.45,.9,length.out = 10),0.93)) ``` --- class: split-square1, dark .column[ <object id="collidingscopes" class="spiro" data="colliding_scopes.svg" type="image/svg+xml"></object> <!-- --> <button type="button" class="playbtn btn btn-light position-absolute bottom-0 start-0 m-2" onclick='playpause("collidingscopes", this);'>Play</button> <!-- --> ] .column.p-2.dark[ .vmiddle.center[ # Colliding # Scopes ] ] ??? ```r tibble(colors = c("#004D47", "#128277")) %>% pmap( spiro, fixed_radius = pi, cycling_radius = sqrt(8), pen_radius = 0.5 * sqrt(8) / pi, windings = 81, start_angle = 0, points_per_polygon = 5001, transparency = 1 ) %>% tibble(input = ., duration = c(7, 11)) %>% pmap(image_animate, attribute = "opacity", values = c(0.2, 0.8, 0.2)) %>% image_merge(output = "colliding_scopes.svg", delete_input = T) %>% image_spin(rpm = c(0.1, -0.1)) %>% add_restart(fill = "#00000000") %>% add_background(color = bg_light) ``` --- class: split-square2, dark .column.p-2.light[ .vmiddle.center[ # .small[.small[The]] Increasing # Returns # .small[.small[of]] Having # Traveled ] ] .column[ <!-- --> <img src="the_increasing_returns_of_having_traveled.svg" type="image/svg+xml"> <!-- --> ] ??? ```r n <- 60 spiro(3, 1, 1, color_groups = 6, colors = LaCroixColoR::lacroix_palettes$Tangerine[1,, drop = T] %>% scales::alpha(0.7), points_per_polygon = 50, file = "delta.svg") %>% image_merge(copies = n, output = "the_increasing_returns_of_having_traveled.svg") %>% image_scale(seq(0.0001,1, length.out = n) ^ (1/1.25)) %>% image_rotate(seq(0,360 * 5.65, length.out = n)) %>% spiro::add_lines(input = ., colors = "gray10") %>% add_background_gradient(colors = c("gray30", "gray5","gray30","gray5")) ``` --- class: split-square1 .column[ <!-- --> <img src="old_errors.svg" type="image/svg+xml"> <!-- --> ] .column.light[ .vmiddle.center[ # Mining # Old Errors # .small[.small[for]] New Truths ] ] ??? ```r my_palette <- scales::colour_ramp(c( rgb(.74, .19, .22, .9), rgb(.86, .76, .22, .2), rgb(.91, .92, .92, .9), rgb(.11, .22, .22, .95), rgb(.22, .22, .22, .1), rgb(.24, .22, .71, .9) )) spiro( fixed_radius = 226, pen_radius = 235, cycling_radius = 15, points_per_polygon = 10, file = "old_errors.svg", color_groups = 40, color_cycles = 61, colors = my_palette(seq(0, 1, length.out = 40)) ) %>% add_background_gradient(colors = c("gray20", "white", "gray90", "gray30","gray5"), stops = c(0, .1, 0.6, .65, 1)) ``` --- class: split-square2, dark .column.p-2.light[ .vmiddle.center[ # Ñañaras ] ] .column.p-3.bg-black[ <!-- --> <img src="nanaras.svg" type="image/svg+xml"> <!-- --> ] ??? ```r my_colors <- c("#969594FF","#00000000","#00000000", "#F9F8F7FF","#00000000","#00000000", "#585756FF","#00000000","#00000000", "#D0CFCDFF","#00000000","#00000000","#171615FF") tibble(fixed_radius = 541, cycling_radius = 601, pen_radius = 191, windings = 200, color_groups = 20, points_per_polygon = 40 * 19:23) %>% pmap(spiro, colors = my_colors) %>% image_merge(output = "nanaras.svg") %>% image_scale(c(1.07, .56, .29, 0.15, 0.075)) %>% image_rotate(degrees = seq(0, 80, 20)) %>% add_background("black") ``` --- class: split-square1 .column[ <!-- --> <img src="portal_to_the_80s.svg" type="image/svg+xml"> <!-- --> ] .column.p-2.light[ .vmiddle.center[ # Portal to # the 80s ] ] ??? ```r string_bezier( file = "portal_to_the_80s.svg", x = c(-1, 1, 0,-1, 1,-1, 0, 1,-1, 1), y = c(0, 0, 1, 0, 0, 0,-1, 0, 0, 0), color = c("#006B6B", "#FFFFFF", "black", "black", "black"), n = 200, lwd = 5, ljoin = 1) %>% add_background(color = "#108998") ``` --- class: split-square2 .fixed-bottom.left.p-4.gray[Inscribed polygons adapted from [Mathologer's T-Shirt](https://www.youtube.com/watch?v=qhbuKbxJsk8)] .column.p-2.dark[ .vmiddle.center[ # Supervillains' # Superior Sense # of Fashion ] ] .column[ <!-- --> <img src="superhero_bg.svg" type="image/svg+xml"> <!-- --> ] ??? ```r library(ggforce) k <- 30 r <- rep(0, k) r[1:2] <- 1 for (i in 3:k) { r[i] <- r[i - 1] * 1 / cos(pi / i) } xy <- function(n, r, dtheta) { tibble(x = r * cos((0:n) * dtheta + pi / 2), y = r * sin((0:n) * dtheta + pi / 2)) } svg("superhero.svg", width = 8, height = 8) par(pty = "s") k <- 30 r <- rep(0, k) r[1:2] <- 1 for (i in 3:k) { r[i] <- r[i - 1] * 1 / cos(pi / i) } plot( x = c(1, 2), y = c(1, 2), xlim = c(-8.7, 8.7), ylim = c(-8.7, 8.7), type = "n", axes = F, xlab = NA, ylab = NA, ann = F ) bg <- "slateblue" plotrix::draw.circle( 0, 0, radius = 8.7, col = bg, border = NA, nv = 200 ) tibble(n = 2:k, r = r[-1]) %>% mutate(dtheta = 2 * pi / n, d_xy = pmap(list(n, r, dtheta), xy)) %>% select(-dtheta) %>% arrange(-n) %>% purrr::pwalk(., function(n, r, d_xy) { plotrix::draw.circle( 0, 0, radius = r, col = paste0("gray", n * 3), border = bg, lwd = 2 * (2 / n) ^ (1 / 5), nv = 200 ) if (n > 2) polygon( d_xy, col = paste0("gray", 2), border = bg, lwd = 2 * (2 / n) ^ (1 / 5) ) }) dev.off() ``` --- class: split-square1 .column[ <object id="sunrisesaresunsetsrecycled" class="spiro" data="sunrises_are_sunsets_recycled.svg" type="image/svg+xml"></object> <!-- --> <button type="button" class="playbtn btn btn-dark position-absolute bottom-0 start-0 m-2" onclick='playpause("sunrisesaresunsetsrecycled", this);'>Play</button> <!-- --> ] .column.p-2.light[ .vmiddle.center[ # Sunrises # Are Sunsets # Recycled ] ] ??? ```r k <- 9 n_dots <- 120 cc <- lacroix_palette( name = "Coconut", n = n_dots) %>% alpha(0.7) s <- spiro( fixed_radius = 9, cycling_radius = 4, pen_radius = 6, draw_fills = F, colors = alpha(rep("black", k), 0.01), color_groups = k, points_per_polygon = 50, file = "asdf.svg") for (i in seq(0, n_dots - 1)) { s <- add_pathdot( s, delay = i / (n_dots / 12), colors = cc[i + 1], radius = 2, duration = 32) } image_merge( s, copies = 5, output = "sunrises_are_sunsets_recycled.svg") %>% image_spin(rpm = 1:5 / 5) %>% add_background(color = bg_dark) %>% pause_onload() ``` --- class: split-square1 .column[ <object id="oneminutereunion" class="spiro" data="one_minute_reunion.svg" type="image/svg+xml"></object> <!-- --> <button type="button" class="playbtn btn btn-light position-absolute bottom-0 start-0 m-2" onclick='playpause("oneminutereunion", this);'>Play</button> <!-- --> ] .column.p-2.dark[ .vmiddle.center[ # One-Minute # Reunion ] ] ??? ```r spiro( fixed_radius = 5, cycling_radius = 1, pen_radius = 11 / 3, color_groups = 5, colors = rep("gray95", 5), start_angle = pi, draw_fills = FALSE, rotation = pi / 10, file = "one_minute_reunion.svg", points_per_polygon = 250) %>% add_pathdot( colors = "firebrick", duration = c(2,3,4,5,6,10,12,15,20,30,60)) %>% # add_restart(color = "#333333",fill = scales::alpha("gray95")) %>% add_background(bg_light) ``` --- class: split-square1 .column[ <!-- --> <img src="graceful_exit.svg" type="image/svg+xml"> <!-- --> ] .column.p-2.light[ .vmiddle.center[ # Graceful Exit ] ] ??? ```r tibble( cycling_radius = c(-5,5), color_cycles = c(64,32), start_angle = c(0 * pi / 64 * 3, 20 * pi / 8), line_width = c(4 * 0.68, 4), file = c("s3.svg","s4.svg")) %>% pmap( spiro, fixed_radius = 8, pen_radius = 5, color_groups = 64, points_per_polygon = 2, draw_fills = F, ljoin = 2, end_at_beginning = T, colors = c( viridis_pal()(32), rev(viridis_pal()(32)))) %>% tibble(input = ., scale = c(1,0.68)) %>% pmap(image_scale) %>% image_merge( output = "graceful_exit.svg", delete_input = T) %>% add_background_gradient(colors = c("gray5", "gray25")) ```