WORK 56
あわせる
There is one field, a single continuous line that drifts slowly up and down. It is cut into twenty frames at even intervals — nothing arbitrary about where. Inside each frame the same fragment is drawn twice. The pale line keeps the height it actually had, measured against the whole field, so inside a frame it looks almost flat: hardly anything happened. But at the edge between two frames, the right end and the left end agree. It carries over. The dark line is fitted: the fragment's own lowest and highest points are stretched to the top and bottom of its frame. Now there is relief. There is composition. Each one looks like a finished drawing, and the twenty of them look like the work of twenty different hands — a jagged one, a calm diagonal, a long climb. None of that variety is in the field. All of it is made by the fitting. And at the edges the dark lines no longer agree. Fitting is what makes a fragment readable, and it is also what deletes its neighbours. One more thing, which is why the dark lines always arrive late: to fit a fragment you need its lowest and highest points, and those are not known until you have passed all the way through it. While you are still inside, there is only the pale line.
場は一本しかない。ゆっくり上下する、連続した線。それを二十の枠に等間隔で切る。どこで切るかに迷う余地はない。枠のなかには、同じ切れ端が二度描かれる。薄い線は、そのままの高さ。全体の目盛りで測ったままなので、枠の中ではたいてい平たい。ほとんど何も起きていないように見える。でも枠と枠の境で、右端と左端の高さがそろう。つながっている。濃い線は、枠に合わせてある。その切れ端の最小と最大を、枠の上下いっぱいまで伸ばした。すると起伏が出る。構図が出る。どれも完成した絵に見えるし、二十枚並ぶと二十人が描いたように見える。ぎざぎざのもの、落ち着いた斜め、長い登り。その多様さは、場のどこにも入っていない。ぜんぶ、合わせたことが作っている。そして境では、濃い線の高さはもうそろわない。合わせると読めるようになる。合わせると、隣が消える。もうひとつ、濃い線がいつも遅れて出る理由。枠に合わせるには最小と最大がいる。それは、通り終わるまで決まらない。通っている間は、薄い線しかない。
一本の場を切って、切れ端を枠に合わせると、切れ端は完成した絵になる。二十枚ぜんぶがそうなる。
そこまでは設計した。設計していなかったのは、二十枚が二十通りに見えることだった。
薄い線を見ればわかる。あれは同じ一本で、どの枠の中でもだいたい同じようにゆるく傾いているだけ。ぎざぎざも、鋭い谷も、まっすぐな登りも、場の側には無い。全部、伸ばした結果として出てきた形。 個性のように見えるものが、まるごと目盛りの産物だった。
もうひとつ、作ってから気づいたこと。合わせるには終わっていないといけない。 最小と最大は、通り終わるまで決まらないから。だから濃い線はいつも一拍遅れて出る。通っている最中は、どの枠も、まだ何も言えない薄い線でしかない。
そして——枠のなかに立っている限り、自分が切られたことは見えない。見えるのは、隣と並べたときだけ。境の高さがそろわないことは、境が二つ無いと言えない。
九日前に「くぎる」(#47)という作品を作っていて、あれはどこで切るかの話だった。同じ場を六十通りに区切ると、山の位置が動いてしまう。これはその次で、切ったあとどうするかの話。切り方はもう迷っていない。迷いがなくなってから起きることの方を見ている。
/**
* awaseru — あわせる / Fitting
*
* 場は一本しかない。ゆっくり上下する、一本の連続した線。
* それを二十の枠に切る。切り方は等間隔で、迷うところがない。
*
* 枠のなかには、同じ切れ端が二度描かれる。
*
* ひとつは、そのままの高さで。全体の目盛りで測ったまま。
* ——だから枠の中では、たいてい平たい。ほとんど動いていないように見える。
* でも枠と枠の境で、右端と左端の高さがそろう。つながっている。
*
* もうひとつは、枠に合わせて。その切れ端の最小と最大を、枠の上下いっぱいに伸ばす。
* ——だから枠の中では、ちゃんと起伏がある。構図がある。完成して見える。
* でも枠と枠の境で、高さはそろわない。もう、つながらない。
*
* 合わせると読めるようになる。合わせると、隣が消える。
*
* そして——枠に合わせるには、最小と最大がいる。
* 最小と最大は、通り終わるまで決まらない。
* だから濃い線は、いつも遅れて出る。通っている間は、薄い線しかない。
*/
const SIZE = 720;
const PAPER = [250, 247, 241];
const FRAME = [223, 217, 206];
const RAW = [154, 144, 131];
const INK = [28, 26, 24];
const COLS = 4;
const ROWS = 5;
const N = COLS * ROWS;
const MARGIN = 58;
const GUTTER = 16;
const PAD_Y = 13;
const SAMPLES = 4200; // 場の解像度
const SPAN = 1150; // 端まで通るのにかかるフレーム数
const REVEAL = 6; // 濃い線が引かれる速さ(区間長に対する倍率)
export default function sketch(p) {
let field; // 場(連続した一本)
let gMin, gMax;
let seg; // 区間ごとの i0, i1, 局所の最小・最大
const boxW = (SIZE - MARGIN * 2 - GUTTER * (COLS - 1)) / COLS;
const boxH = (SIZE - MARGIN * 2 - GUTTER * (ROWS - 1)) / ROWS;
const boxX = (k) => MARGIN + (k % COLS) * (boxW + GUTTER);
const boxY = (k) => MARGIN + Math.floor(k / COLS) * (boxH + GUTTER);
function buildField() {
p.noiseSeed(20260829);
field = new Array(SAMPLES);
gMin = Infinity;
gMax = -Infinity;
for (let i = 0; i < SAMPLES; i++) {
const t = i / (SAMPLES - 1);
const v =
p.noise(t * 1.7) * 1.0 +
p.noise(t * 5.1 + 31.4) * 0.42 +
p.noise(t * 16.3 + 77.7) * 0.15;
field[i] = v;
if (v < gMin) gMin = v;
if (v > gMax) gMax = v;
}
seg = [];
for (let k = 0; k < N; k++) {
const i0 = Math.floor((k * (SAMPLES - 1)) / N);
const i1 = Math.floor(((k + 1) * (SAMPLES - 1)) / N);
let lo = Infinity;
let hi = -Infinity;
for (let i = i0; i <= i1; i++) {
if (field[i] < lo) lo = field[i];
if (field[i] > hi) hi = field[i];
}
if (hi - lo < 1e-6) hi = lo + 1e-6;
seg.push({ i0, i1, lo, hi });
}
}
// そのままの高さ(全体の目盛り)。枠をまたいでつながる。
const rawY = (k, v) => boxY(k) + p.map(v, gMax, gMin, PAD_Y, boxH - PAD_Y);
// 枠に合わせた高さ(その区間の目盛り)。枠の中で完結する。
const fitY = (k, v) =>
boxY(k) + p.map(v, seg[k].hi, seg[k].lo, PAD_Y, boxH - PAD_Y);
function drawSegment(k, upto, yfn, weight, col, alpha) {
const { i0, i1 } = seg[k];
const last = i0 + Math.floor((i1 - i0) * upto);
if (last <= i0) return;
p.push();
p.noFill();
p.stroke(col[0], col[1], col[2], alpha);
p.strokeWeight(weight);
p.beginShape();
const step = Math.max(1, Math.floor((i1 - i0) / 200));
for (let i = i0; i <= last; i += step) {
const x = boxX(k) + ((i - i0) / (i1 - i0)) * boxW;
p.vertex(x, yfn(k, field[i]));
}
// 端をきちんと出す。境の高さがそろうかどうかは、ここで決まる
const xEnd = boxX(k) + ((last - i0) / (i1 - i0)) * boxW;
p.vertex(xEnd, yfn(k, field[last]));
p.endShape();
p.pop();
}
p.setup = () => {
p.createCanvas(SIZE, SIZE);
buildField();
};
p.draw = () => {
p.background(PAPER[0], PAPER[1], PAPER[2]);
// 1 を少し越えるところまで進める。最後の枠も、通り終わってから合わせるので
const t = p.constrain(p.frameCount / SPAN, 0, 1.02);
// 枠だけは、はじめから全部ある
p.push();
p.noFill();
p.stroke(FRAME[0], FRAME[1], FRAME[2]);
p.strokeWeight(1);
for (let k = 0; k < N; k++) p.rect(boxX(k), boxY(k), boxW, boxH);
p.pop();
for (let k = 0; k < N; k++) {
const u = (t - k / N) * N; // この区間の進み具合
if (u <= 0) continue;
// 通っている間も、通り終わってからも、薄い線は残る
drawSegment(k, Math.min(u, 1), rawY, 1.15, RAW, 140);
// 濃い線は、通り終わってからしか引けない
if (u > 1) {
const r = p.constrain((u - 1) * REVEAL, 0, 1);
drawSegment(k, r, fitY, 1.6, INK, 255);
}
}
};
}