在这篇文章中,作者尝试撰写一篇简明扼要的短文,以改掉以往产出过于冗长内容的习惯。文章重点介绍了如何在 R 语言中实现“线性余弦调色板”(linear cosine palettes),这是一种用于生成连续色板的简便程序化方法。
该技术最初由 Inigo Quilez 提出,并由 Mike Cheng 引入 R 语言。它利用余弦函数 $f(t) = \mathbf{a} + \mathbf{b} \ \cos(2 \pi(\mathbf{c} t + \mathbf{d}))$,通过随机选择的基向量来创建平滑且美观的色彩过渡。作者提供了一个紧凑的 R 函数来实现该方法,并展示了这些调色板以条带形式呈现的效果。
为了测试这些调色板的实际应用,作者将其应用于两种生成艺术系统:矩形细分法和利萨茹曲线生成器。尽管几乎没有进行优化,但这些调色板仍产生了高质量的效果。作者最后总结称,该方法非常高效,且最重要的是,成功完成了撰写一篇简明、专注的博文这一练习。
Sorry.
相关文章
原文
So. Looking back at my history on this blog I have noticed that I, ummmmmm, tend to write long posts. It is a character flaw of which I am acutely aware. When I want to understand a thing I feel a kind of psychological compulsion to delve too deeply into the darkness, dive into to as many of the specifics as I possibly can, organise my thoughts around those specifics, and then drag the whole cursed mess into the daylight so that my long-suffering readers can look on in horror at the grotesquerie of my inner world.
I am aware that this is perhaps unwise.
Reflecting on this as a personal weakness, I have set myself a challenge this fine Sunday: is it even possible for me to write a simple blog post? Like, is it even possible for a mediocre bitch to write a short goddamn article without turning it into some macabre monograph? Given my past form, it is not at all obvious that I’m capable of this level of self-restraint. Let’s see if I can do it?
Linear cosine palettes
The motivation came from this mastodon post by Mike Cheng proposing a simple method for randomly generating continuous colour palettes in R. The original idea comes from a blog post by Inigo Quilez on simple procedural palettes, and the idea is painfully simple. Let’s say we have length-3 vectors \(\mathbf{a}\), \(\mathbf{b}\), \(\mathbf{c}\), and \(\mathbf{d}\) representing four “base” colours from which a continous palette is to be generated. In R we could choose these base colours using the colors() function. Once these are selected we can define a smooth palette using the following function
where \(t\) is varied from 0 to 1. The nice thing about this paletting rule is that it can be very fast, especially since – I am told by people who understand such things – there are a lot of optimisations in modern CPUs and GPUs to make cosine evaluation fast. Admittedly, speed is not something I care about much in my generative art work because palette generation is not even close to being a bottleneck in my code and also I’m lazy.
Okay, so here’s an R function that implements a very minor tweak on Mike’s implementation of Inigo Quilez’ cosine palettes:
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] <-1rgb(t(abs(pal)))}cosine_palette(n =16, seed =11)
That’s nice, but as my visual cortex is not optimised for the interpretation of hexadecimal RGB colour codes, I find it convenient to show palettes using… check notes… images? Yes. Yes, that sounds right. To that end I’ll use this shade_strip() that I sometimes use to display a continuously varying palette as a strip:
I wanted to get a sense of how well these palettes might behave if applied in a generative art system, so I chose 12 sequential seeds. The sequence starts at seed = 11 because I happened to like the first piece that was generated using that palette, but apart from that minor intervention I haven’t tried to “hack” the seed to bias the outputs.
Application to generative art
To get a feel for how these palettes behave when used in generative art, here are some pieces created using them. These pieces are created using the subdivision() system that I wrote about as part of the art from code workshop I gave a few years ago.
Not too bad at all. Some of the pieces are pretty awful, a few of them are lovely, and most are okay. Given that I’ve made no attempt at all to optimise the way that palette aligns with the structure of the pieces, that’s not a bad outcome at all.
As a second example, here’s a series of pieces based on the lissajous system, all using the same palettes:
seeds |> purrr::walk(\(s) lissajous(seed = s))
Also pretty tolerable. In any specific application I’d probably want to tinker a bit and adapt to the specific aesthetic that the system is targeting, but I’m not displeased at all. For something so simple it works better than I expected. Okay, all good. We’re done now. Post completed, nothing else to add. Somehow I have managed to write a short blog post without turning it into a computational novella, and the whole exercise only took a few hours from beginning to end. 🎉
Reuse
Citation
BibTeX citation:
@online{navarro2025,
author = {Navarro, Danielle},
title = {Linear Cosine Palettes},
date = {2025-09-14},
url = {https://blog.djnavarro.net/posts/2025-09-14_cosine-palettes/},
langid = {en}
}