Skip to content

Instantly share code, notes, and snippets.

@paithiov909
Last active June 7, 2025 18:41
Show Gist options
  • Save paithiov909/b1c12083b64ccee1a84964588bc8cfeb to your computer and use it in GitHub Desktop.
Save paithiov909/b1c12083b64ccee1a84964588bc8cfeb to your computer and use it in GitHub Desktop.
Rtistry🎨
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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