スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

--.--.-- | | スポンサー広告

グループにわける

ランダムに配置した点を7つのグループにわけるサンプルです。






各点の色が、その点が所属するグループをあらわしています。

「再配置」ボタンを押すと、ランダムな座標に点が300個配置されます。この時点では所属するグループはバラバラで、近くにある点が別のグループに属していてもおかまいなしデス。「グループ化」ボタンを押すと、点は位置が近いグループに配属しなおされます。グループ分けがすんだら背景色が各グループの領域として塗り分けられます。

グループ分けの仕方は、まず各グループごとに所属する点の重心位置を求めます。すべての点について、各グループの重心座標との距離を求めて、もし自分が所属するグループの重心座標よりも別のグループの重心座標の方が近かったらそっちのグループに異動します。コレを、異動する点がなくなるか、100回くりかえすまでやります。K平均法というらしいデス。たぶん。

グループ分けが終わったら、背景のビットマップの各点について一番近くの重心を持つグループの色を設定してます。

今回のサンプルでは、グループをあらわすClusterクラスを作成してフレームアクションでソレを利用するようにしました。
Clusterクラスのソースコードはこんなカンジです。

package {
/**
* クラスタ。
*/
public class Cluster {
/** 点の色 */
private var _color:uint;
/** 背景色 */
private var _backgroundColor:uint = 0;
/** クラスタに含まれる点のx座標の合計 */
private var _sumX:Number = 0;
/** クラスタに含まれる点のy座標の合計 */
private var _sumY:Number = 0;
/** クラスタに含まれる点の数 */
private var _cnt:uint = 0;
/**
* コンストラクタ。
* @param color 点の色
* @param backgroundColor 背景の色
*/
public function Cluster(color:uint, backgroundColor:uint) {
_color = color;
_backgroundColor = backgroundColor;
}
/**
* クラスタに点を追加します。
* @param x 追加する点のx座標
* @param y 追加する点のy座標
*/
public function addPoint(x:Number, y:Number):void {
_sumX += x;
_sumY += y;
_cnt++;
}
/**
* クラスタから指定した座標の点を削除します。
* @param x 削除する点のx座標
* @param y 削除する点のy座標
*/
public function removePoint(x:Number, y:Number):void {
_sumX -= x;
_sumY -= y;
_cnt--;
}
/**
* クラスタ内の点の重心座標を戻します。
* @return 重心の座標をプロパティに持つオブジェクト
*/
public function getCenter():Object {
if (_cnt == 0) {
return {x:0, y:0};
}
return {x:_sumX / _cnt, y:_sumY / _cnt};
}
/**
* 点の色を戻します。
* @return 点の色
*/
public function get color():uint {
return _color;
}
/**
* 背景色を戻します。
* @return 背景色
*/
public function get backgroundColor():uint {
return _backgroundColor;
}
/**
* 状態をリセットします。
*/
public function reset():void {
_sumX = 0;
_sumY = 0;
_cnt = 0;
}
}
}
Clusterクラスは、点の色と背景色以外に所属する点のx座標の合計値/y座標の合計値/点の数を持っています。各合計値を点の数で割ってやれば重心座標が算出できるという寸法デス。

コイツを利用するフレームアクションはこんなカンジです。
// 点の数
var cnt:uint = 300;
// 点の半径
var r:Number = 2;
// 計算回数の最大値
var threshold:uint = 100;
// グループの配列
var clusterArray:Array = [new Cluster(0xFF0000, 0xFFCCCC),
new Cluster(0x00FF00, 0xCCFFCC),
new Cluster(0x0000FF, 0xCCCCFF),
new Cluster(0xFFFF00, 0xFFFFCC),
new Cluster(0x00FFFF, 0xCCFFFF),
new Cluster(0xFF00FF, 0xFFCCFF),
new Cluster(0x000000, 0xCCCCCC)];
// グループ数
var cCnt:uint = clusterArray.length;
// 点の配列
var pointArray:Array;
// 計算回数
var t:uint = 0;
// 背景を描画する領域
var bd:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight);
var bg:Bitmap = new Bitmap(bd);
addChild(bg);
// 点を描画する領域
var pScreen:Sprite = new Sprite();
addChild(pScreen);
// 再配置ボタン
restart_mc.addEventListener(MouseEvent.CLICK,
function (e:MouseEvent):void {
init();
});
addChild(restart_mc);
// グループ化ボタン
cluster_mc.addEventListener(MouseEvent.CLICK,
function (e:MouseEvent):void {
t = 0;
pScreen.addEventListener(Event.ENTER_FRAME, doEnterFrame);
});
addChild(cluster_mc);
// 初期化処理
init();
function init():void {
// グループをリセット
for each (var c:Cluster in clusterArray) {
c.reset();
}
// 点を生成
pointArray = new Array();
for (var i:uint = 0; i < cnt; i++) {
var p:Object = new Object();
p.x = Math.random() * stage.stageWidth;
p.y = Math.random() * stage.stageHeight;
p.cluster = clusterArray[i % cCnt];
p.cluster.addPoint(p.x, p.y);
pointArray.push(p);
}
// 描画
drawAllPoints();
}

