Last active
June 7, 2025 18:41
-
-
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) | |
```{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) | |
たぶん、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) | |
- [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) | |
```{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) | |
```{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) | |
```{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: "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: "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