各コンポーネントをインスタンス化し, ボタンに対して各クラスの関数を関連付けします。
//************************************************************************
//2012/03/22
//手書き波形をFFT
//Proggramed by Koten-Kairoya
//************************************************************************
package
{
import flash.display.Graphics;
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TextEvent;
import flash.text.*;
//画面サイズと背景色,フレームレートを設定
[SWF(width = "522", height = "490", frameRate = "30", backgroundColor = "0xffffff")]
public class Main extends Sprite
{
//================================================================
//各種スプライト
//時間領域描画用スプライト
public var canvas:Canvas = new Canvas(512, 200);
//FFT結果表示用スプライト
public var fftcanvas:fftCanvas = new fftCanvas(512, 220);
//区切り線,ボタン配置用スプライト
public var sp_button:Sprite = new Sprite();
//================================================================
//各種ボタン
public var sin_Button:LightGreenButton = new LightGreenButton("sin(t)");
public var sin20_Button:LightGreenButton = new LightGreenButton("sin(20t)");
public var clear_button:LightCyanButton = new LightCyanButton("消去");
public var fft_button:LightOrangeButton = new LightOrangeButton("FFT");
//================================================================
//main関数
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
//================================================================
//コンストラクタ
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
//============================================================
//FFT結果表示用スプライト表示
addChild(fftcanvas);
fftcanvas.x = 5
fftcanvas.y = 265;
//============================================================
//テキストの設定
var tf_time:TextField = new TextField();
var tf_frequency:TextField = new TextField();
var tf_origin1:TextField = new TextField();
var tf_origin2:TextField = new TextField();
var tf_t:TextField = new TextField();
var tf_omega:TextField = new TextField();
var format:TextFormat = new TextFormat();
//フォント設定
format.size = 20;
format.color = 0x000000;
format.font = "Arial";
tf_time.defaultTextFormat = format;
tf_frequency.defaultTextFormat = format;
tf_origin1.defaultTextFormat = format;
tf_origin2.defaultTextFormat = format;
tf_t.defaultTextFormat = format;
tf_omega.defaultTextFormat = format;
//自動サイズ設定
tf_time.autoSize = TextFieldAutoSize.LEFT;
tf_frequency.autoSize = TextFieldAutoSize.LEFT;
tf_origin1.autoSize = TextFieldAutoSize.LEFT;
tf_origin2.autoSize = TextFieldAutoSize.LEFT;
tf_t.autoSize = TextFieldAutoSize.LEFT;
tf_omega.autoSize = TextFieldAutoSize.LEFT;
//テキスト
tf_time.text = "時間領域波形";
tf_frequency.text = "周波数領域スペクトル";
tf_origin1.text = "o";
tf_origin2.text = "1";
tf_t.text = "t";
tf_omega.text = "ω";
//マウスでドラッグしたときに反転しないようにする
tf_time.selectable = false;
tf_frequency.selectable = false;
tf_origin1.selectable = false;
tf_origin2.selectable = false;
tf_t.selectable = false;
tf_omega.selectable = false;
//各TextFieldを表示し,座標を設定
addChild(tf_time);
tf_time.x = 5;
tf_time.y = 15;
addChild(tf_frequency);
tf_frequency.x = 5;
tf_frequency.y = 275;
addChild(tf_origin1);
tf_origin1.x = 3;
tf_origin1.y = 100;
addChild(tf_origin2);
tf_origin2.x = 3;
tf_origin2.y = 463;
addChild(tf_t);
tf_t.x = 505;
tf_t.y = 75;
addChild(tf_omega);
tf_omega.x = 498;
tf_omega.y = 463;//455;
//============================================================
//時間領域波形描画用スプライトを表示
addChild(canvas);
canvas.x = 5;
canvas.y = 5;
//============================================================
//ボタン配置用スプライトを用意
sp_button.graphics.beginFill(0xfffacd, 1);
sp_button.graphics.drawRect(0, 0, 512, 60);
sp_button.graphics.endFill();
sp_button.graphics.lineStyle(5, 0x696969);
sp_button.graphics.moveTo(0, 2);
sp_button.graphics.lineTo(512, 2);
sp_button.graphics.moveTo(0, 60);
sp_button.graphics.lineTo(512, 60);
sp_button.x = 5;
sp_button.y = 205;
addChild(sp_button);
//============================================================
//sin(t)ボタンを登録
sp_button.addChild(sin_Button);
sin_Button.x = 64 - sin_Button.width / 2;
sin_Button.y = sp_button.height/2 - sin_Button.height/2;
sin_Button.addEventListener(MouseEvent.CLICK, onSinButtonClick);
//sin(20t)ボタンを登録
sp_button.addChild(sin20_Button);
sin20_Button.x = 192 - sin20_Button.width / 2;
sin20_Button.y = sp_button.height/2 - sin20_Button.height/2;
sin20_Button.addEventListener(MouseEvent.CLICK, onSin20ButtonClick);
//消去ボタンを登録
sp_button.addChild(clear_button);
clear_button.x = 320 - clear_button.width/2;
clear_button.y = sp_button.height/2 - clear_button.height/2;
clear_button.addEventListener(MouseEvent.CLICK, onClearButton1Click);
//fftボタンを登録
sp_button.addChild(fft_button);
fft_button.x = 448 - fft_button.width / 2;
fft_button.y = sp_button.height/2 - fft_button.height/2;
fft_button.addEventListener(MouseEvent.CLICK, onFFTButtonClick);
//============================================================
//外枠を描く
this.graphics.lineStyle(10, 0x696969);
this.graphics.drawRect(0, 0, 522, 490);
}
//================================================================
//sin(t)ボタンが押された時に呼ばれる関数
private function onSinButtonClick(e:MouseEvent):void
{
canvas.clear();
fftcanvas.clear();
for (var i:int = 0; i < canvas.Width; i++ )
{
canvas.data[i] = canvas.Height/4*Math.sin( 2 * Math.PI / canvas.Width * i );
}
canvas.makegraph();
}
//================================================================
//sin(20t)ボタンが押された時に呼ばれる関数
private function onSin20ButtonClick(e:MouseEvent):void
{
canvas.clear();
fftcanvas.clear();
for (var i:int = 0; i < canvas.Width; i++ )
{
canvas.data[i] = canvas.Height/4*Math.sin(20* 2 * Math.PI / canvas.Width * i );
}
canvas.makegraph();
}
//================================================================
//消去ボタンが押された時に呼ばれる関数
private function onClearButton1Click(e:MouseEvent):void
{
//時間領域波形・周波数領域波形ともに表示をクリア,データもクリア。
canvas.clear();
fftcanvas.clear();
}
//================================================================
//FFTボタンがクリックされた時に呼ばれる関数
private function onFFTButtonClick(e:MouseEvent):void
{
//実際にメモリ上にあるデータを薄い青色で表示
canvas.makegraph();
//fft画面とデータをクリア。
fftcanvas.clear();
//時間領域波形データをfftデータへコピーする
for (var i:int = 0; i < 512; i++ )
{
//canvasのデータをfftcanvasのデータの実部へコピーする
fftcanvas.data[i][0] = canvas.data[i];
//虚部のデータはゼロ
fftcanvas.data[i][1] = 0;
}
//fft実行
fftcanvas.fft();
}
}
}
マウスドラッグにより,スプライト上に波形を描きます。 512ポイントデータを作製します。
package
{
import flash.display.Bitmap;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.text.*;
public class Canvas extends Sprite
{
//色の設定
public var backgroundColor:int = 0x0000ff;
public var lineColor:int = 0x0000FF;
public var lineColor2:int = 0x1E90FF;
//キャンバスの寸法
public var Width:int = 0;
public var Height:int = 0;
//クリック中フラグ
public var click_flag:Boolean = false;
//座標記録用
public var pre_x:Number = 0;
public var pre_y:Number = 0;
//データ記録用
public var data:Array = new Array();
//軸関係テキスト
public var origin_text:TextField = new TextField();
public var t_text:TextField = new TextField();
public var format:TextFormat = new TextFormat();
//コンストラクタ
public function Canvas(width:int, height:int)
{
//寸法を保存
this.Width = width;
this.Height = height;
//キャンバスを初期化
this.clear();
//キャンバスにイベントリスナを登録
addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
addEventListener(MouseEvent.ROLL_OUT, onMouseRollout);
}
//================================================================
//キャンバス上の波形を消去する関数
public function clear():void
{
//背景色で塗りつぶす
this.graphics.clear();
this.graphics.lineStyle(1, backgroundColor, 0.1);
this.graphics.beginFill(backgroundColor, 0.1);
this.graphics.drawRect(0, 0, Width, Height);
this.graphics.endFill();
//軸を描く
this.graphics.lineStyle(2, 0x000000);
this.graphics.moveTo(0, Height / 2);
this.graphics.lineTo(Width, Height / 2);
//補助線
this.graphics.lineStyle(1, 0x6f6f6f);
this.graphics.moveTo(Width/2, 0);
this.graphics.lineTo(Width / 2, Height);
this.graphics.moveTo(Width/4, 0);
this.graphics.lineTo(Width / 4, Height);
this.graphics.moveTo(3* Width/4, 0);
this.graphics.lineTo(3 * Width/4, Height);
this.graphics.moveTo(0, Height/4);
this.graphics.lineTo(Width, Height/4);
this.graphics.moveTo(0, 3*Height/4);
this.graphics.lineTo(Width, 3 * Height / 4);
//矢印
this.graphics.lineStyle(1, 0x000000);
this.graphics.beginFill(0x000000);
this.graphics.moveTo(Width, Height/2);
this.graphics.lineTo(Width - 12, Height/2 - 5);
this.graphics.lineTo(Width - 12, Height/2 + 5);
this.graphics.lineTo(Width, Height/2);
this.graphics.endFill();
//データをリセットする
for (var i:int = 0; i < Width; i++ )
{
data[i] =0;
}
}
//================================================================
//グラフ再描画関数
public function makegraph():void
{
for (var i:int = 0; i < Width - 1; i++ )
{
//薄い青色で,data配列中のデータを元にグラフを描く
this.graphics.lineStyle(3, lineColor2);
this.graphics.moveTo(i, -data[i]+100);
this.graphics.lineTo(i+1, -data[i+1]+100);
}
}
//================================================================
//キャンバス上でマウスのボタンが押された時に呼ばれる関数
private function onMouseDown(e:MouseEvent):void
{
//click中フラグを立てておく
click_flag = true;
}
//================================================================
//キャンバス上でマウスのボタンが離された時に呼ばれる関数
private function onMouseUp(e:MouseEvent):void
{
//click中フラグを下げる
click_flag = false;
}
//================================================================
//キャンバス上でマウスが動いた場合に呼ばれる関数
private function onMouseMove(e:MouseEvent):void
{
//マウスのボタンが押されていれば,軌跡を描画する
if (click_flag == true)
{
//線分を描く
this.graphics.lineStyle(3, lineColor);
this.graphics.moveTo(pre_x, pre_y);
this.graphics.lineTo(e.localX, e.localY);
//データ配列に現在座標を追加
//ただし,下方向に座標が大きくなるので,通常のグラフのように見せるにはマイナスを付ける必要あり。
//canvasの中心をx軸とするため,オフセット調整で Height/2を足しておく。
data[e.localX] = -e.localY + Height/2;
}
//過去の座標を更新
pre_x = e.localX;
pre_y = e.localY;
}
//================================================================
//キャンバス上からマウスが領域外に行ったときに呼ばれる関数
private function onMouseRollout(e:MouseEvent ):void
{
//click_flagを下げておく
click_flag = false;
}
}
}
このクラス内の“data[]”に与えられたデータをFFT処理し,表示します。 512ポイントFFTです。
package
{
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.text.*;
import flash.utils.ByteArray;
public class fftCanvas extends Sprite
{
//色の設定
public var backgroundColor:int = 0xFFF0F5;
public var lineColor:int = 0xFF7500;
//キャンバスの寸法
public var Width:int = 0;
public var Height:int = 0;
//データ記録用
public var data:Array = new Array();
//最終的にスペクトル情報を保存する配列
public var final_data:Array = new Array();
//テキスト関係
public var origin_text:TextField = new TextField();
public var format:TextFormat = new TextFormat();
//コンストラクタ
public function fftCanvas(width:int, height:int)
{
//寸法を保存
this.Width = width;
this.Height = height;
//キャンバスを初期化
this.clear();
//原点のテキスト
format.font = "Arial";
format.size = 20;
format.color = 0x000000;
origin_text.text = "o"
origin_text.autoSize = TextFieldAutoSize.LEFT;
origin_text.selectable = false;
origin_text.y = 195;
origin_text.x = -2;
origin_text.setTextFormat(format);
origin_text.defaultTextFormat = format;
//addChild(origin_text);
}
//================================================================
//キャンバス上の関数を消去する関数
public function clear():void
{
//背景色で塗りつぶす
this.graphics.clear();
this.graphics.lineStyle(1, backgroundColor);
this.graphics.beginFill(backgroundColor, 1);
this.graphics.drawRect(0, 0, Width, Height);
this.graphics.endFill();
//中心線を描く
this.graphics.lineStyle(1, 0x6f6f6f);
this.graphics.moveTo(Width/2, 0);
this.graphics.lineTo(Width / 2, Height);
this.graphics.moveTo(Width/2-1, 0);
this.graphics.lineTo(Width / 2 - 1, Height);
//矢印,座標軸を描く
this.graphics.lineStyle(4, 0x000000);
this.graphics.moveTo(0, Height-20);
this.graphics.lineTo(500, Height-20);
this.graphics.lineStyle(1, 0x000000);
this.graphics.beginFill(0x000000);
this.graphics.moveTo(Width, Height-20);
this.graphics.lineTo(Width - 12, Height-20 - 5);
this.graphics.lineTo(Width - 12, Height-20 + 5);
this.graphics.lineTo(Width, Height-20);
this.graphics.endFill();
//データをリセット
for (var i:int = 0; i < Width ; i++ )
{
data[i] = [0, 0];
final_data[i] = 0;
}
}
//================================================================
//FFT演算関数(わかりやすくするために若干冗長)
//================================================================
public function fft():void
{
//============================================================
//各種変数
//============================================================
//for文用変数
var i:int;
//バタフライ演算の2つのデータのインターバル
var AB_interval:int = data.length / 2;
//現在演算中の段数(ステップ数)を示す変数
var step_index:int;
//各ステップの中で,現在演算中のブロックを示す変数
var block_index:int;
//各ブロックの中で,現在演算中のデータ番号を示す変数
var data_index:int;
var data_index_B:int;
//バタフライ演算中の一時保存データ(実部,虚部)
var data_temp_re:Number;
var data_temp_im:Number;
var data_temp_reB:Number;
var data_temp_imB:Number;
//回転因子が進む最小単位
var w:Number = 2 * Math.PI / data.length;
//回転因子のインデックス
var Twiddle_index:int;
//?
var AB_interval2:int = 0;
//規格化用変数
var max_val:Number = 0;
//============================================================
//FFT演算実行
//============================================================
//FFT演算の段数は,データ総数がn点の場合,log2(n)段だけ繰り返す。
for (step_index = 1; step_index < data.length; step_index = step_index * 2 )
{
AB_interval2 = AB_interval * 2;
//各段の中で,バタフライ演算のまとまりは2^(段数-1)回だけある。
for (block_index=0; block_index < data.length; block_index += AB_interval2 )
{
//回転因子をリセット
Twiddle_index = 0;
//各バタフライ演算のまとまりの中で,バタフライ演算の回数はデータ総数nの場合,n/2回ずつある。
for (data_index = block_index; data_index < block_index + AB_interval; data_index++ )
{
//データBのインデックスは,データAのインデックス+AB_interbalで指定される。
data_index_B = data_index + AB_interval;
//================================================
//バタフライ演算実行。
//元のデータAを一時保存。
data_temp_re = data[data_index][0];
data_temp_im = data[data_index][1];
data_temp_reB = data[data_index_B][0];
data_temp_imB = data[data_index_B][1];
//新しいデータAを計算: A = (a + b)
data[data_index][0] = data_temp_re + data_temp_reB; //実部
data[data_index][1] = data_temp_im + data_temp_imB; //虚部
//新しいデータBを計算: B = (a - b)*exp(jw) これを複素演算する関数"mulTwiddle"を別に用意。
data[data_index_B][0] = mulTwiddle( data_temp_re - data_temp_reB, data_temp_im - data_temp_imB, w*Twiddle_index, true); //実部
data[data_index_B][1] = mulTwiddle( data_temp_re - data_temp_reB, data_temp_im - data_temp_imB, w*Twiddle_index, false); //虚部
//================================================
//回転因子を進める
Twiddle_index = Twiddle_index + step_index;
}
//1ブロックあたりの演算終了
}
//1段あたりの演算終了
//1段分の演算終了後,データAとデータBの間隔を半分にする。
AB_interval = AB_interval / 2;
}
//FFT演算終了(すべての段で演算終了)
//============================================================
//ビットリバース処理
//============================================================
for (i = 0; i < data.length; i++ )
{
if (i < bitReverse(i))
{
data_temp_re = data[i][0];
data_temp_im = data[i][1];
//データを入れ替える
data[i][0] = data[bitReverse(i)][0];
data[i][1] = data[bitReverse(i)][1];
data[bitReverse(i)][0] = data_temp_re;
data[bitReverse(i)][1] = data_temp_im;
}
}
//============================================================
//複素数の絶対値を取る
//============================================================
for (i = 0; i < data.length; i++ )
{
//絶対値計算
final_data[i] = (Math.sqrt( Math.pow(data[i][0], 2) + Math.pow(data[i][1], 2) ));
//最大値を探す
if (max_val < final_data[i])
{
max_val = final_data[i];
}
}
//============================================================
//最大値を用いて規格化
//============================================================
for (i = 0; i < final_data.length; i++ )
{
final_data[i] *= (Height-20)/max_val;
}
//============================================================
//グラフ更新
//============================================================
for (i = 0; i < data.length; i++ )
{
this.graphics.lineStyle(3, lineColor);
this.graphics.moveTo(i-1, Height-20);
this.graphics.lineTo(i - 1, -1 * final_data[i] + Height-20);
//矢印,座標軸を描く
this.graphics.lineStyle(4, 0x000000);
this.graphics.moveTo(0, Height - 20);
this.graphics.lineTo(500, Height - 20);
this.graphics.lineStyle(1, 0x000000);
this.graphics.beginFill(0x000000);
this.graphics.moveTo(Width, Height - 20);
this.graphics.lineTo(Width - 12, Height-20 - 5);
this.graphics.lineTo(Width - 12, Height-20 + 5);
this.graphics.lineTo(Width, Height-20);
this.graphics.endFill();
}
}
//================================================================
//回転因子("Twiddle factor"):exp(-jω)との積をとる関数
//================================================================
private function mulTwiddle(re:Number, im:Number, theta:Number, re_flag:Boolean ):Number
{
//回転因子: exp(-jw) = cos(w) -jsin(w)
//ある複素数 (a + jb) と上の回転因子との積を考えると,
// (a + jb) * (cos(w) - jsin(w)) = acos(w) + bsin(w) + jbcos(w) - jasin(w)
//実部: acos(w) + bsin(w)
//虚部: bcos(w) - asin(w)
if (re_flag == true)
{
return re * Math.cos(theta) + im * Math.sin(theta);
}
else
{
return im * Math.cos(theta) - re * Math.sin(theta);
}
}
//================================================================
//ビット反転関数
//================================================================
private function bitReverse(number:int):int
{
//全ビット数
var bit_num:int = Math.log(data.length) / Math.log(2);
//for文の繰り返し用にビット数をコピーしておく
var loop_num:int = bit_num;
//ビット配列(各要素が0か1で,2進数データを表す)
var bit_array:Array = new Array();
//最終的に返す値用変数
var return_val:int;
//2進数では,0ビット目から始まるので,「ビット数-1」ビット目がMSBが対応する。
bit_num -= 1;
//ビット数分だけ繰り返す
for (var i:int = 0; i < loop_num; i++ )
{
if (number >= Math.pow(2, bit_num))
{
//引き算する
number -= Math.pow(2, bit_num);
//その桁に"1"をセット
bit_array.push(1);
}
else
{
//その桁に"0"をセット
bit_array.push(0);
}
//桁をひとつ下げる
bit_num -= 1 ;
}
//ビット逆順に取り出す
for (i = loop_num-1; i >=0 ; i-- )
{
//ビットが"1"なら値を加算。
if (bit_array.pop() == 1)
{
//2^nの値をreturn_valに加算して2進→10進変換
return_val += Math.pow(2,i );
}
}
return return_val;
}
}
}
普通のボタンクラスです。 このクラス内ではイベントとして,単にボタンの画像を入れ替える処理のみを実装しています。 このボタンをインスタンスするクラス内で,再度イベントリスナを登録し,所望の処理を実装します。
※“LightGreenButton_off.png”と“LightGreenButton_on.png”という画像を読み込んでいます。
※このクラスの他に,Mainクラスでインスタンス化されている“LightCyanButton”,“LightOrangeButton”は このクラスで使用する画像を入れ替えただけのものです。 それぞれ,“LightCyanButton_off.png”と“LightCyanButton_on.png”, “LightOrangeButton_off.png”と“LightOrangeButton_on.png”の画像が必要になります。 適当に自前の画像で代用可です。
package
{
import flash.display.Sprite;
import flash.text.*;
import flash.events.MouseEvent;
import flash.display.Bitmap;
public class LightGreenButton extends Sprite
{
//画像ファイルを読み込む
[Embed(source = "./LightGreenButton_off.png")]
public var LightGreenButton_off_Img:Class;
public var button_off:Bitmap = new LightGreenButton_off_Img();
[Embed(source = "./LightGreenButton_on.png")]
public var LightGreenButton_on_Img:Class;
public var button_on:Bitmap = new LightGreenButton_on_Img();
//テキスト
public var text:TextField = new TextField();
public var format:TextFormat = new TextFormat();
//クリック中フラグ
public var button_flag:Boolean = false;
//コンストラクタ(ボタンのサイズは画像依存,引数は表示するテキスト)
public function LightGreenButton(button_text:String)
{
//ボタンoffの画像を表示
addChild(button_off);
//テキスト関係
format.size = 28;
format.color = 0x000000;
format.font = "Arial";
text.defaultTextFormat = format;
text.setTextFormat(format);
text.autoSize = TextFieldAutoSize.LEFT;
text.selectable = false;
text.text = button_text;
addChild(text);
text.x = this.width / 2 - text.width / 2;
text.y = this.height/2 - text.height / 2;
//イベントリスナを登録
addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
addEventListener(MouseEvent.ROLL_OUT, onMouseRollout);
}
private function onMouseDown(e:MouseEvent):void
{
if (button_flag == false)
{
button_flag = true;
removeChild(button_off);
addChild(button_on);
setChildIndex(text, 1);
}
}
private function onMouseUp(e:MouseEvent):void
{
if (button_flag == true)
{
button_flag = false;
removeChild(button_on);
addChild(button_off);
setChildIndex(text, 1);
}
}
private function onMouseRollout(e:MouseEvent):void
{
if (button_flag == true)
{
button_flag = false;
removeChild(button_on);
addChild(button_off);
setChildIndex(text, 1);
}
}
}
}