Skip to content

Instantly share code, notes, and snippets.

@paithiov909
Last active November 26, 2025 11:17
Show Gist options
  • Select an option

  • Save paithiov909/b1c12083b64ccee1a84964588bc8cfeb to your computer and use it in GitHub Desktop.

Select an option

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)
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.
```
---
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.
```
---
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)
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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.
```
---
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
)
```
---
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: "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.
```
---
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.
```
---
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.
```
---
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.
```
---
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