Last active
November 26, 2025 11:17
-
-
Save paithiov909/b1c12083b64ccee1a84964588bc8cfeb to your computer and use it in GitHub Desktop.
Rtistry🎨
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "My Title" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| ```{r} | |
| print("Hello, World!") | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "01 円だけを使って描いてみましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| #| label: save-gif | |
| library(skiagd) # https://github.com/paithiov909/skiagd | |
| library(gifski) | |
| n_circles <- 50 | |
| size <- c(360, 360) | |
| trans <- matrix(c(40, 0, size[1] / 2, 0, 40, size[2] / 2, 0, 0, 1), ncol = 3) | |
| radius <- runif(n_circles, min = .5, max = 2) |> sort() | |
| circle <- function(amp, freq, phase) { | |
| amp * 1i^(freq * seq(0, 600, length.out = n_circles) + phase) | |
| } | |
| save_gif(lapply(seq(0, 4 * pi, length.out = 720 + 1)[-1], function(a) { | |
| k <- 7 | |
| l <- sin(pi * (2 * a - .5)) + 1 | |
| z <- circle(pi / 6, -pi, 0) + | |
| circle(l, ceiling(a), -9 * cos(a) + 1) + | |
| circle(l / 2 - 1, ceiling((-a + (k / 2)) %% k) - k, -7 * cos(a) + 1) | |
| z2 <- c(z[-1], z[1]) | |
| hue <- (a + (Re(z / pi))) %% 1 | |
| colors <- grDevices::hsv(hue, 0.66, 0.75, alpha = 1) | |
| canvas("#04010F") |> | |
| add_circle( | |
| matrix(c(Re(z), Im(z), rep_len(1, length(z))), ncol = 3) %*% trans, | |
| radius = 12 * radius, | |
| color = grDevices::col2rgb(colors, alpha = TRUE), | |
| props = paint( | |
| style = Style$Fill, | |
| blend_mode = BlendMode$Plus, | |
| width = 0.5, | |
| ) | |
| ) |> | |
| draw_img() | |
| }), delay = 1 / 30, width = size[1], height = size[2], progress = TRUE) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| The code was heavily inspired by [Mystery curves – George M Savva - Mathematical Art and Creative Coding](https://georgemsavva.github.io/creativecoding/posts/mystery/). | |
| It is free to use, but if you reuse this in your work, you may want to refer to their article. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "02 自分なりのケーキを描いてみましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| たぶん、COLRv1の"Noto Color Emoji"が必要。 | |
| ```{r} | |
| #| label: create-paths | |
| font <- | |
| systemfonts::system_fonts() |> | |
| dplyr::filter(family == "Noto Color Emoji") |> | |
| dplyr::pull(path) | |
| paths <- | |
| purrr::imap(c("🎂"), \(x, i) { | |
| dplyr::mutate( | |
| string2path::string2path(x, font = font[1]), | |
| glyph_id = i | |
| ) | |
| }) |> | |
| dplyr::bind_rows() |> | |
| dplyr::mutate( | |
| emoji = forcats::fct_lump_prop(factor(color), 0.1), | |
| pos = dplyr::tibble( | |
| x = ambient::normalize(x, from = c(0, 1), to = c(-1, 1)), | |
| y = ambient::normalize(y, from = c(0, 1), to = c(-1, 1)), | |
| z = 1 | |
| ) |> as.matrix(), | |
| .keep = "unused" | |
| ) | |
| ``` | |
| ```{r} | |
| #| label: save-gif | |
| library(skiagd) | |
| library(gifski) | |
| size <- c(320, 320) | |
| emoji_pal <- | |
| sample( | |
| c("🍓", "🍊", "🍏", "🍇", "🍌", "🍒"), | |
| size = nlevels(paths$emoji) | |
| ) | |
| save_gif(lapply(seq(-pi, pi, length.out = 120 + 1)[-1], \(j) { | |
| t <- tweenr::tween_at(0, 1, cos(j) + 1, ease = "cubic-in") | |
| dat <- paths |> | |
| dplyr::group_by(glyph_id, path_id) |> | |
| dplyr::slice_head(n = max(1, ceiling(t * nrow(paths)))) |> | |
| dplyr::ungroup() | |
| trans <- | |
| matrix( | |
| c( | |
| 130, 0, size[1] / 2 - 24, | |
| 0, -100, size[2] / 2 - 36, | |
| 0, 0, 1 | |
| ), | |
| ncol = 3 | |
| ) | |
| canvas("white") |> | |
| add_text( | |
| emoji_pal[dat$emoji], | |
| rsx_trans = dat |> | |
| dplyr::mutate(pos = pos %*% trans) |> | |
| dplyr::reframe( | |
| sc = 0.5 + t, | |
| rot = sin(t), | |
| x = pos[, 1], | |
| y = pos[, 2], | |
| ax = 0, | |
| ay = 0 | |
| ) |> | |
| as.matrix(), | |
| props = paint( | |
| family = "Noto Color Emoji", | |
| fontsize = 6, | |
| color = col2rgba(grDevices::hsv(1, 1, 1, alpha = min(max(t, 0.05), 1))) | |
| ) | |
| ) |> | |
| draw_img() | |
| }), delay = 1 / 20, width = size[1], height = size[2], gif_file = "minacode-02.gif", progress = TRUE) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "03 いつもと違う場所で描いてみましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| ```{r} | |
| library(dplyr) | |
| library(ggplot2) | |
| library(gifski) | |
| ## [地下鉄のシンボルカラー メトロカラー - Metro Colors](https://www.colordic.org/m) | |
| pal <- c( | |
| "東京メトロ銀座線" = "#f39700", | |
| "東京メトロ丸ノ内線" = "#e60012", | |
| "東京メトロ日比谷線" = "#9caeb7", | |
| "東京メトロ東西線" = "#00a7db", | |
| "東京メトロ千代田線" = "#009944", | |
| "東京メトロ有楽町線" = "#d7c447", | |
| "東京メトロ半蔵門線" = "#9b7cb6", | |
| "東京メトロ南北線" = "#00ada9", | |
| "東京メトロ副都心線" = "#bb641d" | |
| ) | |
| metro <- | |
| jprailway::polylines |> | |
| dplyr::filter( | |
| stringr::str_detect(name, "東京メトロ") | |
| ) |> | |
| dplyr::summarize( | |
| geometry = sf::st_union(geometry), | |
| .by = c(id, name) | |
| ) |> | |
| dplyr::mutate(col = pal[name]) | |
| anim <- metro |> | |
| tidyr::nest(.by = id) |> | |
| dplyr::mutate(data2 = dplyr::lead(data, default = data[1])) |> | |
| dplyr::reframe( | |
| id = id, | |
| anim = purrr::map2(data, data2, \(cur, nxt) { | |
| transformr::tween_sf(cur, nxt, "cubic-in-out", 40) |> | |
| tweenr::keep_state(10) | |
| }) | |
| ) |> | |
| tidyr::unnest(anim) |> | |
| dplyr::mutate(frame = dplyr::consecutive_id(id, .frame)) | |
| save_gif(lapply(split(anim, anim$frame), \(d) { | |
| p <- ggplot(d) + | |
| geom_sf(aes(colour = col, geometry = geometry), linewidth = 3) + | |
| coord_sf(datum = NA, xlim = c(139.604, 139.965), ylim = c(35.623, 35.8265)) + | |
| scale_colour_identity() + | |
| labs( | |
| title = " 言えるかな? 東京メトロクイズ", | |
| caption = paste( | |
| "出典:国土数値情報(鉄道データ)(国土交通省) ", | |
| "https://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-N02-v2_3.html ", | |
| sep = "\n" | |
| ) | |
| ) + | |
| theme_void() + | |
| theme( | |
| ## パネルに色を付けると画像の端が白くなるので、あきらめて白にする | |
| plot.background = element_rect( | |
| fill = "white", colour = "white" | |
| ), | |
| legend.position = "none", | |
| plot.margin = unit(c(0, 0, 0, 0), "cm") | |
| ) | |
| plot(p) | |
| }), delay = 1 / 20, width = 640, height = 360) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "04 ランダムを使って、いろんな表情のコードを描きましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| #| label: constants | |
| library(skiagd) | |
| library(gifski) | |
| folium <- \(n, a) { | |
| theta <- seq(-.2, pi / 3 * 2, length.out = n) | |
| sn <- sin(theta) | |
| cs <- cos(theta) | |
| r <- 3 * a * sn * cs / (sn^3 + cs^3) | |
| ret <- | |
| dplyr::tibble( | |
| x = r * cs, | |
| y = r * sn, | |
| z = 0 | |
| ) | |
| as.matrix(ret) | |
| } | |
| gen_seed <- \(n) { | |
| dplyr::tibble( | |
| grp = ifelse(rbinom(n, 1, 0.5) == 1, -1, 1), | |
| pos = dplyr::tibble( | |
| x = rasengan::normalize(rnorm(n, sd = .2), to = c(0, 1)), | |
| y = rasengan::normalize(rnorm(n, sd = .2), to = c(0, 1)), | |
| z = 1 | |
| ) |> as.matrix() | |
| ) | |
| } | |
| update_center <- \() { | |
| size / 2 + c(runif(1, min = -8, max = 8), runif(1, min = -8, max = 8)) | |
| } | |
| size <- c(320L, 320L) | |
| n <- 660 | |
| n_frames <- 30 | |
| pal <- c( | |
| "#5dffd7", | |
| "#ff5d80", | |
| "#ffdd5d", | |
| "#ddff5d", | |
| "#5dddff", | |
| "#ff5ddd" | |
| ) | |
| ``` | |
| ```{r} | |
| #| label: render | |
| ## Setup | |
| seed <<- gen_seed(n) | |
| rd <<- ceiling(runif(1, min = n_frames, max = n_frames * 3)) | |
| center <<- update_center() | |
| gifski(purrr::map_chr(seq_len(n_frames * 5), \(f) { | |
| i <- (f %% n_frames) | |
| if (i == 0) { | |
| seed <<- gen_seed(n) | |
| rd <<- ceiling(runif(1, min = n_frames, max = n_frames * 3)) | |
| center <<- update_center() | |
| } | |
| j <- tweenr::tween_at(0, 1, (i + 1) / n_frames, ease = "circular-in-out") | |
| cj <- min(.98, max(j, .125)) | |
| alpha <- tweenr::tween_at(0, 1, sin(pi * (i + 1) / n_frames), ease = "circular-in-out") | |
| noise <- rasengan::noise_3d()(j, f, 1:2, seed = rd) |> | |
| rasengan::normalize(from = c(-1, 1), to = c(-3, 3)) | |
| sc <- 2 * rd * cj | |
| pos <- seed |> | |
| dplyr::group_by(grp) |> | |
| dplyr::slice_head(prop = -cj) |> | |
| dplyr::group_modify(\(d, g) { | |
| g <- sign(g$grp) | |
| pos <- d$pos | |
| dplyr::tibble( | |
| pos = (pos + folium(nrow(pos), j)) %*% matrix( | |
| c( | |
| g * sc * sin(j), cos(j), noise[1] * g + center[1], | |
| cos(j), -sc * sin(j), noise[2] * g + center[2], | |
| 0, 0, 1 | |
| ), | |
| ncol = 3 | |
| ) | |
| ) | |
| }) |> | |
| dplyr::pull(pos) | |
| file_path <- paste0(tempdir(), stringr::str_pad(f, 4, "left", "0"), ".png") | |
| canvas("#1a0022", canvas_size = size) |> | |
| add_circle( | |
| pos, | |
| rep_len(16 * (1 - sin((i + 1) / n_frames)), nrow(pos)), | |
| color = sample(pal, nrow(pos), replace = TRUE) |> | |
| grDevices::col2rgb(alpha = FALSE) |> | |
| rbind(rep_len(ceiling(255 * alpha), nrow(pos))), | |
| props = paint( | |
| blend_mode = BlendMode$Plus, | |
| style = Style$Fill, | |
| canvas_size = size, | |
| ) | |
| ) |> | |
| as_png(props = paint(canvas_size = size)) |> | |
| aznyan::morphology(ksize = c(2.4, 2.4, 1.0), alphasync = FALSE) |> | |
| writeBin(con = file_path) | |
| file_path | |
| }), delay = 1 / 15, width = size[1], height = size[2], progress = TRUE) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "06 架空の生き物をつくってみましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| #| label: constants | |
| library(skiagd) | |
| library(gifski) | |
| size <- as.integer(c(640, 480)) | |
| size <- size / 2 ## for development. canvasが大きいと描画が遅くなるので | |
| fps <- 30 | |
| duration_in_frames <- 12 * fps | |
| len <- 36 ## length of the curve (fish body) to be drawn. | |
| ## Updates curve where the ebi moves | |
| update_curve <- \(curr, nxt) { | |
| sg <- if (rbinom(1, 1, 0.5) == 1) -1 else 1 | |
| ## `euler_curve()`は2点間が近すぎたりするとき、求解に失敗することがある | |
| crv <- | |
| rasengan::euler_curve( | |
| start = c(0, 0, pi), | |
| end = c(nxt, sg * pi / dist(matrix(c(0, 0, nxt), ncol = 2, byrow = TRUE))[1]), | |
| max_n = 300, | |
| biarch = TRUE | |
| ) | |
| x <- seq(curr[1], nxt[1], length.out = nrow(crv)) | |
| y <- seq(curr[2], nxt[2], length.out = nrow(crv)) | |
| dplyr::tibble( | |
| x = x + crv[["x"]], | |
| y = y + crv[["y"]] | |
| ) | |
| } | |
| ## Converts curve into an rsxform table | |
| curve2rsx <- \(d, t) { | |
| if (nrow(d) < len) { | |
| rlang::abort(glue::glue("`d` is too short: {nrow(d)} rows.")) | |
| } | |
| offset <- len + ceiling((nrow(d) - len) * t) ## [len, nrow(d)] | |
| curr_state <- d |> | |
| dplyr::slice_head(n = offset) |> | |
| dplyr::slice_tail(n = len) | |
| ## 外れ値を引くと点が飛んでしまうのでcapする | |
| ns <- rasengan::cap(rnorm(nrow(curr_state), sd = 2), -12, 12) | |
| dplyr::tibble( | |
| sc = seq(2, 6, length.out = nrow(curr_state)), | |
| rot = 0, | |
| x = (curr_state[["x"]] + ns), | |
| y = (curr_state[["y"]] + ns), | |
| ax = 0, | |
| ay = 0 | |
| ) | |
| } | |
| ## Background | |
| bg <- | |
| canvas("#85ffde") |> | |
| add_rect( | |
| matrix(c(0, 0, size), ncol = 4), | |
| props = paint( | |
| color = "snow", | |
| blend_mode = BlendMode$Xor, | |
| shader = Shader$turbulence( | |
| freq = c(.06, .06), | |
| octaves = 2, | |
| seed = sample.int(1e3, 1), | |
| tile_size = c(256, 256) | |
| ) | |
| ) | |
| ) | |
| ``` | |
| ```{r} | |
| #| label: test | |
| bg |> | |
| add_circle( | |
| matrix(size / 2, ncol = 2), | |
| 14, | |
| props = paint( | |
| canvas_size = size, | |
| style = Style$Fill, | |
| width = 1, | |
| blend_mode = BlendMode$SoftLight, color = "#cccc00", | |
| ) | |
| ) |> | |
| draw_img(props = paint(canvas_size = size)) | |
| ``` | |
| ```{r} | |
| #| label: render | |
| ## Setup | |
| last_pos <- size / 2 | |
| nxt_pos <- | |
| runif(2, min = min(size) / 12, max = min(size) / 12 * 5) |> | |
| ceiling() | |
| dat <- | |
| update_curve(last_pos, c(1, 1)) |> | |
| dplyr::mutate( | |
| x = rev(x), | |
| y = rev(y) | |
| ) |> | |
| dplyr::slice_tail(n = len) |> | |
| dplyr::bind_rows(update_curve(last_pos, nxt_pos)) | |
| ## Redner loop | |
| save_gif(lapply(seq_len(duration_in_frames), \(frame) { | |
| i <- frame %% fps | |
| ## Add ripple onto the canvas | |
| j <- tweenr::tween_at(0, 1, i / fps, "quadratic-in-out") | |
| ripple <- abs(2 * (last_pos - nxt_pos)) | |
| cnv <- bg |> | |
| add_circle( | |
| dplyr::tibble( | |
| x = rep_len(ripple[1], 5), | |
| y = rep_len(ripple[2], 5) | |
| ) |> as.matrix(), | |
| color = grDevices::hsv(.5, 1, .545, alpha = rep_len(1, 5) * (1 - j)) |> | |
| grDevices::col2rgb(alpha = TRUE), | |
| matrix(sin(seq(1, pi, length.out = 5)) * 300 * j), | |
| props = paint( | |
| width = 3, | |
| color = "navy", | |
| style = Style$Stroke, | |
| blend_mode = BlendMode$Screen | |
| ) | |
| ) | |
| if (i == 0) { | |
| ## If it's the first frame of every step, update the curve. | |
| last_pos <- | |
| dplyr::slice_tail(dat, n = 1) |> | |
| dplyr::select(x, y) |> | |
| as.numeric() | |
| nxt_pos <- | |
| runif(2, min = min(size) / 12, max = min(size) / 12 * 5) |> | |
| ceiling() | |
| dat <<- | |
| dplyr::slice_tail(dat, n = len) |> | |
| dplyr::bind_rows(update_curve(last_pos, nxt_pos)) | |
| ## Draw the frame | |
| rsx <- curve2rsx(dat, 0) | |
| cnv |> | |
| add_rect( | |
| matrix(rep(c(0, 0, 4, 4), nrow(rsx)), ncol = 4, byrow = TRUE), | |
| as.matrix(rsx), | |
| color = grDevices::hsv(seq(0, .167, length.out = nrow(rsx)), 1, .8, alpha = .66) |> | |
| grDevices::col2rgb(alpha = TRUE), | |
| props = paint(width = 1.5, style = Style$Stroke, blend_mode = BlendMode$SoftLight) | |
| ) |> | |
| draw_img() | |
| } else { | |
| ## Or, just draw the frame. | |
| t <- tweenr::tween_at(0, 1, i / fps, ease = "cubic-in-out") | |
| rsx <- curve2rsx(dat, t) | |
| cnv |> | |
| add_rect( | |
| matrix(rep(c(0, 0, 4, 4), nrow(rsx)), ncol = 4, byrow = TRUE), | |
| as.matrix(rsx), | |
| color = grDevices::hsv(seq(0, .167, length.out = nrow(rsx)), 1, .8, alpha = .66) |> | |
| grDevices::col2rgb(alpha = TRUE), | |
| props = paint(width = 1.5, style = Style$Stroke, blend_mode = BlendMode$SoftLight) | |
| ) |> | |
| draw_img() | |
| } | |
| invisible(NULL) | |
| }), delay = 1 / 15, width = size[1], height = size[2], progress = TRUE) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "07 たくさんの図形を組み合わせて1つの大きなオブジェクトを作ってみましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| #| label: setup | |
| library(skiagd) | |
| library(affiner) | |
| library(gifski) | |
| ## 螺旋をXY方向について散らした図形 | |
| create_spiral <- \(n = 180, sc = .4, amp = 10) { | |
| theta <- seq(-12 * pi, 9 * pi, length.out = n) | |
| rd <- sc * (theta + amp * rasengan::cap(rnorm(n), -1, 1)) | |
| dplyr::tibble( | |
| theta = theta, | |
| pos = dplyr::tibble( | |
| x = cos(theta) * rd, | |
| y = sin(theta) * rd, | |
| z = seq(-amp, amp, length.out = n), | |
| w = 1 | |
| ) |> as.matrix() | |
| ) | |
| } | |
| ``` | |
| ```{r} | |
| #| label: test | |
| dat <- create_spiral(3 * 1e3) | |
| ## XY平面について見るとき、右上に引っ張ったようなようなせん断 | |
| trans <- | |
| create_mapping( | |
| matrix( | |
| c( | |
| -2, -2, | |
| -2, 2, | |
| 2, 2, | |
| 2, -2 | |
| ), | |
| byrow = TRUE, ncol = 2 | |
| ), | |
| matrix( | |
| c( | |
| -2, -2, | |
| 0, 2, | |
| 3, 3, | |
| 2, -2 | |
| ), | |
| byrow = TRUE, ncol = 2 | |
| ) | |
| ) |> | |
| rbind(c(0, 0, 0)) |> | |
| cbind(c(0, 0, 0, 1)) |> | |
| affiner::as_transform3d() | |
| with( | |
| dplyr::mutate(dat, pos = pos %*% trans), | |
| plot(pos[, 1], pos[, 2]) | |
| ) | |
| ``` | |
| ```{r} | |
| #| label: sprites | |
| size <- as.integer(c(120, 120)) | |
| pngs <- purrr::map(1:16, \(j) { | |
| dat <- create_spiral() |> | |
| dplyr::slice_sample(prop = 1) | |
| ## いい感じに変形する | |
| d <- dat |> | |
| dplyr::mutate( | |
| pos = pos %*% trans %*% | |
| reflect3d("yz-plane") %*% | |
| scale3d(3) %*% | |
| translate3d(size[1] / 2, size[2] / 2, 0) | |
| ) | |
| img <- | |
| canvas("transparent") |> | |
| add_vertices( | |
| d$pos[, 1:2], | |
| color = seq(0, 1, length.out = nrow(d[[2]])) |> | |
| grDevices::hsv(.979, .949, .3) |> | |
| grDevices::col2rgb(alpha = TRUE), | |
| props = paint( | |
| style = Style$Fill, | |
| blend_mode = BlendMode$Screen, | |
| canvas_size = size, | |
| ) | |
| ) |> | |
| as_png(props = paint(canvas_size = size)) |> | |
| aznyan::diffusion_filter() | |
| img | |
| }) | |
| ``` | |
| ```{r} | |
| #| label: save-img | |
| size <- as.integer(c(960, 540)) | |
| cv <- | |
| purrr::reduce(pngs, \(curr, nxt) { | |
| pts <- | |
| rasengan::wind_mouse( | |
| start = c(size[1] / 2, 0), | |
| end = c( | |
| runif(1, 0, size[1]), | |
| runif(1, 0, size[2]) | |
| ), | |
| mouse_speed = 24 | |
| ) | |
| curr |> | |
| add_atlas( | |
| nxt, | |
| dplyr::select(pts, x, y) |> | |
| dplyr::reframe( | |
| sc = seq(1, .25, length.out = nrow(pts)), | |
| rot = seq(-2 * pi, 2 * pi, length.out = nrow(pts)), | |
| x = x, | |
| y = y, | |
| ax = 0, | |
| ay = 0 | |
| ), | |
| props = paint( | |
| canvas_size = size, | |
| ) | |
| ) | |
| }, .init = canvas("#131313", canvas_size = size)) | |
| cv |> | |
| as_png(props = paint(canvas_size = size)) |> | |
| writeBin("temp.png") | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "08 何かをたくさん並べてみましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| #| label: setup | |
| library(skiagd) | |
| library(affiner) | |
| library(gifski) | |
| ## https://2d-sorcery.hatenablog.com/entry/2018-05-30 | |
| ## 同様の図形を出すにはXYをスワップする | |
| quadratrix <- \(n = 300, amp = sqrt(320), sc = 1) { | |
| theta <- seq(-sc * pi, sc * pi, length.out = n) | |
| dplyr::tibble( | |
| theta = theta, | |
| pos = dplyr::tibble( | |
| x = amp * sin(theta), | |
| y = (amp^2 / 2) * (theta + sin(theta) * cos(theta)), | |
| z = seq(-amp, amp, length.out = n), | |
| w = 1 | |
| ) |> as.matrix() | |
| ) | |
| } | |
| with( | |
| quadratrix(n = 50, amp = sqrt(320)) |> | |
| dplyr::mutate( | |
| pos = pos %*% | |
| transform3d() %*% | |
| permute3d("yxz") %*% | |
| scale3d(0.5, 10, 1) | |
| ), | |
| plot(pos[, 1], pos[, 2], type = "p") | |
| ) | |
| ``` | |
| ```{r} | |
| #| label: save-gif | |
| ## タイリングするピクチャのサイズ。実際のキャンバスは960x540 | |
| cv_size <- as.integer(c(360, 360)) | |
| dat <- | |
| purrr::imap(c(320, 160, 80, 64), \(amp, sc) { | |
| quadratrix(n = 30 * sc, amp = sqrt(amp), sc = sc) | |
| }) |> | |
| dplyr::bind_rows() |> | |
| dplyr::mutate(step = dplyr::if_else(runif(dplyr::n()) > 0.5, 1, 2)) |> | |
| dplyr::slice_sample(prop = 1) | |
| step <- 45 | |
| fps <- 15 | |
| duration <- (step / fps) * 10 | |
| bg_col <- "#150015" | |
| props <- list( | |
| canvas_size = cv_size, | |
| style = Style$Fill, | |
| blend_mode = BlendMode$Plus | |
| ) | |
| h <- sample.int(170, 1) / 360 | |
| gifski(purrr::map_chr(seq_len(duration * fps), \(i) { | |
| j <- i %% step + 1 ## [1, step] | |
| d <- dat |> | |
| dplyr::mutate( | |
| pos = pos %*% | |
| transform3d() %*% | |
| permute3d("yxz") %*% ## 軸の入れ替え | |
| scale3d(.5, 10, 1) %*% | |
| translate3d(cv_size[1] / 2, cv_size[2] / 2, 0) | |
| ) |> | |
| dplyr::group_by(step) |> | |
| dplyr::group_map(~.) | |
| if (i == 1) { | |
| angle_a <<- sample(seq(0, 180, length.out = nrow(d[[1]])), nrow(d[[1]])) | |
| angle_b <<- sample(seq(0, 180, length.out = nrow(d[[2]])), nrow(d[[2]])) | |
| } | |
| if (j == 1) { | |
| h <<- sample.int(180, 1) / 360 | |
| } | |
| t <- tweenr::tween_at(0, 2 * pi, j / step, ease = "cubic-in-out") | |
| deg <- rasengan::rad2deg(t) | |
| sw <- if (t > pi) 1 else -1 | |
| sc_a <- tweenr::tween_at(1, 0, j / step, ease = "cubic-in-out") | |
| sc_b <- tweenr::tween_at(0, 1, j / step, ease = "cubic-in-out") | |
| pict <- | |
| canvas(bg_col) |> | |
| ## step A: ワイプイン・フェードアウト | |
| add_arc( | |
| matrix(c(0, 0, 4, 4), nrow(d[[1]]), ncol = 4, byrow = TRUE), | |
| dplyr::reframe(d[[1]], | |
| sc = 5, | |
| rot = 0, | |
| x = pos[, 1], | |
| y = pos[, 2], | |
| ## 4x4なのでc(2, 2)をアンカーにするとx, yが中央になる | |
| ax = 2, | |
| ay = 2 | |
| ) |> as.matrix(), | |
| angle = matrix( | |
| c(angle_a, rep_len(deg, nrow(d[[1]]))), | |
| ncol = 2 | |
| ), | |
| props = paint( | |
| !!!props, | |
| color = grDevices::hsv(h, .9, 1, sc_a), | |
| ) | |
| ) |> | |
| ## Mask for step A: 背景色のマスクは拡大する | |
| add_circle( | |
| dplyr::pull(d[[1]], pos), | |
| rep_len(1.96 * 5 * (1 - sc_b), nrow(d[[1]])), | |
| props = paint( | |
| !!!props, | |
| color = if (sw < 0) bg_col else "transparent", | |
| ) | |
| ) |> | |
| ## step B: ワイプアウト・フェードイン | |
| add_arc( | |
| matrix(c(0, 0, 4, 4), nrow(d[[2]]), ncol = 4, byrow = TRUE), | |
| dplyr::reframe(d[[2]], | |
| sc = 5, | |
| rot = 0, | |
| x = pos[, 1], | |
| y = pos[, 2], | |
| ax = 2, | |
| ay = 2 | |
| ) |> as.matrix(), | |
| angle = matrix( | |
| c(angle_b, rep_len(deg, nrow(d[[2]]))), | |
| ncol = 2 | |
| ), | |
| props = paint( | |
| !!!props, | |
| color = grDevices::hsv(1 - h, .9, 1, sc_b), | |
| ) | |
| ) |> | |
| ## Mask for step B | |
| add_circle( | |
| dplyr::pull(d[[2]], pos), | |
| rep_len(1.96 * 5 * (1 - sc_a), nrow(d[[2]])), | |
| props = paint( | |
| !!!props, | |
| color = if (sw > 0) bg_col else "transparent", | |
| ) | |
| ) | |
| fp <- file.path(tempdir(), glue::glue("frame-{i}.png")) | |
| canvas(bg_col, canvas_size = c(960L, 540L)) |> | |
| add_rect( | |
| matrix(c(0, 0, 960, 540), ncol = 4), | |
| props = paint( | |
| canvas_size = c(960L, 540L), | |
| shader = Shader$from_picture( | |
| pict, | |
| mode = TileMode$Repeat, | |
| tile_size = cv_size / 2L, | |
| transform = diag(3) | |
| ), | |
| ## タイリングしたピクチャそれ自体もフェードアウトしないと変なので、透明度を変える | |
| color = grDevices::hsv( | |
| 0, 0, 0, | |
| tweenr::tween_at(1, .125, j / step, ease = "circular-in") | |
| ) | |
| ) | |
| ) |> | |
| as_png(props = paint(canvas_size = c(960L, 540L))) |> | |
| writeBin(con = fp) | |
| return(fp) | |
| }), delay = 1 / fps, width = 960L, height = 540L) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "09 お気に入りの曲を聴きながらコードを描いてみましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| #| label: test | |
| library(skiagd) | |
| library(gifski) | |
| n_points <- 40 | |
| canvas("#050002") |> | |
| add_circle( | |
| matrix( | |
| c( | |
| rep(dev_size()[1] / 2, n_points) + rnorm(n_points, sd = 2), | |
| runif(n_points, 0, dev_size()[2]) | |
| ), | |
| ncol = 2 | |
| ), | |
| runif(n_points, 6, 16), | |
| color = grDevices::hsv(seq(0, 1, length.out = n_points), 1, 1) |> | |
| grDevices::col2rgb(alpha = TRUE), | |
| props = paint(width = 4, blend_mode = BlendMode$Plus) | |
| ) |> | |
| as_png() |> | |
| aznyan::gaussian_blur(6) |> | |
| aznyan::preserve_edge(800, 100) |> | |
| aznyan::diffusion_filter() |> | |
| add_png(canvas("transparent"), png = _) |> | |
| draw_img() | |
| ``` | |
| ```{r} | |
| #| label: render | |
| fps <- 15 | |
| cv_size <- as.integer(c(480, 360)) | |
| n_points <- 40 | |
| x <- runif(n_points, -2, cv_size[1] + 2) | |
| col <- | |
| sample(c("#c97300", "#00bbc9"), n_points, replace = TRUE) |> | |
| grDevices::col2rgb(alpha = TRUE) | |
| gifski(purrr::map_chr(seq_len(300), \(i) { | |
| j <- i / fps | |
| nz <- | |
| rasengan::noise_2d()(seq_len(n_points), j, seed = fps) |> | |
| rasengan::normalize(to = c(3, 8)) | |
| y <- rep(cv_size[2] / 2, n_points) + | |
| sin((j * seq(-2 * pi, 2 * pi, length.out = n_points)) / 4) * 12 | |
| img <- | |
| canvas("#050002", canvas_size = cv_size) |> | |
| add_circle( | |
| matrix(c(x, y), ncol = 2), | |
| 2 * nz, | |
| color = col, | |
| props = paint( | |
| canvas_size = cv_size, | |
| width = 3, | |
| style = Style$Stroke, | |
| blend_mode = BlendMode$Plus, | |
| path_effect = PathEffect$discrete(2, 3, 1), | |
| ) | |
| ) |> | |
| as_png(props = paint(canvas_size = cv_size)) |> | |
| aznyan::gaussian_blur(6) |> | |
| aznyan::preserve_edge(800, 100) |> | |
| aznyan::diffusion_filter() | |
| fp <- file.path(tempdir(), sprintf("%04d.png", i)) | |
| writeBin(img, con = fp) | |
| fp | |
| }), delay = 1 / fps, width = cv_size[1], height = cv_size[2]) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "10 液体のような形を描いてみましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| #| label: render | |
| library(skiagd) | |
| library(gifski) | |
| cv_size <- c(480L, 360L) | |
| fps <- 15 | |
| n_points <- 500L | |
| n_seq <- 180L ## フレーム数 | |
| pal <- c( | |
| "#ff003b", | |
| "#ff4500", | |
| "#ff00ba", | |
| "#00baff" | |
| ) | |
| dat <- | |
| dplyr::tibble( | |
| id = seq_len(n_points), | |
| init_x = runif(n_points, 0, cv_size[1]), | |
| init_y = runif(n_points, 0, cv_size[2]), | |
| direction = ifelse(runif(n_points) > 0.5, 1, -1), | |
| velocity = runif(n_points, pi / 16, 4 * pi) * direction, | |
| damping = -1, | |
| radii = runif(n_points, 12, 16), | |
| col = sample(pal, n_points, replace = TRUE) | |
| ) |> | |
| dplyr::reframe( | |
| frame_id = seq_len(n_seq), | |
| x = rasengan::bounce_off(n_seq, init_x, velocity, c(0, cv_size[1]), damping), | |
| y = rasengan::bounce_off(n_seq, init_y, velocity, c(0, cv_size[2]), damping), | |
| radii = radii, | |
| col = col, | |
| .by = id | |
| ) | |
| save_gif(lapply(seq_len(n_seq), \(i) { | |
| d <- dplyr::filter(dat, frame_id == i) | |
| nz <- matrix(rasengan::noise_3d()(1:nrow(d), 1:2, as.double(i)), ncol = 2) |> | |
| rasengan::normalize(from = c(-1, 1), to = c(-3, 3)) | |
| ## テクスチャ | |
| img <- | |
| canvas("#001305") |> | |
| add_circle( | |
| matrix(c(d$x, d$y), ncol = 2) + nz, | |
| d$radii, | |
| color = grDevices::col2rgb(d$col, alpha = TRUE), | |
| props = paint( | |
| # color = "snow", | |
| blend_mode = BlendMode$Screen, | |
| ) | |
| ) |> | |
| as_png() |> | |
| aznyan::gaussian_blur(8) |> | |
| aznyan::median_blur(12) |> | |
| aznyan::preserve_edge() | |
| ## 水色のキャンバスにハードライトする | |
| canvas("#00ffc5") |> | |
| add_rect( | |
| matrix(c(0, 0, cv_size), ncol = 4), | |
| props = paint( | |
| color = "white", | |
| blend_mode = BlendMode$HardLight, | |
| shader = Shader$from_png(img, TileMode$Clamp, diag(3)) | |
| ) | |
| ) |> | |
| draw_img() | |
| }), delay = 1 / fps, width = cv_size[1], height = cv_size[2], progress = TRUE) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "11 質感のあるコードを描きましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| #| label: flower | |
| library(skiagd) | |
| library(affiner) | |
| library(gifski) | |
| ## 三つ葉 | |
| trifolium <- \(n, offset = 0, sc = 1) { | |
| theta <- offset + seq(-pi, pi, length.out = n) | |
| r <- sc * cos(3 * theta) | |
| as.matrix(data.frame( | |
| x = r * cos(theta), | |
| y = r * sin(theta), | |
| z = 1 | |
| )) | |
| } | |
| dat <- | |
| purrr::map(seq_len(60), \(i) { | |
| dplyr::tibble( | |
| id = i, | |
| col = grDevices::hsv(.0083, 1, seq(.333, .773, length.out = 100), alpha = .8), | |
| pos = trifolium(100, offset = i / 60, sc = i) | |
| ) | |
| }) |> | |
| dplyr::bind_rows() | |
| ``` | |
| ```{r} | |
| #| label: stem | |
| ## Excalidrawで手描きした線 | |
| stem <- | |
| "M 1.46,-0.54 Q 1.46,-0.54 2.20,1.56 2.95,3.68 3.98,5.52 5.02,7.37 6.00,9.27 6.98,11.18 8.65,13.67 10.31,16.16 12.64,21.19 14.96,26.23 18.01,33.61 21.05,40.98 24.69,53.28 28.33,65.57 32.72,84.11 37.11,102.65 41.05,121.74 44.98,140.82 47.95,160.52 50.92,180.21 52.18,199.47 53.45,218.74 53.73,236.04 54.00,253.34 52.83,268.44 51.65,283.55 48.54,294.96 45.43,306.38 42.54,313.70 39.65,321.02 37.65,325.52 35.64,330.03 34.26,332.68 32.88,335.32 31.77,336.90 30.67,338.49 29.50,340.24 28.34,342.00 28.14,342.19 27.95,342.37 27.70,342.49 27.46,342.61 27.19,342.64 26.92,342.67 26.65,342.61 26.39,342.56 26.15,342.42 25.92,342.28 25.74,342.08 25.57,341.87 25.46,341.62 25.36,341.37 25.34,341.10 25.33,340.83 25.40,340.56 25.47,340.30 25.62,340.08 25.77,339.85 25.98,339.69 26.20,339.52 26.45,339.43 26.71,339.34 26.98,339.34 27.25,339.34 27.51,339.42 27.77,339.51 27.99,339.67 28.20,339.83 28.36,340.06 28.51,340.28 28.59,340.54 28.66,340.80 28.65,341.07 28.63,341.34 28.54,341.60 28.44,341.85 28.26,342.06 28.09,342.26 27.86,342.41 27.63,342.55 27.36,342.61 27.10,342.67 26.83,342.64 26.56,342.61 26.31,342.50 26.06,342.39 25.87,342.20 25.67,342.02 25.54,341.78 25.41,341.54 25.36,341.27 25.32,341.01 25.36,340.74 25.40,340.47 25.53,340.23 25.65,339.99 25.65,339.99 25.65,339.99 27.01,338.37 28.37,336.76 29.46,335.43 30.55,334.10 31.90,331.53 33.24,328.96 35.22,324.51 37.21,320.05 40.05,312.87 42.89,305.68 45.96,294.51 49.02,283.33 50.19,268.36 51.37,253.38 51.09,236.15 50.81,218.91 49.56,199.76 48.30,180.60 45.34,160.98 42.38,141.36 38.45,122.31 34.52,103.26 30.14,84.80 25.76,66.33 22.16,54.17 18.55,42.01 15.52,34.69 12.48,27.37 10.25,22.54 8.01,17.70 6.25,15.10 4.49,12.49 3.50,10.65 2.50,8.80 1.33,6.76 0.15,4.72 -0.65,2.63 -1.46,0.54 -1.50,0.35 -1.55,0.17 -1.55,-0.01 -1.54,-0.20 -1.50,-0.38 -1.45,-0.56 -1.36,-0.73 -1.27,-0.89 -1.15,-1.03 -1.02,-1.17 -0.87,-1.28 -0.71,-1.38 -0.53,-1.45 -0.36,-1.52 -0.17,-1.54 0.01,-1.56 0.20,-1.53 0.38,-1.51 0.56,-1.44 0.73,-1.37 0.89,-1.26 1.04,-1.16 1.17,-1.01 1.29,-0.87 1.37,-0.70 1.46,-0.54 1.46,-0.54 L 1.46,-0.54 Z" | |
| path_bounds(stem) | |
| ``` | |
| ```{r} | |
| #| label: render | |
| n_dup <- 32 | |
| rot <- seq(-5 * pi, 5 * pi, length.out = n_dup) | |
| sc <- seq(1, 2.4, length.out = n_dup) | |
| cv_size <- c(320L, 640L) | |
| cv <- list(canvas_size = cv_size) | |
| canvas("transparent", canvas_size = cv_size) |> | |
| ## 背景のグラデーション | |
| add_rect( | |
| matrix(c(0, 0, cv_size), ncol = 4), | |
| props = paint( | |
| canvas_size = cv_size, | |
| color = "black", | |
| shader = Shader$linear_gradient( | |
| start = c(0, 0), end = cv_size + c(9, 16) * 8, | |
| from = col2rgba("#6d00c5"), | |
| to = col2rgba("#bbc500"), | |
| mode = TileMode$Clamp, | |
| flags = FALSE, | |
| transform = diag(3) | |
| ), | |
| ) | |
| ) |> | |
| ## 茎 | |
| add_path( | |
| stem, | |
| matrix(c(1.2, 0, cv_size / 2, 0, 0), ncol = 6), | |
| props = paint(!!!cv, color = "#59c500") | |
| ) |> | |
| ## 花 | |
| purrr::reduce(rev(seq_len(n_dup)), \(acc, i) { | |
| add_circle(acc, | |
| ceiling(dplyr::pull(dat, pos)) %*% | |
| transform2d() %*% | |
| scale2d(sc[i]) %*% | |
| rotate2d(i * rot[i]) %*% | |
| translate2d(cv_size[1] / 2, cv_size[2] / 2), | |
| rep_len(1.4, nrow(dat)), | |
| color = grDevices::col2rgb(dat$col, alpha = TRUE), | |
| props = paint( | |
| !!!cv, | |
| width = 1, | |
| style = Style$Stroke, | |
| blend_mode = BlendMode$Screen, | |
| ) | |
| ) | |
| }, .init = _) |> | |
| as_png(props = paint(!!!cv)) |> | |
| writeBin("flower.png") | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "12 誰かにメッセージを伝えるためのコードを描きましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| #| label: setup | |
| library(skiagd) | |
| library(affiner) | |
| library(gifski) | |
| #' なんかくるっと動くlerp | |
| #' | |
| #' @param src,dst Double matrix of source and destination points. | |
| #' @param mask Amount of transition. | |
| #' @returns Interpolated points. | |
| lerp <- function(src, dst, mask) { | |
| v <- rasengan::blend(dst, src, mask) | |
| theta <- | |
| tweenr::tween_at(-pi / 2, pi / 2, mask, "circular-in-out") | |
| rot <- atan2(v[, 2], v[, 1]) | |
| v + cos(theta) * sin(mask * rot * pi) | |
| } | |
| ``` | |
| ```{r} | |
| #| label: render | |
| ## それぞれの文字はだいたい[0, 1]の正方形の範囲に正規化されるが、 | |
| ## フォントなので正方形からはみ出しがち | |
| paths <- | |
| string2path::string2fill( | |
| "えらい!!", | |
| font = here::here("src/fonts/keifont.ttf") | |
| ) |> | |
| dplyr::mutate( | |
| ## 対応する点を適当につくる | |
| src_x = rnorm(dplyr::n(), mean = 2, sd = .5), ## 中央はだいたい2文字目の位置 | |
| src_x = rasengan::cap(src_x, lower = 1.96, upper = 2.04), | |
| src_y = rnorm(dplyr::n(), mean = 0, sd = .1), ## 高さは1文字分 | |
| src_y = rasengan::cap(src_y, lower = -.5, upper = .5), | |
| color = hsv(0, .77, seq(.2, 1, length.out = dplyr::n()), alpha = 1), | |
| .by = c(glyph_id, triangle_id) | |
| ) | |
| ## マッピングをつくる。ここでは「左上・左下・右下・右上」の順 | |
| m <- cbind(range(paths$x), range(paths$y)) | |
| trans <- create_mapping( | |
| matrix( | |
| c(m[1, 1], m[2, 2], m[1, 1], m[1, 2], m[2, 1], m[1, 2], m[2, 1], m[2, 2]), | |
| ncol = 2, | |
| byrow = TRUE | |
| ), | |
| matrix( | |
| c(-2, 1, -2, -1, 2, -1, 2, 1), ## 等幅だとして4文字分あるので、横幅は左右に2文字分 | |
| ncol = 2, | |
| byrow = TRUE | |
| ) | |
| ) | |
| trans | |
| cv_size <- c(384L, 288L) | |
| save_gif(lapply(seq_len(75), function(i) { | |
| ## アニメーションの終わりでちょっと止めたいのでclampする | |
| j <- rasengan::cap(i, lower = 1, upper = 60) / 60 | |
| j <- tweenr::tween_at(0, 1, at = j, ease = "exponential-in-out") | |
| d <- lerp( | |
| as.matrix(dplyr::select(paths, src_x, src_y)), | |
| as.matrix(dplyr::select(paths, x, y)), | |
| mask = j | |
| ) | |
| canvas("white") |> | |
| add_rect( | |
| matrix(c(0, 0, cv_size), ncol = 4), | |
| props = paint( | |
| shader = Shader$linear_gradient( | |
| c(0, 0), | |
| cv_size / 2, | |
| from = col2rgba("#b9f8cd"), | |
| to = col2rgba("#f8cdb9"), | |
| mode = TileMode$Clamp, | |
| flags = FALSE, | |
| transform = diag(3) | |
| ) | |
| ) | |
| ) |> | |
| add_vertices( | |
| cbind(d, 1) %*% | |
| trans %*% | |
| scale2d(72 * j) %*% | |
| reflect2d("x-axis") %*% | |
| translate2d(cv_size[1] / 2, cv_size[2] / 2), | |
| color = dplyr::pull(paths, color) |> | |
| grDevices::col2rgb(alpha = TRUE), | |
| props = paint( | |
| blend_mode = BlendMode$HardLight, | |
| shader = Shader$sweep_gradient( | |
| cv_size / 2, | |
| 0, 300, | |
| from = col2rgba("#3de2a3"), | |
| to = col2rgba("#e2503d"), | |
| mode = TileMode$Clamp, | |
| flags = FALSE, | |
| transform = diag(3) | |
| ) | |
| ) | |
| ) |> | |
| as_png() |> | |
| ## ぼかさないと不要な線が目立つので | |
| aznyan::median_blur(2) |> | |
| aznyan::gaussian_blur(1) |> | |
| add_png(canvas("transparent"), png = _) |> | |
| draw_img() | |
| }), delay = 1 / 30, width = cv_size[1], height = cv_size[2]) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "13 文字をかたちとして捉えてコードを描いてみましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| #| label: render | |
| library(skiagd) | |
| library(affiner) | |
| library(gifski) | |
| cv_size <- c(480L, 360L) | |
| sc <- 24L | |
| blend <- list( | |
| canvas_size = cv_size, | |
| width = 3, | |
| blend_mode = BlendMode$Plus, | |
| path_effect = PathEffect$dash(c(3, 3), 0) | |
| ) | |
| chr <- R"{ | |
| ↑上←左右→下↓ | |
| }" | |
| row <- 1 ## 縦 | |
| col <- stringr::str_count(chr) | |
| dat <- | |
| string2path::string2path( | |
| chr, | |
| font = here::here("src/fonts/851Gkktt_005.ttf") ## 851ゴチカクット | |
| ) |> | |
| dplyr::reframe( | |
| glyph_id = dplyr::consecutive_id(glyph_id, path_id), | |
| color = path_id, | |
| pos = dplyr::tibble( | |
| x = rasengan::normalize(x, to = c(-col, col)), | |
| y = rasengan::normalize(y, to = c(-row, row)), | |
| ## 厚みをもたせすぎると、角度を付けたときに文字だとわかりづらいのでほどほどに | |
| z = rasengan::normalize(sin(path_id) * cos(glyph_id %% 3), to = c(-.6, .6)) | |
| ), | |
| ) | |
| bg <- | |
| dplyr::tibble( | |
| pos = dplyr::tibble( | |
| x = runif(800, min = -1), | |
| y = runif(800, min = -1), | |
| z = 1 | |
| ) |> as.matrix() | |
| ) | |
| gifski(purrr::map_chr(seq_len(360), function(i) { | |
| j <- sin(i %% 60 / 60 * pi) | |
| theta <- tweenr::tween_at(-pi / 4, pi / 4, at = j, ease = "linear") | |
| sc_factor <- cos(theta) + 2.0 | |
| deg <- rasengan::rad2deg(theta) | |
| paths <- dat |> | |
| dplyr::mutate( | |
| pos = dplyr::mutate(pos, | |
| x = rasengan::blend(0, x, j), | |
| y = rasengan::blend(0, y, j), | |
| z = z, | |
| w = 1 | |
| ), | |
| pos1 = as.matrix(pos) %*% | |
| transform3d() %*% | |
| reflect3d("zx-plane") %*% | |
| rotate3d("x-axis", deg) %*% | |
| rotate3d("y-axis", i) %*% | |
| rotate3d("z-axis", i) %*% | |
| scale3d(sc * sc_factor) %*% | |
| translate3d(cv_size[1] / 2, cv_size[2] / 2, 0), | |
| pos = dplyr::mutate(pos, | |
| z = z * j | |
| ), | |
| pos2 = as.matrix(pos) %*% | |
| transform3d() %*% | |
| reflect3d("zx-plane") %*% | |
| rotate3d("x-axis", deg) %*% | |
| rotate3d("y-axis", i) %*% | |
| rotate3d("z-axis", i) %*% | |
| scale3d((sc + .5) * sc_factor) %*% | |
| translate3d(cv_size[1] / 2, cv_size[2] / 2, 0) | |
| ) | |
| img <- | |
| canvas("black", canvas_size = cv_size) |> | |
| add_point( | |
| dplyr::pull(bg, pos) %*% | |
| transform2d() %*% | |
| rotate2d(-i / sc) %*% | |
| scale2d(250) %*% | |
| translate2d(cv_size[1] / 2, cv_size[2] / 2), | |
| props = paint(color = "snow") | |
| ) |> | |
| ## 一度に描いたほうが速いが、余計なパスが生えるため、一つ一つ描く | |
| ## パスの数が多いとピクチャ数の制限にひっかかるかも | |
| purrr::reduce(unique(paths$glyph_id), function(cv, id) { | |
| d <- dplyr::filter(paths, glyph_id == id) | |
| cv |> | |
| add_point( | |
| dplyr::pull(d, pos1), | |
| props = paint( | |
| color = "#00eaffee", | |
| point_mode = PointMode$Polygon, | |
| !!!blend, | |
| ) | |
| ) |> | |
| add_point( | |
| dplyr::pull(d, pos2), | |
| props = paint( | |
| color = "#ff00eaee", | |
| point_mode = PointMode$Polygon, | |
| !!!blend, | |
| ) | |
| ) |> | |
| add_line( | |
| dplyr::pull(d, pos1), | |
| dplyr::pull(d, pos2), | |
| props = paint( | |
| color = "#eaff00ee", | |
| !!!blend, | |
| ) | |
| ) | |
| }, .init = _) |> | |
| as_png(props = paint(!!!blend)) | |
| ## 本番では光らせている | |
| # |> aznyan::diffusion_filter() | |
| fp <- file.path(tempdir(), sprintf("%03d.png", i)) | |
| writeBin(img, fp) | |
| fp | |
| }), delay = 1 / 20, width = cv_size[1], height = cv_size[2]) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "15-6 Drift and Gradient" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| #| label: squircle | |
| squircle <- \(n, tz, scale = 1) { | |
| theta <- seq(0, 2 * pi, length.out = n) | |
| cos <- cos(theta) | |
| sin <- sin(theta) | |
| dplyr::tibble( | |
| x = sqrt(abs(cos)) * scale * sign(cos), | |
| y = sqrt(abs(sin)) * scale * sign(sin), | |
| z = sin(seq(-2 * pi, 2 * pi, length.out = n) + tz) * scale, | |
| w = seq(0, 1, length.out = n + 1)[-1] | |
| ) | |
| } | |
| # with(squircle(50, 6), plot(x, y)) | |
| ``` | |
| ```{r} | |
| #| label: render | |
| library(skiagd) | |
| library(affiner) | |
| library(gifski) | |
| cv_size <- c(360L, 640L) ## 9:16 | |
| center <- cv_size / 2 | |
| n_sq <- 150 | |
| pal <- hsv(seq(0.45, 0.92, length.out = n_sq), .66, .876, alpha = .85) | |
| dat <- | |
| dplyr::tibble( | |
| id = seq_len(n_sq), | |
| sc = seq(18, 180, length.out = n_sq), ## scale | |
| col = factor(seq_len(n_sq)) | |
| ) | |
| save_gif(lapply(seq(-8 * pi, 8 * pi, length.out = 300), \(j) { | |
| sq <- dat |> | |
| dplyr::mutate( | |
| pos = list(squircle(n_sq, j, 1)), | |
| theta = seq(-j, j, length.out = n_sq), ## rotation | |
| ) |> | |
| tidyr::unnest(pos) |> | |
| dplyr::group_by(sc, theta) |> | |
| dplyr::group_modify(~ { | |
| trans <- transform3d() %*% | |
| rotate3d("z-axis", 72) %*% | |
| rotate3d("x-axis", rasengan::rad2deg(.y$theta)) %*% | |
| permute3d("yxz") %*% | |
| scale3d(.y$sc) %*% | |
| translate3d(center[1] + 180, center[2] + 180, 0) | |
| k <- ceiling(nrow(.x) * rasengan::normalize(j, from = c(-pi, pi))) | |
| dplyr::tibble( | |
| from = matrix( | |
| c(.x$x, .x$y, .x$z, .x$w), | |
| ncol = 4 | |
| ) %*% trans, | |
| to = matrix( | |
| c(c(.x$x[-1], .x$x[1]), c(.x$y[-1], .x$y[1]), c(.x$z[-1], .x$z[1]), .x$w), | |
| ncol = 4 | |
| ) %*% trans, | |
| col = pal[as.integer(forcats::fct_shift(.x$col, k))], | |
| width = .x$w + 1 | |
| ) | |
| }) |> | |
| dplyr::ungroup() | |
| canvas("#150015") |> | |
| add_line( | |
| sq |> | |
| dplyr::pull(from), | |
| sq |> | |
| dplyr::pull(to), | |
| color = sq |> | |
| dplyr::pull(col) |> | |
| colorfast::col_to_rgb(), | |
| width = sq |> | |
| dplyr::pull(width), | |
| props = paint( | |
| path_effect = PathEffect$discrete(cos(j) + 2, 1, 0), | |
| blend_mode = BlendMode$Screen, | |
| cap = Cap$Square, | |
| ) | |
| ) |> | |
| draw_img() | |
| }), delay = 1 / 20, width = cv_size[1], height = cv_size[2], gif_file = "out/squircle.gif") | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "17 梅雨をコードで表現してみましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| ```{r} | |
| library(ggplot2) | |
| library(gifski) | |
| bg_col <- "gray10" | |
| ## 停滞前線の画像 | |
| front_line <- | |
| readBin( | |
| con = here::here("public/frontline.png"), | |
| what = "raw", | |
| n = file.info(here::here("public/frontline.png"))$size | |
| ) |> | |
| aznyan::box_blur(10) |> | |
| fastpng::read_png(type = "nativeraster") | |
| ## 「低」ラベルの位置 | |
| labs <- | |
| dplyr::tibble( | |
| x = runif(32, 0, 256), | |
| y = runif(32, 0, 256), | |
| ) | |
| cap <- ragg::agg_capture(width = 384, height = 288) | |
| pngs <- purrr::imap_chr(seq(90, 0, by = -0.5), function(i, f) { | |
| s <- seq(0, 256, by = 8) | |
| dat <- | |
| tidyr::expand_grid(x = s + i, y = s - i / 2) |> | |
| dplyr::mutate( | |
| z = rasengan::noise_2d("Cellular", fractal_type = "FBm")(data = as.matrix(dplyr::pick(x, y)), seed = 123), | |
| z = rasengan::fract(z) * 1000 | |
| ) | |
| gp <- | |
| ggplot(dat, aes(x = x, y = y)) + | |
| geom_contour_filled(aes(z = z), bins = 7) + | |
| geom_label( | |
| data = labs |> | |
| dplyr::mutate(x = x + i, y = y - i / 2), | |
| label = "低", | |
| label.r = unit(1, "cm"), | |
| fill = "red", | |
| color = "snow", | |
| alpha = 0.4, | |
| size = 13, | |
| ) + | |
| annotation_raster( | |
| front_line, | |
| xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf | |
| ) + | |
| scale_fill_manual( | |
| values = c("purple", "red", "yellow", "green", "blue", "cyan", "gray") | |
| ) + | |
| labs( | |
| title = "脳内雨雲レーダーのようす(実況)", | |
| caption = sprintf("6/18 %s", format(Sys.time() - ((90 - f) * 60), "%H:%M")) | |
| ) + | |
| theme_void() + | |
| theme( | |
| plot.title = element_text(size = 20, color = "snow"), | |
| plot.caption = element_text(size = 12, color = "snow"), | |
| plot.background = element_rect( | |
| fill = bg_col, colour = bg_col | |
| ), | |
| panel.background = element_rect( | |
| fill = bg_col, colour = bg_col | |
| ), | |
| legend.position = "none", | |
| plot.margin = unit(c(0, 0, 0, 0), "cm") | |
| ) | |
| print(gp) | |
| rast <- cap(native = TRUE) | |
| fp <- file.path(tempdir(), sprintf("contour-%03d.png", f)) | |
| rast |> | |
| fastpng::write_png(file = NULL) |> | |
| aznyan::preserve_edge() |> | |
| aznyan::gaussian_blur(2) |> | |
| writeBin(con = fp) | |
| fp | |
| }, .progress = TRUE) | |
| dev.off() | |
| gifski(pngs, gif_file = "contour.gif", width = 388, height = 288, delay = 1 / 15) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "18-9 Pattern and Quick" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| library(skiagd) | |
| cv_size <- c(960L, 540L) | |
| size <- list(canvas_size = cv_size) | |
| bs <- 12 * 12 | |
| rep <- 120 | |
| rects <- | |
| lapply(1:rep, \(i) { | |
| k <- rep - i | |
| rectpacker::pack_rects(bs, bs, sample(rep), sample(rep)) |> | |
| dplyr::filter(packed) |> | |
| dplyr::mutate( | |
| pid = i, | |
| x = x + cv_size[1] / bs * k, | |
| y = y + cv_size[2] / bs * k | |
| ) | |
| }) |> | |
| dplyr::bind_rows() | |
| # len <- dplyr::group_by(rects, pid) |> dplyr::summarise(n = dplyr::n()) | |
| # len | |
| pic <- | |
| canvas("snow", canvas_size = cv_size) |> | |
| add_rect( | |
| rects |> | |
| dplyr::select(x, y, w, h) |> | |
| as.matrix(), | |
| props = paint( | |
| !!!size, | |
| width = .1, | |
| cap = Cap$Square, | |
| join = Join$Bevel, | |
| style = Style$Stroke, | |
| ) | |
| ) | |
| # pic |> | |
| # draw_img(props = paint(!!!size)) | |
| pic |> | |
| as_png(props = paint(!!!size)) |> | |
| aznyan::preserve_edge(6) |> | |
| writeBin("rects.png") | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "20 途中まで作って誰かに話して、そこからまた続きを描いてみましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| library(skiagd) | |
| library(gifski) | |
| cv_size <- c(320L, 240L) | |
| rects <- | |
| rectpacker::pack_rects( | |
| cv_size[1], cv_size[2], | |
| sample.int(cv_size[1] / 12 * 4, 200, replace = TRUE), | |
| sample.int(cv_size[2] / 12 * 3, 200, replace = TRUE) | |
| ) |> | |
| dplyr::filter(packed == TRUE) |> | |
| dplyr::select(x, y, w, h) | |
| n_rects <- nrow(rects) | |
| steps <- rep(c(0, 1), each = 15) | |
| blend <- list( | |
| canvas_size = cv_size, | |
| style = Style$Fill, | |
| blend_mode = BlendMode$Xor, | |
| path_effect = PathEffect$path_1d("M -10 0 L 0 -10, 10 0, 0 10 Z", 4, 0, "rotate") | |
| ) | |
| gifski(purrr::imap_chr(rep(steps, each = 12), \(k, i) { | |
| f <- i %% 45 / 45 | |
| t <- rasengan::smootherstep(f) | |
| sc <- c(1 - t, t) |> | |
| rasengan::shift(k) |> | |
| rep(n_rects / 2) | |
| png <- | |
| canvas("snow", canvas_size = cv_size) |> | |
| add_rect( | |
| rects |> | |
| dplyr::slice_head(n = length(sc)) |> | |
| dplyr::mutate( | |
| x = x + 4, | |
| y = y + 3, | |
| w = w * sc, | |
| h = h * sc | |
| ) |> | |
| as.matrix(), | |
| color = seq(.2908, .9575, length.out = length(sc)) |> | |
| rasengan::shift(i) |> | |
| grDevices::hsv(1, 1, alpha = .5) |> | |
| colorfast::col_to_rgb(), | |
| props = paint(!!!blend) | |
| ) |> | |
| as_png(props = paint(!!!blend)) | |
| fp <- file.path(tempdir(), sprintf("m20-%04d.png", i)) | |
| writeBin(png, fp) | |
| fp | |
| }), delay = 1 / 15, width = cv_size[1], height = cv_size[2], progress = TRUE) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "21 枠線などの線だけを使って描いてみましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| library(skiagd) | |
| library(affiner) | |
| library(gifski) | |
| cb <- | |
| ## 引数のpartial matchの警告が出るため | |
| suppressWarnings({ | |
| geozoo::cube.dotline() |> | |
| unclass() | |
| }) | |
| pts <- | |
| rasengan::normalize(cb$points, from = c(0, 1), to = c(-1, 1)) | |
| n_seq <- nrow(pts) * 3 | |
| cv_size <- c(640L, 480L) | |
| prps <- list( | |
| canvas_size = cv_size, | |
| width = .45, | |
| blend_mode = BlendMode$Plus, | |
| ## 実際にはdashは効いてないかも | |
| path_effect = c(PathEffect$dash(c(2, 1), 0), PathEffect$discrete(3, 2, 1)) | |
| ) | |
| imgs <- purrr::map_chr(seq_len(n_seq), \(i) { | |
| f <- i %% 12 | |
| t <- rasengan::ease_in_out(f / 12, "quad") | |
| d <- | |
| (cbind(pts, 1) %*% | |
| transform3d() %*% | |
| scale3d(96.03, 96, 96) %*% | |
| rotate3d("y-axis", t * 360 + 24) %*% | |
| rotate3d("x-axis", 24) | |
| ) |> | |
| dplyr::as_tibble(.name_repair = ~ c("x", "y", "z", "w")) |> | |
| dplyr::mutate(x = x + cv_size[1] / 2, y = y + cv_size[2] / 2) | |
| png <- | |
| canvas("#000a0a", canvas_size = cv_size) |> | |
| add_line( | |
| d |> | |
| dplyr::arrange(z, x) |> | |
| as.matrix(), | |
| d |> | |
| dplyr::arrange(z, x) |> | |
| dplyr::mutate( | |
| x = dplyr::lag(x, default = dplyr::first(x)), | |
| y = dplyr::lag(y, default = dplyr::first(y)) | |
| ) |> | |
| as.matrix(), | |
| color = ifelse(runif(nrow(d)) > .72, "#00e2e2dd", "#ffc500dd") |> | |
| colorfast::col_to_rgb(), | |
| props = paint( | |
| !!!prps, | |
| ) | |
| ) |> | |
| add_line( | |
| d |> | |
| dplyr::arrange(x, y) |> | |
| dplyr::mutate( | |
| x = rasengan::shift(x, i %% 3), | |
| y = rasengan::shift(y, i %% 3) | |
| ) |> | |
| as.matrix(), | |
| d |> | |
| dplyr::arrange(x, y) |> | |
| dplyr::mutate( | |
| x = rasengan::shift(x, i %% 5 + 26), | |
| y = rasengan::shift(y, i %% 5 + 26) | |
| ) |> | |
| as.matrix(), | |
| props = paint( | |
| !!!prps, | |
| color = "#ff003bdd", | |
| ) | |
| ) |> | |
| add_line( | |
| d |> | |
| dplyr::arrange(y, z) |> | |
| dplyr::mutate( | |
| x = rasengan::shift(x, i %% 5), | |
| y = rasengan::shift(y, i %% 5) | |
| ) |> | |
| as.matrix(), | |
| d |> | |
| dplyr::arrange(y, z) |> | |
| dplyr::mutate( | |
| x = rasengan::shift(x, i %% 7 + 52), | |
| y = rasengan::shift(y, i %% 7 + 52) | |
| ) |> | |
| as.matrix(), | |
| props = paint( | |
| !!!prps, | |
| color = "#ff4500dd", | |
| ) | |
| ) |> | |
| as_png(props = paint(!!!prps)) |> | |
| aznyan::diffusion_filter() | |
| fp <- file.path(tempdir(), sprintf("gz-%03d.png", i)) | |
| writeBin(png, fp) | |
| fp | |
| }, .progress = TRUE) | |
| gifski(imgs, delay = 1 / 6, width = cv_size[1], height = cv_size[2]) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "22 スクリーンセーバーに使えるコードを描いてみましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| library(skiagd) | |
| cv_size <- c(960L, 540L) | |
| prps <- list( | |
| canvas_size = cv_size | |
| ) | |
| s <- 60 | |
| coord <- | |
| tidyr::expand_grid( | |
| col = c(0, seq_len(960 / s)), | |
| row = c(0, seq_len(540 / s)) | |
| ) |> | |
| dplyr::mutate( | |
| id = dplyr::consecutive_id(col, row), | |
| x = col * s, | |
| y = row * s, | |
| w = x + s, | |
| h = y + s, | |
| sd = runif(dplyr::row_number()) | |
| ) | |
| scenario <- | |
| seq_len(9) |> | |
| rev() |> | |
| rep(each = 6) |> | |
| rep(6) | |
| pngs <- purrr::imap_chr(scenario, \(f, i) { | |
| p <- seq(0, 2 * pi, length.out = 9 * 6) | |
| g <- cos(p[i %% length(p) + 1]) |> | |
| rasengan::ease_in_out("sine") | |
| d <- coord |> | |
| dplyr::mutate( | |
| rx = 24 * g * rasengan::pulse(col + row, sd), | |
| rx = dplyr::if_else( | |
| row %% 2 == 1, | |
| rasengan::shift(rx, -f), | |
| rasengan::shift(rx, f) | |
| ), | |
| ry = rasengan::shift(rx, f), | |
| color = rasengan::shift( | |
| grDevices::hsv( | |
| (col + row) %% 5 / 5, | |
| .5, .9, | |
| alpha = 1 | |
| ), | |
| f | |
| ) | |
| ) | |
| img <- | |
| canvas("snow", canvas_size = cv_size) |> | |
| add_rect( | |
| d |> | |
| dplyr::select(x, y, w, h) |> | |
| as.matrix(), | |
| radii = d |> | |
| dplyr::select(rx, ry) |> | |
| as.matrix(), | |
| color = dplyr::pull(d, color) |> | |
| colorfast::col_to_rgb(), | |
| props = paint( | |
| !!!prps, | |
| style = Style$Fill, | |
| ) | |
| ) |> | |
| as_png(props = paint(!!!prps)) | |
| fp <- file.path("public/pictures", sprintf("%04d.png", i)) | |
| writeBin(img, fp) | |
| fp | |
| }, .progress = TRUE) | |
| gifski::gifski( | |
| pngs, "out/temp.gif", | |
| width = cv_size[1], height = cv_size[2], | |
| delay = 1 / 15 | |
| ) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "24 音を感じるコードを描いてみましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| library(skiagd) | |
| library(gifski) | |
| cv_size <- c(640L, 480L) | |
| prps <- list( | |
| canvas_size = cv_size | |
| ) | |
| n_pts <- 500 | |
| duration <- 120 | |
| dat <- | |
| dplyr::tibble( | |
| n_seq = duration, | |
| id = seq_len(n_pts), | |
| init_x = runif(n_pts, 0, cv_size[1]), | |
| init_y = runif(n_pts, 0, cv_size[2]), | |
| vel = 6 * abs(rnorm(n_pts, 0, 3)) | |
| ) |> | |
| dplyr::reframe( | |
| fid = seq_len(n_seq), | |
| x = rasengan::bounce_off(n_seq, init_x, vel, c(0, cv_size[1])), | |
| y = rasengan::bounce_off(n_seq, init_y, vel, c(0, cv_size[2])), | |
| .by = id | |
| ) | |
| circle <- | |
| dplyr::tibble( | |
| theta = seq(0, 2 * pi, length.out = n_pts), | |
| x = 24 * cos(theta) + cv_size[1] / 2, | |
| y = 24 * sin(theta) + cv_size[2] / 2 | |
| ) | |
| imgs <- purrr::imap_chr(seq_len(duration), \(f, i) { | |
| d <- dat |> | |
| dplyr::filter(fid == f) | |
| t <- rasengan::smoothstep(f %% 16 / 16) | |
| png <- | |
| canvas("black", canvas_size = cv_size) |> | |
| add_line( | |
| dplyr::select(d, x, y) |> | |
| as.matrix(), | |
| dplyr::select(circle, x, y) |> | |
| as.matrix(), | |
| color = c("cyan", "magenta") |> | |
| rep(n_pts / 2) |> | |
| colorfast::col_to_rgb(), | |
| props = paint( | |
| !!!prps, | |
| width = 0.72, | |
| blend_mode = BlendMode$Xor, | |
| path_effect = PathEffect$dash(c(2, 1), f), | |
| ) | |
| ) |> | |
| add_circle( | |
| matrix(rep(cv_size / 2, n_pts), nrow = n_pts, byrow = TRUE), | |
| exp(seq(1, 6.4, length.out = n_pts)) + | |
| seq(1, 2 * pi, length.out = n_pts) * | |
| sin(rasengan::normalize(t, c(0, 1), c(0, pi))), | |
| props = paint( | |
| !!!prps, | |
| width = .225, | |
| style = Style$Stroke, | |
| color = "yellow", | |
| blend_mode = BlendMode$Difference, | |
| path_effect = PathEffect$dash(c(2, 1), f), | |
| ) | |
| ) |> | |
| as_png(props = paint(!!!prps)) | |
| fp <- file.path(tempdir(), sprintf("ll-%03d.png", i)) | |
| writeBin(png, fp) | |
| fp | |
| }, .progress = TRUE) | |
| gifski(imgs, gif_file = "out/minacode-24.gif", delay = 1 / 12, width = cv_size[1], height = cv_size[2]) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "27 他の人の作品を真似してみましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| library(skiagd) | |
| library(gifski) | |
| n_seq <- 180 | |
| n_points <- 320 | |
| cv_size <- c(480L, 320L) | |
| prps <- list( | |
| canvas_size = cv_size | |
| ) | |
| radii <- runif(n_points, 12, 24) | |
| color <- | |
| grDevices::hsv(seq(0, 1, length.out = n_points), 1, 1) |> | |
| colorfast::col_to_rgb() | |
| ## ここでは、以下で公開されていたシェーダをSkSLに移植したコードを使っている | |
| ## [ShingoHosoda/ascii-filter](https://github.com/ShingoHosoda/ascii-filter) | |
| sksl <- readr::read_lines("src/shaders/asciifilter.sksl") | |
| shdr <- RuntimeEffect$make(paste0(sksl, collapse = "\n")) | |
| # shdr$source() | |
| dat <- | |
| dplyr::tibble( | |
| x = rnorm(n_points, cv_size[1] / 2, cv_size[1] / 4) |> | |
| rasengan::cap(0, cv_size[1]), | |
| y = runif(n_points, -dev_size()[2], dev_size()[2] * 2) | |
| ) | |
| pngs <- purrr::imap_chr(seq_len(n_seq), \(f, i) { | |
| nz <- rasengan::noise_2d()(1:n_points, f, seed = 123) |> | |
| rasengan::normalize(from = c(-1, 1), to = c(0, 1)) |> | |
| matrix(ncol = 2) | |
| texture <- | |
| canvas("#050002", canvas_size = cv_size) |> | |
| add_circle( | |
| dat |> | |
| dplyr::mutate( | |
| x = x + (nz[, 1] - nz[, 2]) * 3, | |
| y = y - seq(0, 1, length.out = n_points + 1)[-1] * 6 * f | |
| ) |> | |
| as.matrix(), | |
| radii, | |
| color = color, | |
| props = paint( | |
| !!!prps, | |
| width = 4, | |
| blend_mode = BlendMode$Plus, | |
| ) | |
| ) |> | |
| as_png(props = paint(!!!prps)) |> | |
| aznyan::median_blur(8) |> | |
| aznyan::preserve_edge(800, 100) |> | |
| aznyan::diffusion_filter(8) | |
| img <- | |
| canvas(canvas_size = cv_size) |> | |
| add_rect( | |
| matrix(c(0, 0, cv_size), ncol = 4), | |
| props = paint( | |
| !!!prps, | |
| shader = Shader$from_png(texture, TileMode$Clamp, diag(3)), | |
| image_filter = ImageFilter$runtime_shader( | |
| shdr, | |
| uniforms = list( | |
| denominator = 54.0, | |
| iResolution = as.double(cv_size) | |
| ) | |
| ) | |
| ) | |
| ) |> | |
| as_png(props = paint(!!!prps)) | |
| fp <- file.path("public/pictures", sprintf("%04d.png", i)) | |
| writeBin(img, fp) | |
| fp | |
| }, .progress = TRUE) | |
| gifski( | |
| pngs, | |
| "out/temp.gif", | |
| width = cv_size[1], | |
| height = cv_size[2], | |
| delay = 1 / 15 | |
| ) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "28 2色だけで描いてみましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| library(skiagd) | |
| library(affiner) | |
| cv_size <- c(800L, 600L) | |
| prps <- list( | |
| canvas_size = cv_size | |
| ) | |
| n_seq <- 360 | |
| dat <- | |
| dplyr::tibble( | |
| pid = seq_len(4) | |
| ) |> | |
| dplyr::reframe( | |
| sid = seq_len(n_seq), | |
| pos = dplyr::tibble( | |
| x = e1071::rbridge(frequency = n_seq), | |
| y = e1071::rbridge(frequency = n_seq), | |
| z = e1071::rbridge(frequency = n_seq), | |
| w = 1 | |
| ) |> | |
| as.matrix(), | |
| .by = pid | |
| ) | |
| pngs <- purrr::imap_chr(seq_len(n_seq), \(k, i) { | |
| t <- 1 - rasengan::ease_in(i / n_seq, "sine") | |
| d <- dat |> | |
| dplyr::slice_head(n = k, by = pid) |> | |
| dplyr::slice_tail(n = floor(k * t + 1), by = pid) |> | |
| dplyr::mutate( | |
| pos = pos %*% | |
| transform3d() %*% | |
| scale3d(250) %*% | |
| rotate3d("z-axis", k) %*% | |
| rotate3d("x-axis", 45) %*% | |
| rotate3d("y-axis", 35) %*% | |
| translate3d(x = cv_size[1] / 2, y = cv_size[2] / 2, z = 0), | |
| r = log(rasengan::mag(pos)) * seq(.5, 4, length.out = dplyr::n()), | |
| .by = pid | |
| ) | |
| img <- | |
| canvas("snow", canvas_size = cv_size) |> | |
| purrr::reduce(unique(d$pid), \(cv, id) { | |
| cv |> | |
| add_circle( | |
| d |> | |
| dplyr::filter(pid == id) |> | |
| dplyr::pull(pos), | |
| radius = d |> | |
| dplyr::filter(pid == id) |> | |
| dplyr::pull(r), | |
| props = paint( | |
| !!!prps, | |
| style = Style$Fill, | |
| path_effect = PathEffect$discrete(3, 2, 1), | |
| blend_mode = BlendMode$Difference, | |
| color = "yellow", ## 白地にDifferenceするので補色の青っぽい色になる | |
| ) | |
| ) | |
| }, .init = _) |> | |
| as_png(props = paint(!!!prps)) | |
| fp <- file.path("public/pictures", sprintf("%04d.png", i)) | |
| writeBin(img, fp) | |
| fp | |
| }, .progress = TRUE) | |
| gifski::gifski( | |
| pngs, | |
| "out/minacode-28.gif", | |
| width = cv_size[1], | |
| height = cv_size[2], | |
| delay = 1 / 20, | |
| progress = TRUE | |
| ) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "29 思いきりカラフルにしてみましょう" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| [#minacoding - theme](https://minacoding.online/theme) | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/skiagd](https://github.com/paithiov909/skiagd) | |
| - [paithiov909/aznyan](https://github.com/paithiov909/aznyan) | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| grid <- \(ltrb, rd, resolution = 0.1, seed = 1234) { | |
| n_col <- ceiling((ltrb[3] - ltrb[1]) * resolution) | |
| n_row <- ceiling((ltrb[4] - ltrb[2]) * resolution) | |
| if (n_col * n_row > 1e5) { | |
| rlang::abort("Oops! The grid seems too large. Try a smaller resolution.") | |
| } | |
| r <- log(n_col * n_row) | |
| rd <- rd * resolution | |
| # rd <- sqrt(n_col * n_row) * resolution | |
| mat <- rasengan::noise_2d("OpenSimplex2S", fractal_type = "Rigid")( | |
| seq(0, r^(rd), length.out = n_col * n_row), | |
| rd, | |
| seed = seed | |
| ) | |
| if (anyNA(mat)) { | |
| mat[is.na(mat)] <- runif(sum(is.na(mat)), 0, 2 * pi) | |
| rlang::warn(glue::glue("Replaced NaN in the noise with random values")) | |
| } | |
| tidyr::expand_grid(x = seq_len(n_col), y = seq_len(n_row)) |> | |
| dplyr::mutate( | |
| angle = rasengan::normalize(mat, from = c(-1, 1)) |> | |
| rlang::as_function(~ rasengan::blend(0, 2 * pi, mask = 1 - .))() |> | |
| _[1:(n_col * n_row)] | |
| ) | |
| } | |
| flow_field <- \(grid, n_curves = 8) { | |
| idx <- matrix( | |
| dplyr::pull(grid, angle), | |
| nrow = max(dplyr::pull(grid, x)), | |
| ncol = max(dplyr::pull(grid, y)), | |
| byrow = TRUE | |
| ) | |
| step <- \(cx, cy, id, step_len = sample(6:20, 1)) { | |
| out_x <- rep_len(cx, step_len + 1) | |
| out_y <- rep_len(cy, step_len + 1) | |
| for (i in (seq_len(step_len) + 1)) { | |
| ix <- ceiling(cx) | |
| iy <- ceiling(cy) | |
| # print(ix); print(iy) | |
| if (any(ix < 1, ix > nrow(idx), iy < 1, iy > ncol(idx))) { | |
| out_x <- out_x[1:i] | |
| out_y <- out_y[1:i] | |
| break | |
| } | |
| out_x[i] <- cx <- out_x[i - 1] + cos(idx[ix, iy]) | |
| out_y[i] <- cy <- out_y[i - 1] + sin(idx[ix, iy]) | |
| } | |
| data.frame(idx = id, x = out_x, y = out_y) | |
| } | |
| purrr::imap(seq_len(n_curves), \(k, i) { | |
| step(runif(1, 1, nrow(idx)), runif(1, 1, ncol(idx)), i) | |
| }) |> | |
| dplyr::bind_rows() |> | |
| dplyr::as_tibble() | |
| } | |
| library(skiagd) | |
| cv_size <- c(640L, 480L) | |
| arrow <- | |
| readBin("public/yjnew-31-60x20.png", what = "raw", n = file.info("public/yjnew-31.png")$size) | |
| pngs <- purrr::imap_chr(seq_len(120), \(f, i) { | |
| gr <- grid(c(0, 0, cv_size), f, seed = 1234) | |
| rsx_trans <- gr |> | |
| dplyr::mutate( | |
| sc = .25, | |
| rot = angle, | |
| x = (cv_size[1] / max(x)) * x, | |
| y = (cv_size[2] / max(y)) * y, | |
| ax = 0, | |
| ay = 0, | |
| ) |> | |
| dplyr::select(sc, rot, x, y, ax, ay) | |
| curves <- gr |> | |
| rlang::as_function(~ withr::with_seed(1234, flow_field(., 300)))() |> | |
| dplyr::mutate( | |
| x = rasengan::normalize(x, to = c(0, cv_size[1])), | |
| y = rasengan::normalize(y, to = c(0, cv_size[2])), | |
| ) | |
| img <- | |
| canvas("snow") |> | |
| add_atlas( | |
| arrow, | |
| as.matrix(rsx_trans) | |
| ) |> | |
| add_point( | |
| curves |> | |
| dplyr::select(x, y) |> | |
| as.matrix(), | |
| group = dplyr::pull(curves, idx), | |
| color = unique(dplyr::pull(curves, idx)) |> | |
| rlang::as_function(~ . %% 16 / 16)() |> | |
| grDevices::hsv(.6, .8, alpha = .7) |> | |
| colorfast::col_to_rgb(), | |
| props = paint( | |
| point_mode = PointMode$Polygon, | |
| width = 44, | |
| cap = Cap$Round, | |
| join = Join$Round, | |
| blend_mode = BlendMode$HardLight, | |
| ) | |
| ) |> | |
| as_png() |> | |
| aznyan::median_blur(8) | |
| fp <- file.path("public/pictures", sprintf("%04d.png", i)) | |
| writeBin(img, fp) | |
| fp | |
| }, .progress = TRUE) | |
| gifski::gifski( | |
| pngs, | |
| "out/temp.gif", | |
| width = cv_size[1], | |
| height = cv_size[2], | |
| delay = 1 / 10 | |
| ) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "Rotating Arrows" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| ```{r} | |
| ## 適当に拡大してマスクの元をつくる | |
| chrs <- string2path::string2fill("➡", "src/fonts/Tsukuhou-35Point-Gothic.ttf") |> | |
| dplyr::mutate( | |
| gid = dplyr::consecutive_id(glyph_id, triangle_id), | |
| x = (x - mean(x)) * 1e3, | |
| y = (y - mean(y)) * 1e3, | |
| x = x + abs(min(x)), | |
| y = y + abs(min(y)) | |
| ) | |
| ## 文字ごとに3つ組の点をポリゴンに統合する | |
| poly_sf <- chrs |> | |
| dplyr::group_by(glyph_id, triangle_id) |> | |
| dplyr::summarize( | |
| geometry = list(sf::st_polygon(list(as.matrix( | |
| rbind(dplyr::pick(x, y), dplyr::pick(x, y)[1, ]) | |
| )))), | |
| .groups = "drop" | |
| ) |> | |
| sf::st_as_sf() | |
| ## 全体をMULTIPOLYGONに統合する | |
| poly_union <- sf::st_union(poly_sf) | |
| bbox <- sf::st_bbox(poly_union) ## bounding box | |
| ## bounding box内にランダムに点を充填し、マスクに含まれるものだけを残す | |
| pts <- | |
| poissoned::poisson2d( | |
| w = ceiling(bbox$xmax), | |
| h = ceiling(bbox$ymax) | |
| ) |> | |
| rlang::as_function(~ { | |
| is_inside <- sf::st_as_sf(., coords = c("x", "y"), crs = NA) |> | |
| sf::st_within(poly_union, sparse = FALSE) | |
| dplyr::filter(., is_inside[, 1]) | |
| })() |> | |
| dplyr::slice_sample(n = 1e3) | |
| library(skiagd) | |
| library(affiner) | |
| use("rasengan", "%!*%") | |
| cv_size <- dev_size() | |
| arrow <- | |
| canvas("transparent", c(24L, 24L)) |> | |
| add_text( | |
| "→", | |
| matrix(c(1, 0, 8, 8, 0, 0), ncol = 6), | |
| props = paint( | |
| color = "snow", | |
| family = "IPAexGothic", | |
| canvas_size = c(24L, 24L) | |
| ) | |
| ) |> | |
| as_png() | |
| canvas() |> | |
| add_png(arrow) |> | |
| draw_img() | |
| ragg::agg_webp_anim( | |
| "arrow-rotate.webp", | |
| width = cv_size[1], | |
| height = cv_size[2], | |
| delay = 1 / 15 | |
| ) | |
| for (i in seq_len(90)) { | |
| pos <- (pts |> | |
| dplyr::mutate( | |
| x = rasengan::normalize(x, to = c(-1, 1)), | |
| y = rasengan::normalize(y, to = c(-1, 1)), | |
| z = 1, | |
| w = 1 | |
| ) |> | |
| as.matrix()) %*% | |
| transform3d() %*% | |
| reflect3d("yz-plane") %*% | |
| rotate3d("z-axis", theta = angle(pi / 6 * i, unit = "radians")) %*% | |
| rasengan::lookat3d(c(.1, .1, 6), c(0, 0, 1)) %*% | |
| rasengan::persp3d(pi / 4, 720 / 576) %!*% | |
| rasengan::viewport3d(cv_size[1], cv_size[2]) | |
| canvas("darkgreen") |> | |
| add_atlas( | |
| arrow, | |
| dplyr::tibble( | |
| sc = 1, | |
| rot = rasengan::deg2rad(sample.int(360, size = 1000, replace = TRUE)), | |
| x = pos[, 1], | |
| y = pos[, 2], | |
| ax = 12, | |
| ay = 0 | |
| ) |> as.matrix(), | |
| props = paint( | |
| color = "gray90", | |
| width = 4, | |
| ) | |
| ) |> | |
| draw_img() | |
| } | |
| dev.off() | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "Bubble Universe" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| - [もとのつぶやきProcessing](https://x.com/yuruyurau/status/1226846058728177665) | |
| - [naraを使ったRコード](https://github.com/coolbutuseless/narademos/blob/main/bubble-universe.R) | |
| ### skiagd | |
| ```{r} | |
| #| label: bubble-univ | |
| W <- 540L | |
| N <- W / 2 - 40 | |
| tau <- 2 * pi | |
| mirai::daemons(6) | |
| library(skiagd) ## for `%timer%` | |
| print(time) %timer% | |
| gifski::gifski( | |
| purrr::imap_chr(seq(0, 6, by = 0.02), purrr::in_parallel( | |
| \(t, frame) { | |
| library(skiagd) | |
| library(affiner) | |
| dat <- | |
| tidyr::expand_grid(i = seq(N), j = seq(N)) |> | |
| dplyr::group_by(i) |> | |
| dplyr::group_modify(~ { | |
| x <- y <- 0 | |
| xs <- ys <- double(N) | |
| for (j in .x$j) { | |
| u <- sin(.y$i + y) + sin(r * .y$i + x) | |
| v <- cos(.y$i + y) + cos(r * .y$i + x) | |
| x <- u + t | |
| y <- v | |
| xs[j] <- u | |
| ys[j] <- y | |
| } | |
| data.frame( | |
| x = xs, | |
| y = ys, | |
| z = 1, | |
| col = rgb(.y$i, .x$j, (y + 2) * N / 4, maxColorValue = N) | |
| ) | |
| }) |> | |
| dplyr::ungroup() | |
| img <- | |
| canvas("black", c(W, W)) |> | |
| add_point( | |
| dat |> | |
| dplyr::select(x, y, z) |> | |
| as.matrix() %*% | |
| transform2d() %*% | |
| scale2d(N / 2) %*% | |
| translate2d(W / 2, W / 2), | |
| group = seq_len(nrow(dat)), | |
| color = dplyr::pull(dat, col) |> | |
| colorfast::col_to_rgb(), | |
| props = paint( | |
| width = 3, | |
| blend_mode = BlendMode$Plus, | |
| canvas_size = c(W, W) | |
| ) | |
| ) |> | |
| as_png(props = paint(canvas_size = c(W, W))) | |
| fp <- file.path("public/pictures", sprintf("%04d.png", frame)) | |
| writeBin(img, fp) | |
| fp | |
| }, | |
| W = W, | |
| N = N, | |
| r = tau / N | |
| ), .progress = TRUE), | |
| width = W, | |
| height = W, | |
| delay = 1 / 30 | |
| ) | |
| ``` | |
| ### naraを使っているコード | |
| ```{r} | |
| library(nara) | |
| W <- 400 | |
| N <- W / 2 - 40 | |
| tau <- 2 * pi | |
| print(time) %timer% | |
| gifski::save_gif( | |
| purrr::walk(seq(0, 3, by = 0.02), \(t) { | |
| nr <- nr_new(W, W, "black") | |
| x <- 0 | |
| y <- 0 | |
| r <- tau / N | |
| # nr_fill(nr, "black") | |
| xs <- double(N) | |
| ys <- double(N) | |
| for (i in seq(N)) { | |
| for (j in seq(N)) { | |
| u <- sin(i + y) + sin(r * i + x) | |
| v <- cos(i + y) + cos(r * i + x) | |
| x <- u + t | |
| y <- v | |
| xs[j] <- u | |
| ys[j] <- y | |
| } | |
| col <- rgb(i, seq(N), (ys + 2) * N / 4, maxColorValue = N) | |
| nr_point(nr, xs * N / 2 + W / 2, ys * N / 2 + W / 2, color = col) | |
| } | |
| grid::grid.newpage() | |
| grid::grid.raster(nr, interpolate = TRUE) | |
| }, .progress = TRUE), | |
| width = W, | |
| height = W, | |
| delay = 1 / 30 | |
| ) | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "GeoZoo torus" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| ```{r} | |
| #| label: torus | |
| library(skiagd) # https://github.com/paithiov909/skiagd | |
| library(affiner) | |
| library(tibble) | |
| # 4:3 | |
| ragg::agg_png("test.png", width = 720, height = 480) | |
| size <- dev_size() | |
| n_points <- 548 | |
| pts <- | |
| geozoo::torus(n = n_points)$points |> | |
| prcomp() | |
| # 15 bases * (1 seconds / 25 fps)*4 frames * n_points | |
| anim <- | |
| tourr::render_anim( | |
| pts$x, | |
| frames = tourr::save_history( | |
| pts$x, | |
| max_bases = 15 | |
| ) |> | |
| tourr::interpolate(angle = .04) | |
| ) | |
| n_frames <- | |
| factor(anim$frames$frame) |> | |
| nlevels() | |
| {tm <<- time} %timer% purrr::walk(seq_len(n_frames), \(i) { | |
| tmp <- | |
| canvas("#b11b12") |> | |
| add_text( | |
| rep_len("蟹", n_points), | |
| point = anim$frames |> | |
| dplyr::filter(dplyr::consecutive_id(frame) == i) |> | |
| dplyr::mutate(d = 1) |> | |
| dplyr::select(P1, P2, d) |> | |
| as.matrix() %*% | |
| transform2d() %*% | |
| # rotate2d(pi / 2 * 4) %*% | |
| scale2d(170) %*% translate2d(size[1] / 2, size[2] / 2), | |
| props = paint( | |
| color = "snow", | |
| fontsize = 14, | |
| fontfamily = "IPAexMincho", | |
| fontface = FontStyle$Bold, | |
| blend_mode = BlendMode$Overlay, | |
| ) | |
| ) | |
| tmp |> | |
| as_png() |> | |
| writeBin( | |
| paste0( | |
| "public/pictures/", sprintf("%04d", i), ".png" | |
| ) | |
| ) | |
| }, .progress = TRUE) | |
| tm | |
| ``` | |
| ```r | |
| pts <- geozoo::sphere.hollow(3, n = 300)$points |> | |
| prcomp() | |
| pts <- geozoo::conic.spiral(n = 500, a = 1.6, b = 5)$points |> | |
| prcomp() | |
| ``` | |
| ## References | |
| - [3 Dimension reduction overview – Interactively exploring high-dimensional data and models in R](https://dicook.github.io/mulgar_book/3-intro-dimred.html) | |
| - [Function reference • tourr](https://ggobi.github.io/tourr/reference/index.html) | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "Colorful Helix" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| ```{r} | |
| library(ggplot2) | |
| use("rasengan", "%!*%") # https://github.com/paithiov909/rasengan | |
| ragg::agg_webp_anim( | |
| "test-animated-spiral.webp", | |
| width = 320, | |
| height = 180, | |
| delay = 1 / 12 | |
| ) | |
| s <- seq(-4 * pi, 4 * pi, length.out = 500) | |
| for (frame in seq(0, 4 * pi, length.out = 60)) { | |
| helix <- | |
| dplyr::tibble( | |
| m = dplyr::tibble( | |
| x = cos(s - frame), | |
| y = sin(s - frame), | |
| z = s, | |
| w = 1 | |
| ) |> | |
| as.matrix() | |
| ) |> | |
| dplyr::mutate( | |
| m = m %*% | |
| rasengan::lookat3d(c(3, 3, 18), c(0, 0, 0)) %*% | |
| rasengan::persp3d(pi / 4, 16 / 9) %!*% | |
| rasengan::viewport3d(320, 180) | |
| ) | |
| gp <- | |
| ggplot(helix) + | |
| geom_point( | |
| aes(x = m[, 1], y = m[, 2], colour = m[, 3], size = 5.25 * rasengan::normalize(m[, 3])), | |
| alpha = .6 | |
| ) + | |
| theme_void() + | |
| theme( | |
| plot.background = element_rect(fill = "gray90"), | |
| panel.background = element_rect(fill = "gray90"), | |
| plot.margin = margin(0, 0, 0, 0), | |
| legend.position = "none" | |
| ) + | |
| scale_size_identity() + | |
| scale_color_viridis_c(option = "plasma") + | |
| coord_cartesian( | |
| xlim = c(0, 320), | |
| ylim = c(0, 180) | |
| ) | |
| print(gp) | |
| } | |
| dev.off() | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "Kaleidoscope" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| ```{r} | |
| #| label: save-images | |
| library(skiagd) | |
| library(affiner) | |
| library(dee) # https://github.com/trevorld/dee | |
| s <- 0.6 | |
| ro <- 50 | |
| ri <- s * ro | |
| d <- d_circle(0, 0, c(ro, ri, ri - 3)) + d_star(0, 0, ro, s, 8, digits = 0) | |
| # print(d) | |
| cv_size <- c(640L, 360L) | |
| n_dee <- 48 | |
| sprl <- | |
| rasengan::curve_archimedean(n_dee, a = 3, c = sqrt(2)) |> | |
| dplyr::mutate( | |
| x = rasengan::normalize(x, to = c(-1, 1)), | |
| y = rasengan::normalize(y, to = c(-1, 1)), | |
| z = 1 | |
| ) |> | |
| as.matrix() | |
| ragg::agg_png( | |
| "public/pictures/%04d.png", | |
| width = cv_size[1], | |
| height = cv_size[2] | |
| ) | |
| dev_size() | |
| fps <- 25 | |
| duration <- 20 * fps | |
| purrr::walk(seq_len(duration), \(frame) { | |
| rot <- | |
| rasengan::blend(0, 2 * pi, frame / duration) |> | |
| rasengan::rad2deg() | |
| sc <- | |
| (cos(frame / 20) * .5 + .5) |> | |
| rasengan::blend(60, 240, mask = _) | |
| trans <- | |
| transform2d() %*% | |
| rotate2d(rot) %*% | |
| scale2d(sc) %*% | |
| translate2d(cv_size[1] / 2, cv_size[2] / 2) | |
| dat <- sprl %*% trans | |
| canvas("gray10") |> | |
| add_path( | |
| rep_len(d, n_dee), | |
| rsx_trans = dplyr::tibble( | |
| sc = seq(1.2, .4, length.out = n_dee), | |
| rot = 0, | |
| x = dat[, 1], | |
| y = dat[, 2], | |
| ax = 0, | |
| ay = 0 | |
| ) |> | |
| as.matrix(), | |
| color = seq(1, 0, length.out = n_dee) |> | |
| rasengan::shift(frame) |> | |
| hsv(s = .8, v = 1) |> | |
| colorfast::col_to_rgb(), | |
| sigma = seq(5, 10, length.out = n_dee), | |
| props = paint( | |
| blend_mode = BlendMode$Plus, | |
| fill_type = FillType$EvenOdd, | |
| ) | |
| ) |> | |
| draw_img() | |
| }, .progress = TRUE) | |
| dev.off() | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "Rotating Attractor" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| The following R packages are used. These packages are currently under development, so these codes may not work due to API changes. | |
| - [paithiov909/rasengan](https://github.com/paithiov909/rasengan) | |
| ```{r} | |
| #| label: lorenz-attractor | |
| lorenz_eq <- \(t, y, params, ...) { | |
| with(params, { | |
| dy1 <- sigma * (y[2] - y[1]) | |
| dy2 <- rho * y[1] - y[2] - y[1] * y[3] | |
| dy3 <- y[1] * y[2] - beta * y[3] | |
| list(c(dy1, dy2, dy3)) | |
| }) | |
| } | |
| params <- list(sigma = 10, beta = 8 / 3, rho = 28) | |
| init_state <- c(x = -2.29209, y = 0.098299, z = 24.50526) | |
| attractor <- | |
| deSolve::ode(init_state, seq(0, 100, 0.01), lorenz_eq, params) | |
| dat <- | |
| dplyr::tibble( | |
| x = attractor[, 2], | |
| y = attractor[, 3], | |
| z = attractor[, 4], | |
| w = 1 | |
| ) |> | |
| as.matrix() | |
| s <- seq(-pi, pi, length.out = 100) | |
| v <- | |
| dplyr::tibble( | |
| x = 1, | |
| y = 25 * cos(s), | |
| z = 25 * sin(s) | |
| ) |> | |
| as.matrix() | |
| ``` | |
| ```{r} | |
| #| label: render-gif | |
| mirai::daemons(2) | |
| gifski::gifski( | |
| purrr::map_chr(seq_len(nrow(v)), purrr::in_parallel(\(view) { | |
| library(ggplot2) | |
| use("rasengan", "%!*%") | |
| theta <- rasengan::blend(-pi, pi, view / nrow(v)) | |
| up <- if (cos(theta) != 0) c(cos(theta), -cos(theta), 0) else c(0, 0, -1) | |
| xfm <- | |
| affiner::transform3d() %*% | |
| affiner::rotate3d(theta = affiner::as_angle(theta, "rad")) %*% | |
| rasengan::lookat3d(v[view, ], c(.3, .3, .3), up) | |
| mat <- (dat %*% xfm %!*% rasengan::viewport3d(640, 360)) |> | |
| dplyr::as_tibble(.name_repair = ~ c("x", "y", "z", "w")) |> | |
| dplyr::mutate( | |
| x2 = dplyr::lag(x, default = dplyr::last(x)), | |
| y2 = dplyr::lag(y, default = dplyr::last(y)) | |
| ) | |
| gp <- | |
| ggplot(mat) + | |
| geom_segment( | |
| aes(x = x, y = y, xend = x2, yend = y2), | |
| colour = "snow", | |
| linewidth = 0.24, | |
| alpha = .6 | |
| ) + | |
| theme_void() + | |
| theme( | |
| plot.background = element_rect(fill = "gray20"), | |
| panel.background = element_rect(fill = "gray20"), | |
| plot.margin = margin(0, 0, 0, 0) | |
| ) + | |
| coord_cartesian(xlim = c(0, 640), ylim = c(0, 360)) | |
| fp <- file.path(tmp, sprintf("%03d.png", view)) | |
| ggsave(fp, gp, width = 640, height = 360, units = "px") | |
| fp | |
| }, v = v, dat = dat, tmp = tempdir()), .progress = TRUE), | |
| gif_file = "lorenz.gif", | |
| width = 640, | |
| height = 360, | |
| delay = 1 / 20, | |
| progress = TRUE | |
| ) | |
| mirai::daemons(0) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "Spherical Harmonics" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Code | |
| ```{r} | |
| library(skiagd) | |
| use("rasengan", "%!*%") | |
| # Copied from: | |
| # * https://github.com/cran/cooltools/blob/db35d06cd8933b8bef83400221887bc6fb3a60eb/R/sphericalharmonics.R | |
| # * https://github.com/cran/cooltools/blob/db35d06cd8933b8bef83400221887bc6fb3a60eb/R/unitvector.R | |
| source("docs/sphericalharmonics.R") | |
| N <- 40 | |
| W <- 640L | |
| H <- 480L | |
| # some kind of dithering effect | |
| sksl <- readLines("src/shaders/dither-alpha.sksl") | |
| effect <- RuntimeEffect$make(paste0(sksl, collapse = "\n")) | |
| n_frames <- 360 | |
| imgs <- | |
| purrr::imap_chr(seq_len(n_frames), \(t, i) { | |
| tau <- 2 * pi | |
| f <- t %% 40 / 40 | |
| g <- rasengan::fract(cos(f * tau)) | |
| theta <- | |
| rep(seq(0, tau, length.out = N) + | |
| rasengan::blend(-tau, tau, f), each = N) | |
| phi <- rep(seq(0, pi, length.out = N), times = N) | |
| l <- 8 | |
| m <- 2 | |
| Y <- sphericalharmonics(l, m, matrix(cbind(theta, phi), ncol = 2)) | |
| r <- (1.2 + 0.3 * cos(t / n_frames * tau)) * rasengan::normalize(Y, to = c(-1, 1)) | |
| points <- | |
| dplyr::tibble( | |
| x = r * sin(phi) * cos(theta), | |
| y = r * sin(phi) * sin(theta), | |
| z = r * cos(phi), | |
| w = 1 | |
| ) | |
| texture <- | |
| canvas("transparent", canvas_size = c(W, H)) |> | |
| add_point( | |
| as.matrix(points) %*% | |
| rasengan::lookat3d(eye = c(3 * cos(f * tau), -3, 3 * sin(f * tau)), center = c(.11, 2, .11)) %*% | |
| rasengan::persp3d(fovy = pi / 2.8, aspect = W / H) %!*% | |
| rasengan::viewport3d(W, H, 0, 40), | |
| group = seq_len(nrow(points)), | |
| color = rasengan::normalize(Y) |> | |
| rasengan::shift(40 * sin(f)) |> | |
| grDevices::hsv(.66, .88, 1) |> | |
| colorfast::col_to_rgb(), | |
| props = paint( | |
| canvas_size = c(W, H), | |
| width = 5, | |
| sigma = 2 + 3 * g, | |
| blur_style = BlurStyle$Solid, | |
| blend_mode = BlendMode$Plus, | |
| ) | |
| ) | |
| png <- | |
| canvas("gray20", canvas_size = c(W, H)) |> | |
| add_rect( | |
| matrix(c(0, 0, W, H), ncol = 4), | |
| props = paint( | |
| canvas_size = c(W, H), | |
| shader = Shader$from_picture(texture, TileMode$Clamp, c(W, H), diag(3)), | |
| image_filter = | |
| ImageFilter$runtime_shader( | |
| effect, | |
| list(c = 1 + 12 * g, iResolution = as.double(c(W, H))) | |
| ), | |
| ) | |
| ) |> | |
| as_png(props = paint(canvas_size = c(W, H))) | |
| fp <- sprintf("public/pictures/%04d.png", i) | |
| writeBin(png, fp) | |
| fp | |
| }, | |
| .progress = TRUE | |
| ) | |
| ``` | |
| ```{r} | |
| gifski::gifski(imgs, "out/sphere.gif", width = W, height = H, delay = 1 / 30) | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "Spiral Dots" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| ```{r} | |
| #| label: save-gif | |
| library(skiagd) # https://github.com/paithiov909/skiagd | |
| library(gifski) | |
| ## [R:グラフィックス](https://sakas.w.waseda.jp/R/Rgraphics17.html) を元に作成 | |
| ## `A`は回転行列。前のステップからすこしずつ回しながら`r`でスケールして点を生成する関数をつくっている | |
| ## 回す角度は黄金角だが、フィボナッチ数列とはたぶん関係ない | |
| dots <- \(r = .998, theta = (1 + sqrt(5)) / 2) { | |
| function(n = 500, trans = c(0, 0)) { | |
| A <- | |
| matrix( | |
| c(r * cos(theta), -r * sin(theta), r * sin(theta), r * cos(theta)), | |
| ncol = 2 | |
| ) | |
| x <- y <- rep(1, n + 1) | |
| for (i in 2:(n + 1)) { | |
| x[i] <- x[i - 1] * A[1, 1] + y[i - 1] * A[1, 2] + trans[1] | |
| y[i] <- x[i - 1] * A[2, 1] + y[i - 1] * A[2, 2] + trans[2] | |
| } | |
| data.frame(x = x[-1], y = y[-1]) | |
| } | |
| } | |
| ## test | |
| # with(dots(r = cos(0.099))(n = 500), plot(x, y)) | |
| size <- dev_size() | |
| size | |
| center <- size / 2 | |
| n_dots <- 360 | |
| radii <- seq(0, 1, length.out = n_dots) |> | |
| ambient::gen_simplex(seed = n_dots) |> | |
| ambient::normalize(to = c(2, 6)) | |
| save_gif(lapply(seq(0, 1, length.out = 300), \(t) { | |
| theta <- t * 2 * pi | |
| trans <- | |
| matrix( | |
| c( | |
| 200 * cos(theta), 200 * sin(theta), center[1], | |
| 200 * -1 * sin(theta), 200 * cos(theta), center[2], | |
| 0, 0, 1 | |
| ), | |
| ncol = 3 | |
| ) | |
| j <- tweenr::tween_at(0, pi / 2, t, ease = "circular-in-out") | |
| d <- dots(r = cos(j))(n_dots) | |
| canvas("#04010F") |> | |
| add_circle( | |
| d |> | |
| cbind(z = 1) |> | |
| as.matrix() %*% trans, | |
| radius = radii, | |
| color = seq(0, 1, length.out = nrow(d)) |> | |
| grDevices::hsv(0.66, 1 - t, alpha = 1) |> | |
| grDevices::col2rgb(alpha = TRUE), | |
| props = paint(style = Style$Fill, blend_mode = BlendMode$Plus) | |
| ) |> | |
| draw_img() | |
| }), delay = 1 / 60, width = size[1], height = size[2], gif_file = "dots.gif") | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| title: "Vogel Spiral" | |
| format: gfm | |
| fig-width: 8 | |
| fig-height: 6 | |
| knitr: | |
| opts_chunk: | |
| dev: "ragg_png" | |
| collapse: true | |
| comment: "#>" | |
| out.width: "100%" | |
| --- | |
| ## Codes | |
| ```{r} | |
| #| label: setup | |
| library(skiagd) # https://github.com/paithiov909/skiagd | |
| library(gifski) | |
| spiral <- \(n = 500, scale = 1, theta = (1 + sqrt(5)) / 2) { | |
| n <- seq_len(n) | |
| data.frame( | |
| x = scale * sqrt(n) * cos(theta * n), | |
| y = scale * sqrt(n) * sin(theta * n) | |
| ) | |
| } | |
| # size <- dev_size() | |
| size <- c(360, 360) | |
| center <- size / 2 | |
| n_dots <- 360 # 260 | |
| trans <- | |
| matrix( | |
| c( | |
| 10, 0, center[1], | |
| 0, 10, center[2], | |
| 0, 0, 1 | |
| ), | |
| ncol = 3 | |
| ) | |
| ## test | |
| with(spiral(n = 500, theta = log(0.1)), plot(x, y)) | |
| ``` | |
| ```{r} | |
| #| label: dots | |
| ## (0, exp(1)] | |
| save_gif(lapply(seq(0, 1 * pi, length.out = 360 + 1)[-1], \(j) { | |
| d <- | |
| spiral(n = n_dots, theta = log(j)) |> | |
| cbind(z = 1) | |
| t <- ambient::normalize(j, from = c(0, 1 * pi)) | |
| col <- | |
| grDevices::hsv(t, .8, .2, alpha = 1) |> | |
| grDevices::col2rgb(alpha = TRUE) | |
| canvas("#04010F") |> | |
| add_circle( | |
| d |> | |
| as.matrix() %*% trans, | |
| radius = seq(1, 2, length.out = nrow(d)) * (1 + t), | |
| color = seq(0, 1, length.out = nrow(d)) |> | |
| grDevices::hsv(0.66, 1, alpha = 1) |> | |
| grDevices::col2rgb(alpha = TRUE), | |
| props = paint( | |
| style = Style$Fill, | |
| ) | |
| ) |> | |
| add_rect( | |
| matrix( | |
| c(0, 0, size), | |
| ncol = 4 | |
| ), | |
| props = paint( | |
| shader = Shader$color(col), | |
| blend_mode = BlendMode$Screen, | |
| ) | |
| ) |> | |
| draw_img() | |
| }), delay = 1 / 15, width = size[1], height = size[2], gif_file = "spiral-1.gif") | |
| ``` | |
| ```{r} | |
| #| label: lines | |
| save_gif(lapply(seq(0, 1 * pi, length.out = 360 + 1)[-1], \(j) { | |
| d <- | |
| spiral(n = n_dots, theta = log(j)) |> | |
| cbind(z = 1) | |
| t <- ambient::normalize(j, from = c(0, 1 * pi)) | |
| col <- | |
| grDevices::hsv(t, .8, .2, alpha = 1) |> | |
| grDevices::col2rgb(alpha = TRUE) | |
| canvas("#04010F") |> | |
| add_line( | |
| d |> | |
| dplyr::slice_head(n = -1) |> | |
| as.matrix() %*% trans, | |
| d |> | |
| dplyr::slice_tail(n = -1) |> | |
| as.matrix() %*% trans, | |
| color = seq(0, 1, length.out = nrow(d) - 1) |> | |
| grDevices::hsv(0.66, 1, alpha = 1) |> | |
| grDevices::col2rgb(alpha = TRUE), | |
| props = paint( | |
| width = 1.2, | |
| style = Style$Fill, | |
| ) | |
| ) |> | |
| add_rect( | |
| matrix( | |
| c(0, 0, size), | |
| ncol = 4 | |
| ), | |
| props = paint( | |
| shader = Shader$color(col), | |
| blend_mode = BlendMode$Screen, | |
| ) | |
| ) |> | |
| draw_img() | |
| }), delay = 1 / 15, width = size[1], height = size[2], gif_file = "spiral-2.gif") | |
| ``` | |
| ## License | |
| The codes in this file are licensed under the WTFPL v2. | |
| ``` | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| Version 2, December 2004 | |
| Copyright (C) 2025 paithiov909 | |
| Everyone is permitted to copy and distribute verbatim or modified | |
| copies of this license document, and changing it is allowed as long | |
| as the name is changed. | |
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | |
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
| 0. You just DO WHAT THE FUCK YOU WANT TO. | |
| ``` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment