
H: 0~360Hについては図を見ての通りぐるっと一周360度です。その他は0~1の範囲にあるパラメータとします。さて、しかしながらこのHが曲者でして...、Hは色相ですのでペイントでやってみると、たとえば左上の点から上に沿って右に進ませていくっていう操作に当たります。このときRGBをじっくり観察してみると、
S: 0.0~1.0
V: 0.0~1.0
R ⇒ Y ⇒ G ⇒ C ⇒ B ⇒ M(Y:Yellow、C:Cyan、M:Magenta)と移り変わります。要は、まずRを255に保ったままGが0から255へ変化、後Gを255に保ったままRが255から0へ変化...(以下略)と動きます。プログラムし辛い...、ヤダヤダ。と思っているところにWikipedia様が登場です。











(Wikipedia: HSV色空間より抜粋)
なんと!こんな便利な変換式があるんですね。この計算のミソは、はじめにHiを60で割ってあげるところにあります。すると0~6(0/60~360/60)になるのですが、この値をガウス記号(床関数、floor)で切り捨ててあげちゃうんです。つまり、0、1、2、3、4、5のどれかにしちゃうというわけですが...、すると、この数字がそれぞれRYGCBMのどれかに当たることになります!なるほど、これで場合分けをすればいいのか...。ちなみにmod6(6で割ったときの余り)を求めているのは、色相が0~360ではない角度(1000度とか)になった場合を考慮してのものです。さてさて、この数字による場合分けを行ってしまえばめでたくHMKをRGBに変換することができるので、以下にソースを示します。
// HSVtoRGB変換
function HSVtoRGB(H, S, V) {
var ret = [0, 0, 0];
// 引数チェック
if(S < 0 || S > 1 || V < 0 || V > 1) return ret;
var Hi = Math.floor(H/60) % 6;
var f = (H/60) - Hi;
// RGB
V = V * 255;
var p = Math.round(V * (1 - S));
var q = Math.round(V * (1 - f*S));
var t = Math.round(V * (1 - (1 - f)*S));
V = Math.round(V);
// 色相による場合わけ
switch(Hi) {
case 0: ret = [V, t, p]; break;
case 1: ret = [q, V, p]; break;
case 2: ret = [p, V, t]; break;
case 3: ret = [p, q, V]; break;
case 4: ret = [t, p, V]; break;
case 5: ret = [V, p, q]; break;
}
return ret;
}
こんな感じで配列でRGBを返します。どうせならペイントみたいなインターフェースを作ってしまいましょう。これを頭に#をつけた16進数コードであらわされたRGBの文字列に変換する関数を付け加えます。function RGBto16(RGB) {
var R = (RGB[0] < 16) ? "0" + RGB[0].toString(16): RGB[0].toString(16);
var G = (RGB[1] < 16) ? "0" + RGB[1].toString(16): RGB[1].toString(16);
var B = (RGB[2] < 16) ? "0" + RGB[2].toString(16): RGB[2].toString(16);
return ("#" + R + G + B);
}
function getClr(H, S, V) {
return RGBto16(HSVtoRGB(H,S,V));
}
ここでtoString()を使いましたが、これは与えた引数を基数とした数字を返してくれます。16なので16進数のコードが返ってくるわけです。んー、便利。また、3項演算子を用いて、もし、15=fのように16進数に直した時1文字になってしまうとき、頭に0をつけて2文字にするようにします。この関数とHSVtoRGB()をまとめてやってくれるgetClr()を定義しました。この関数を使って色を設定します。表示部は以下のようになります。ちなみに、上の例ではペイントに合わせるため、getClrの第3引数を(2+j)/3としました。もし、ここが1だった場合は下はグレーではなく真白になります。
// パラメータ
di = 6; // 横の刻み(0~360)
dj = 0.04; // 縦の刻み(0~1)
// HSVtoRGB変換
function HSVtoRGB(H, S, V) {
var ret = [0, 0, 0];
// 引数チェック
if(S < 0 || S > 1 || V < 0 || V > 1) return ret;
var Hi = Math.floor(H/60) % 6;
var f = (H/60) - Hi;
// RGB
V = V * 255;
var p = Math.round(V * (1 - S));
var q = Math.round(V * (1 - f*S));
var t = Math.round(V * (1 - (1 - f)*S));
V = Math.round(V);
// 色相による場合わけ
switch(Hi) {
case 0: ret = [V, t, p]; break;
case 1: ret = [q, V, p]; break;
case 2: ret = [p, V, t]; break;
case 3: ret = [p, q, V]; break;
case 4: ret = [t, p, V]; break;
case 5: ret = [V, p, q]; break;
}
return ret;
}
// 16進数変換
function RGBto16(RGB) {
var R = (RGB[0] < 16) ? "0" + RGB[0].toString(16): RGB[0].toString(16);
var G = (RGB[1] < 16) ? "0" + RGB[1].toString(16): RGB[1].toString(16);
var B = (RGB[2] < 16) ? "0" + RGB[2].toString(16): RGB[2].toString(16);
return ("#" + R + G + B);
}
// まとめ
function getClr(H, S, V) {
return RGBto16(HSVtoRGB(H,S,V));
}
// カラーコード取得用グローバル変数
_H = 0;
_S = 0;
// VBar変更
function changeVBar(H, S) {
_H = H; _S = S;
id=0;
for(j=1; j>=0; j-=dj) {
document.getElementById("b_"+ id).bgColor = getClr(H,S,j);
id++;
}
}
// カラーコード取得
function setClr(V) {
with(document.getElementById("gtnClr")) {
bgColor = getClr(_H, _S, V);
style.color = getClr(_H, 0, 1 - V);
innerHTML = getClr(_H, _S, V);
}
}
| <table class="color_table" border="0" cellpadding="0" cellspacing="0"> <script type="text/javascript"> for(j=1; j>=0; j-=dj) { document.write("<tr>"); for(i=0; i<360; i+=di) { document.write('<td onclick="changeVBar(' + i + ',' + j + ')" bgcolor="' + getClr(i,j,1) + '"> </td>'); } document.write("</tr>"); } </script> </table> | <table class="color_table VBar" border="0" cellpadding="0" cellspacing="0"> <script type="text/javascript"> id = 0; for(j=1; j>=0; j-=dj) { document.write('<tr>'); document.write('<td id="b_' + id + '" onclick="setClr(' + j + ')" bgcolor="' + getClr(0,1,j) + '"> </td>'); document.write("</tr>"); id++; } </script> </table> |
| color | |
①でかい領域のどこかをクリック
color
var Human = Class.create();このように記述します。クラスの定義については、
Human.prototype = {こんな感じです。initializeがコンストラクタで、この中にフィールドを入れます。メソッドはprototypeの中に放り込んでやればおkです。他にも記述の仕方はありますが、僕の場合は面倒くさいんでこれで統一しています。ちなみにプログラム自体は、場合分け処理が適当なので見ての通りうまく避けてくれませんw。誰かうまい処理考えてくれないかなー...。以下全ソースです。
initialize: function(obj,x,y,vx,vy) {
~
},
move: function() {
~
}
};
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;">
<meta http-equiv="content-script-type" content="text/JavaScript">
<title>人間工学シミュレーション</title>
<meta http-equiv="Content-Type" content="text/html; charset=Shift-JIS">
<style type="text/css"><!--
* {
margin: 0;
padding: 0;
}
.street {
background-color: #ccc;
position: absolute;
}
.human {
position: absolute;
font-size: 16px;
line-height: 16px;
margin-left: -8px;
margin-top: -8px;
}
.human2 {
color: #f00;
}
#area{
background-color: #000;
}
--></style>
<script type="text/javascript" src="pt.js"></script>
<script type="text/javascript">
// ---------- 初期設定 ----------
// ウインドウサイズと道幅
var WinSizeW = 360, WinSizeH = 360;
var StreetW = WinSizeH/2;
var StreetL = (WinSizeW - StreetW)/2;
var StreetR = StreetL + StreetW;
// 描画間隔(ms)
var t = 10;
// 速度幅
var vy = 5;
var vx = 0;
// 人
var num = 8; // 人数
var size = 8;
var human = Array(num);
var human2 = Array(num);
// ---------- 初期化 ----------
// 初期化関数
function init() {
createStreet(); // 道の描画
for(i=0; i<num; i++) {
var left = StreetL + Math.floor(StreetW * Math.random());
var vx0 = Math.floor(vx * Math.random());
var vy0 = Math.floor(1 + vy * Math.random());
human[i] = new Human($('human_p' + i), left, 0, vx0, vy0);
human[i].move();
left = StreetL + Math.floor(StreetW * Math.random());
vx0 = Math.floor(vx * Math.random());
vy0 = Math.floor(1 + vy * Math.random());
human2[i] = new Human($('human_m' + i), left, WinSizeH, vx0, -vy0);
human2[i].move();
}
// タイマーの実行
Timer();
}
// 道の生成
function createStreet() {
var vertical = $('vertical');
Element.setStyle(vertical,
{
'left' : StreetL+ "px",
'top' : 0 + "px",
'width' : StreetW + "px",
'height': WinSizeH + "px"
});
}
// humanクラス
var Human = Class.create();
// クラスの中身
Human.prototype = {
initialize: function(obj,x,y,vx,vy) { // コンストラクタ(ここでメンバ変数も指定)
this.obj = obj;
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
Element.setStyle(this.obj, // 実際の移動 / 体裁整え
{
'left': x + "px",
'top' : y + "px",
'font-size': size + "px",
'line-height': size + "px"
});
},
move: function() { // 移動の記述
if(this.y > WinSizeH || this.y < 0) { // 下から出たら
var left = StreetL + Math.floor(StreetW * Math.random()); // 開始位置指定
var tb = this.vy / Math.abs(this.vy); // 進んでた向き
var vx0 = Math.floor(vx * Math.random()); // 初速度
var vy0 = tb * Math.floor(1 + vy * Math.random()); // 初速度
if(tb>0) this.y = 0; // 上に戻す
else this.y = WinSizeH; // 下に戻す
this.x = left; // 初期位置に持っていく
this.vx = vx0; // 初速度指定
this.vy = vy0; // 初速度指定
}
if(this.x < StreetL) { // 道の外に出たら
this.vx = -this.vx; // 速度逆
this.x += size;
} else if(this.x > StreetR) {
this.vx = -this.vx;
this.x -= size;
}
this.x += this.vx; // 進む
this.y += this.vy; // 進む
this.vx = 0; // 速度初期化
Element.setStyle(this.obj, // 実際の移動
{
'left': this.x + "px",
'top' : this.y + "px"
});
},
// --------- ここが衝突判定 ---------
// this: 自分 obj: 相手
conflict: function( obj ) {
// --------- ここから判断の記述 ---------
var tb = this.vy / Math.abs(this.vy);
var lx = (obj.x - this.x); // x方向距離
var ly = tb*(obj.y - this.y); // y方向距離
var lf = tb*((obj.y + 10*obj.vy) - (this.y + 10*this.vy)); // 10時間後の距離
if(
ly > 0 && // 後ろにいて
Math.abs(lx) < 3*size && // かつ3人分の幅の中に入っていて
lf < 0 // 10時間後抜かしてるとき時
) {
var lr = this.x - obj.x; // 右か左かの符号用
if(!lr) lr = 0.5 - Math.random() // 真ん中だったらどっちか
lr = lr / Math.abs(lr); // 1か-1にする
var to = obj.x + lr*3*size; // 抜かす際の移動幅
this.vx += Math.floor((to - this.x)/3 + 0.5); // 3時間後に抜かす速度
}
// ------------- ここまで -------------
}
};
// 画面更新関数
function winDraw() {
for(i=0; i<num; i++) {
for(j=0; j<num; j++) {
if(i!=j) human[i].conflict(human[j]);
if(i!=j) human[i].conflict(human2[j]);
if(i!=j) human2[i].conflict(human[j]);
if(i!=j) human2[i].conflict(human2[j]);
}
}
for(i=0; i<num; i++) {
human[i].move();
human2[i].move();
}
}
// タイマー
function Timer() {
winDraw();
setTimeout("Timer()", t);
}
--></script>
</head>
<body>
<div id="area">
<div id="vertical" class="street"></div>
<script type="text/javascript">
for(i=0; i<num; i++) {
document.write('<div id="human_p' + i + '" class="human">●</div>');
document.write('<div id="human_m' + i + '" class="human human2">●</div>');
}
</script>
</div>
</body>
<script type="text/javascript">init();</script>
</html>
Introduction
凹みTipsはHTML、CSS、PHP、ActionScript3.0、JavaScriptからAjaxまで初心者の凹が試行錯誤繰り返しながら勉強していくサイトです。一緒に勉強していきましょう!
Categories
Recent Entries
Monthly Archives