// すべての点を描画する
function drawAllPoints():void {
var g:Graphics = pScreen.graphics;
g.clear();
for each (var p:Object in pointArray) {
g.beginFill(p.cluster.color);
g.drawCircle(p.x, p.y, r);
g.endFill();
}
}

// 背景を描画する
function drawBackground():void {
var cArray:Array = getCenterArray();
bd.lock();
for (var iy:uint = 0; iy < bd.height; iy++) {
for (var ix:uint = 0; ix < bd.width; ix++) {
var c:Cluster = getNearCluster({x:ix, y:iy}, cArray);
bd.setPixel(ix, iy, c.backgroundColor);
}
}
bd.unlock();
}

// フレームイベントハンドラ
function doEnterFrame(e:Event):void {
var moved:Boolean = false;
// 重心の配列
var cArray = getCenterArray();
// 最も近い重心も持つグループへ点を移動
for (var i:uint = 0; i < cnt; i++) {
var p:Object = pointArray[i];
var c:Cluster = getNearCluster(p, cArray);
if (p.cluster != c) {
p.cluster.removePoint(p.x, p.y);
p.cluster = c;
p.cluster.addPoint(p.x, p.y);
moved = true;
}
}
// 再描画
drawAllPoints();
// 最大計算回数を越えたか、グループを移動した点がなければ終了
if (!moved || t > threshold) {
pScreen.removeEventListener(Event.ENTER_FRAME, doEnterFrame);
drawBackground();
return;
}
t++;
}

// 重心の配列を戻します。
function getCenterArray():Array {
var cArray = new Array();
for (var i:uint = 0; i < cCnt; i++) {
cArray.push(clusterArray[i].getCenter());
}
return cArray;
}

// 最も重心が近いグループを戻します。
function getNearCluster(p:Object, cArray:Array):Cluster {
var cIdx:uint = 0;
var min:Number = getDist2(p, cArray[0]);
for (var i:uint = 1; i < cCnt; i++) {
var d2:Number = getDist2(p, cArray[i]);
if (d2 < min) {
cIdx = i;
min = d2;
}
}
return clusterArray[cIdx];
}

// 2点間の距離の2乗を戻します。
function getDist2(p1:Object, p2:Object):Number {
var dx:Number = p1.x - p2.x;
var dy:Number = p1.y - p2.y;
return dx * dx + dy * dy;
}
clusterArrayは、Clusterオブジェクトを保持する配列デス。今回のサンプルでは、色の設定が違う7つのClusterオブジェクトを用意しています。

initメソッドでは、点をあらわすオブジェクトを生成しています。座標はランダムです。また、このオブジェクトは点が所属するグループもプロパティで持っています。ココではグループは均等に割り当てています。点の生成が終わったらdrawAllPointsメソッドで、点を描画しています。サンプルでは、半径2の円デス。

doEnterFrameメソッドは、グループ化ボタンが押されたときにEvent#ENTER_FRAMEとひもづけられるイベントハンドラです。initメソッドで生成したすべての点について、一番近い重心座標を探してもしヨソのグループの方が近ければソッチに異動させてます。異動する点がなくなったor設定された計算回数を越えたらイベントリスナを解除して、drawBackgroundメソッドで背景を描画しています。

drawBackgroundメソッドは、背景ビットマップの各点について一番近い重心座標をもつグループを探して、ソイツに設定された背景色でピクセルを塗っています。



3つのグループだとイマイチつまらないので、7つにふやしてみました。

スポンサーサイト

テーマ:Flash - ジャンル:コンピュータ

2008.03.02 | | Comments(0) | Trackback(0) | Flash CS3

«  | HOME |  »

プロフィール

HundredthMonkey

Author:HundredthMonkey
プログラマ。

ブロとも申請フォーム

この人とブロともになる

メールフォーム

名前:
メール:
件名:
本文:

ブログ内検索


上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。