Top | 戻る
四角形の自由変形
主に3D効果で用いられる自由変形手法の定番です。
任意の4点に囲まれた四角形を、別の4点に囲まれた四角形に変形します。
まず始めに、 三角形a(a0,a1,a2)を別の三角形b(b0,b1,b2)に変形する行列を作成します。
行列の作成は複雑そうですが、ベクトルと行列を使うと三角関数を使わずに四則演算のみで計算できます。
行列は、
- O(原点)へ移動
- 単位行列に変換(aの座標系の逆行列)
- bの座標系に変換
- b0へ移動
といった4変換の組み合わせで作成します(文末のソース createMatrix 関数)。
図1 三角形を変換するために必要なベクトル
実行結果 (三角形の自由変形)
四角形を変形する場合は、四角形を2つの三角形に分割してから各々の三角形を自由変形します。
しかし、四角形が平行四辺形でない場合は三角形の境界で画像が折れ曲がってしまいます(図2)。
実行結果 (四角形の自由変形/三角形×2)
図2 対角線が折れ曲がってしまう
折れ曲がりを解消するために、あらかじめ四角形を複数の四角形に分割してから各々の四角形を自由変形します。
分割した四角形は元の四角形よりも平行四辺形に近くなるため、スムーズな変形が可能になります(図3)。
転送元を短形のビットマップとして転送先を変形するのが一般的ですが、転送先を短形として転送元を変形するほうが意外性がある画像が得られるようです。
図3 四角形を分割
実行結果 (四角形の自由変形/縦横4分割, 三角形×32)
TransformUtil.as
package {
import flash.geom.Point;
import flash.geom.Matrix;
import flash.display.Graphics;
import flash.display.BitmapData;
public class TransformUtil {
public static function drawBitmapTriangle(
g : Graphics, bitmapData : BitmapData,
a0 : Point, a1 : Point, a2 : Point,
b0 : Point, b1 : Point, b2 : Point
) : void {
var matrix : Matrix = createMatrix(a0, a1, a2, b0, b1, b2);
g.beginBitmapFill(bitmapData, matrix);
drawTriangle(g, a0, a1, a2, matrix);
g.endFill();
}
public static function drawBitmapQuadrangle(
g : Graphics, bitmapData : BitmapData,
a0 : Point, a1 : Point, a2 : Point, a3 : Point,
b0 : Point, b1 : Point, b2 : Point, b3 : Point,
hDiv : int = 4, vDiv : int = 4
) : void {
for (var h : int = 0; h < hDiv; h++) {
var h0 : Number = h / hDiv;
var h1 : Number = (h + 1) / hDiv;
var ta0 : Point = getPoint(a1, a0, h0);
var ta1 : Point = getPoint(a1, a0, h1);
var ta2 : Point = getPoint(a3, a2, h0);
var ta3 : Point = getPoint(a3, a2, h1);
var tb0 : Point = getPoint(b1, b0, h0);
var tb1 : Point = getPoint(b1, b0, h1);
var tb2 : Point = getPoint(b3, b2, h0);
var tb3 : Point = getPoint(b3, b2, h1);
for (var v : int = 0; v < vDiv; v++) {
var v0 : Number = v / vDiv;
var v1 : Number = (v + 1) / vDiv;
var tta0 : Point = getPoint(ta1, ta0, v0);
var tta1 : Point = getPoint(ta1, ta0, v1);
var tta2 : Point = getPoint(ta3, ta2, v0);
var tta3 : Point = getPoint(ta3, ta2, v1);
var ttb0 : Point = getPoint(tb1, tb0, v0);
var ttb1 : Point = getPoint(tb1, tb0, v1);
var ttb2 : Point = getPoint(tb3, tb2, v0);
var ttb3 : Point = getPoint(tb3, tb2, v1);
drawBitmapTriangle(g, bitmapData,
tta0, tta1, tta2, ttb0, ttb1, ttb2);
drawBitmapTriangle(g, bitmapData,
tta3, tta1, tta2, ttb3, ttb1, ttb2);
}
}
}
private static function getPoint(p0 : Point, p1 : Point, ratio : Number) : Point {
return new Point(
p0.x + (p1.x - p0.x) * ratio,
p0.y + (p1.y - p0.y) * ratio
);
}
private static function createMatrix(
a0 : Point, a1 : Point, a2 : Point,
b0 : Point, b1 : Point, b2 : Point
) : Matrix {
var ma : Matrix = new Matrix(
a1.x - a0.x, a1.y - a0.y,
a2.x - a0.x, a2.y - a0.y);
ma.invert();
var mb : Matrix = new Matrix(
b1.x - b0.x, b1.y - b0.y,
b2.x - b0.x, b2.y - b0.y);
var m : Matrix = new Matrix();
m.translate(-a0.x, -a0.y);
m.concat(ma);
m.concat(mb);
m.translate(b0.x, b0.y);
return m;
}
private static function drawTriangle(
g : Graphics,
p0 : Point, p1 : Point, p2 : Point,
matrix : Matrix
) : void {
p0 = matrix.transformPoint(p0);
p1 = matrix.transformPoint(p1);
p2 = matrix.transformPoint(p2);
g.moveTo(p0.x, p0.y);
g.lineTo(p1.x, p1.y);
g.lineTo(p2.x, p2.y);
g.lineTo(p0.x, p0.y);
}
}
}
Contents Copyright © Kazuhiko Arase