はじめに
3DCGにおいて、変形させる機能をdeformerと呼んだりします。その中で、「曲げ」Bendツールをプログラムした話です。chatGPT利用。
環境
Processing 4
アルゴリズム
点P(x,y)をbend関数でθ曲げることを考えます。
Bend変形のカギとなるのは、x座標を円周上の角度にマッピングすることです。
Bend適用前のグリッド全体の長さをL、bend角度をθとしたとき、円弧上での対応する角度θxは次式であらわすことができます。
\theta_x=\frac{\theta}{L}x
次に、yを円弧の半径へマッピングします。円の基準半径をRとし、点Pの半径をrとし次式を定義します。
r=R+y
bend変形後の座標をP(x',y')とすると、
x'=r\sin(\theta_x)
y'=R-r\cos(\theta_x)
となる。
このとき、
\sin(\theta_x)は円周方向の移動を表し、
\cos(\theta_x)は円の高さの変化を表す。
最終的な変換式は、以下となる。
x'=(R+y)\sin(\frac{\theta}{L}x)
y'=R-(R+y)\cos(\frac{\theta}{L}x)
プログラム
int gridWidth = 32; // X方向の点の数
int gridHeight = 6; // Y方向の点の数
float cellSize = 20; // 格子のサイズ
float length = gridWidth * cellSize; // グリッドの全長
PVector[][] originalGrid = new PVector[gridWidth+1][gridHeight+1];
PVector[][] bentGrid = new PVector[gridWidth+1][gridHeight+1];
float bendAngle; // 曲げ角度(度)
void setup() {
size(600, 600);
generateGrid();
}
void draw() {
background(255);
translate(width / 2, height / 2); // 画面中央に配置
// ベンド処理
applyBend(bendAngle);
// 曲げた後のグリッドを描画(黒)
stroke(0);
drawGrid(bentGrid);
// 角度を変化させてアニメーション
bendAngle = 360*sin(frameCount*0.01);
}
// **長方形のグリッドを作成**
void generateGrid() {
for (int x = 0; x <= gridWidth; x++) {
for (int y = 0; y <= gridHeight; y++) {
float posX = (x - gridWidth / 2) * cellSize; // 中心を0にする
float posY = (y - gridHeight / 2) * cellSize; // 中心を0にする
originalGrid[x][y] = new PVector(posX, posY);
}
}
}
// **Bend処理**
void applyBend(float angle) {
float theta = radians(angle);
float R = (theta != 0) ? length / theta : Float.POSITIVE_INFINITY; // 曲げる半径
for (int x = 0; x <= gridWidth; x++) {
for (int y = 0; y <= gridHeight; y++) {
PVector p = originalGrid[x][y];
// **X軸方向の回転角(円周上の位置)**
float localAngle = p.x*theta/length;
// **Y軸方向の半径変化(各層が均等にカーブする)**
float r = R + p.y; // 層ごとに異なる半径
// **円弧上に変換**
float newX = r * sin(localAngle);
float newY = R - r * cos(localAngle); // 円の中心基準に配置
bentGrid[x][y] = new PVector(newX, newY);
}
}
}
// **グリッドを描画**
void drawGrid(PVector[][] grid) {
for (int x = 0; x <= gridWidth; x++) {
for (int y = 0; y <= gridHeight; y++) {
PVector p = grid[x][y];
ellipse(p.x, p.y, 3, 3);
if (x > 0) {
PVector prevX = grid[x - 1][y];
line(prevX.x, prevX.y, p.x, p.y);
}
if (y > 0) {
PVector prevY = grid[x][y - 1];
line(prevY.x, prevY.y, p.x, p.y);
}
}
}
}