返回

文章详情

线性余弦调色板(2025)

Hacker News2026年6月5日 03:40

所以,回顾我在这个博客上的历史,我注意到我,嗯嗯嗯,倾向于写长篇文章。这是我非常清楚的一个性格缺陷。当我想理解某件事情时,我感到有一种心理上的强迫,想要深入到黑暗中,尽可能多地探讨细节,围绕这些细节组织我的思考,然后将这整个该死的混乱拖入阳光下,让我那些长久忍受的读者可以在恐怖中审视我的内心世界的丑陋。我知道这可能并不明智。反思这一点作为个人的弱点,我在这个美好的星期天给自己设定了一个挑战:我是否真的能写一篇简单的博客文章?就像,对于一个平庸的家伙来说,是否真的可以写一篇短小的该死的文章而不把它变成某种可怕的专著?鉴于我过去的表现,显然我不太可能有这种自我克制的能力。让我们看看我能否做到这一点?线性余弦调色板 动机来自于Mike Cheng在Mastodon上的一篇帖子,提出了一种在R语言中随机生成连续颜色调色板的简单方法。这个创意来源于Inigo Quilez的一篇博客文章,讨论的是简单的过程调色板,这个想法痛苦到极点的简单。假设我们有长度为3的向量{{a}、{b}、{c}、而 o {d},表示从中生成连续调色板的四种“基础”颜色。在R中,我们可以使用colors()函数选择这些基础颜色。选择好这些基础颜色后,我们可以使用以下函数定义光滑的调色板: f(t) = {a} + {b} imes { ext{cos}}ig(2 imes { ext{pi}}ig({c} imes t + {d}ig)ig) 其中t的取值范围从0到1。这种调色板规则的好处是它的速度非常快,特别是因为——那些懂这一点的人告诉我——现代CPU和GPU中有许多优化可以加速余弦评估。诚然,我在我的生成艺术工作中并不太在意速度,因为调色板生成根本没有接近我的代码瓶颈,而且我也很懒。好吧,这里有一个R函数,它在Mike的Inigo Quilez的余弦调色板实现上实现了一个非常小的调整: cosine_palette <- function(n, base = NULL, seed = NULL) { if ( ! is.null(seed)) set.seed(seed) if (is.null(base)) base <- colors(distinct = TRUE) a <- c(0.5, 0.5, 0.5) b <- (sample(base, 1) |> col2rgb() |> as.vector()) / 255 c <- (sample(base, 1) |> col2rgb() |> as.vector()) / 255 d <- (sample(base, 1) |> col2rgb() |> as.vector()) / 255 pal <- vapply(seq(0, 1, length.out = n), function(t) a + b * cos(2 * pi * (c * t + d)), double(3)) pal[pal > 1] <- 1 rgb(t(abs(pal))) } cosine_palette(n = 16, seed = 11) [1] "#7F1616" "#6F1A17" "#362A20" "#22442F" "#8A6642" "#F18A5A" "#FFAD74" [8] "#FFCB8F" "#FFE0A9" "#FFE9C0" "#FFE6D3" "#AAD6E1" "#40BDE8" "#1F9CE9" [15] "#6377E3" "#7F54D6" 这很好,但由于我的视觉皮层并未针对十六进制RGB颜色代码的解读进行优化,我发现使用……看看记录……图像来显示调色板非常方便?是的。是的,听起来对。为此,我将使用这个shade_strip(),我有时用它来以条带的形式显示连续变换的调色板: shade_strip <- function(cols) { withr :: with_par(list(mar = c(0, 0, 0, 0)), image(matrix(seq_along(cols), ncol = 1), col = cols, axes = FALSE)) } seeds <- 11:22 seeds |> purrr::map({s} cosine_palette(n = 256, seed = s)) |> purrr::walk(shade_strip) 我想了解这些调色板在生成艺术系统中应用时的表现,所以我选择了12个连续的种子。这个序列从seed = 11开始,因为我恰好喜欢用那个调色板生成的第一件作品,除此之外我没有尝试去“破解”种子以偏向输出。 应用于生成艺术 为了感受这些调色板在生成艺术中使用时的表现,下面是一些使用它们创建的作品。这些作品是使用我几年前在代码艺术工作坊上写的subdivision()系统创建的。 subdivision()的代码 choose_rectangle <- function(blocks) { sample(nrow(blocks), 1, prob = blocks$area) } choose_break <- function(lower, upper) { round((upper - lower) * runif(1)) } create_rectangles <- function(left, right, bottom, top, value) { tibble::tibble(left = left, right = right, bottom = bottom, top = top, width = right - left, height = top - bottom, area = width * height, value = value)} split_rectangle_x <- function(rectangle, new_value) { with(rectangle, { split <- choose_break(left, right) new_left <- c(left, left + split) new_right <- c(left + split, right) new_value <- c(value, new_value) create_rectangles(new_left, new_right, bottom, top, new_value) }) } split_rectangle_y <- function(rectangle, new_value) { with(rectangle, { split <- choose_break(bottom, top) new_bottom <- c(bottom, bottom + split) new_top <- c(bottom + split, top) new_value <- c(value, new_value) create_rectangles(left, right, new_bottom, new_top, new_value) }) }

赞助内容

NordVPN Next-gen Antivirus

本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。

请我喝杯咖啡