drowing_1.wrl source
#VRML V2.0 utf8

Viewpoint {}

NavigationInfo {
type "EXAMINE"
}

# 箱
Group {
children [
Shape {
appearance Appearance {
texture DEF PT PixelTexture {}
material Material {
diffuseColor 1 1 1
}
}
geometry Box {
size 5 5 5
}
}
DEF ThS TouchSensor {}
]
}

# 線描画スクリプト
DEF Sc Script {
eventIn SFVec2f set_hitTexCoord
eventIn SFBool penUpDowned
eventOut SFImage image
field SFInt32 dt 128 # テクスチャの1辺のピクセル数
field MFInt32 ary [] # テクスチャーの色情報が納められている配列
field SFVec2f hitPoint 0 0 # ポインタが触れているテクスチャー座標
field SFVec2f startPoint 0 0 # 線分開始位置を示すテクスチャー座標
field SFInt32 penColor 0xFFFF00 # 線の色
field SFInt32 bgColor 0x0000FF # 背景の色
field SFBool penDownd FALSE # ポインタのボタンが押されているかどうか
field SFFloat segmentLength 0.02 # 線分の長さ(0以上1未満)
url "javascript:

// 整数で表された2色を rt : 1-rt の割合でブレンドした色(整数)を返す関数。
function blend (i0,i1,rt) {

var i0r = ( i0 & 0xFF0000 ) >> 16;
var i0g = ( i0 & 0x00FF00 ) >> 8;
var i0b = i0 & 0x0000FF;

var i1r = ( i1 & 0xFF0000 ) >> 16;
var i1g = ( i1 & 0x00FF00 ) >> 8;
var i1b = i1 & 0x0000FF;

var rt1 = 1 - rt;

var r = Math.floor( i0r * rt + i1r * rt1 ) << 16;
var g = Math.floor( i0g * rt + i1g * rt1 ) << 8;
var b = Math.floor( i0b * rt + i1b * rt1 );

return r + g + b;

}

// SFVec2fオブジェクトで示されたテクスチャー座標に penColor を適応させる関数。
function psetAA(v) {

var tv = v.multiply ( dt );
var s = Math.floor( tv.x );
var t = Math.floor( tv.y );

s = Math.max ( 0, Math.min( dt - 1, s ));
t = Math.max ( 0, Math.min( dt - 1, t ));

// 対象となるピクセルとそれを取り囲む8個のピクセルについて調べる。
for ( var y = -1; y <= 1; y++ ) {
for ( var x = -1; x <= 1; x++ ) {

var sx = s + x;
var ty = t + y;
if ( sx < 0 || sx >= dt ) continue;
if ( ty < 0 || ty >= dt ) continue;

// 調べるピクセルの中心位置を dc とする。
var dc = new SFVec2f ( sx + 0.5, ty + 0.5 );

// 点を打つ位置とピクセルの中心位置との距離から色の混合率 rate を決める。
// 点を打つ位置とピクセルの中心位置が同じならば rate は 1 となるが、距離が離れるに従い値は低くなる。
var rate = 1 - tv.subtract( dc ).length();

// rate が 0 以下ならばそのピクセルの色を変えない。
if ( rate <= 0 ) continue;

// 元からある色 baseColor と penColor を rate の割合で混ぜた色を適応させる。
var baseColor = ary[ sx + ty * dt ];
ary[ sx + ty * dt ] = blend ( penColor, baseColor, rate );

}
}

}

// SFVec2fオブジェクトで示された2点 v0 と v1 との間に線を描くための関数。
// テクスチャーの縦横のピクセル数は同じである必要がある。
function linesetAA(v0,v1) {

// v0 から v1 へのベクトルを v01 とする。
var v01 = v1.subtract( v0 );
// v0 と v1 との距離を v01len とする。
var v01len = v01.length();

if ( v01len == 0 ) {

// v0 と v1 との距離が 0 なら、v0 の位置に点を打つ。
psetAA ( v0 );

}
else {

// v0 と v1 との距離が 0 ではないなら、v0 から v1 まで0.5ピクセル間隔で点を打つ。

// v01 と同じ向きで、長さが0.5ピクセル分のベクトルを dv とする。
var dv = v01.normalize().divide( dt * 2 );

// v0 のコピーである v2 を新規生成する。
var v2 = new SFVec2f ( v0.x, v0.y );

// v0 と v1 との距離をdvの長さで割ることにより、描く点の個数 n を求める。
var n = v01len / dv.length();

// v2 の場所に点を描き、v2 をdv(0.5ピクセルの距離)だけ移動させる。
// それを n 回繰り返すことにより、v0 と v1 とを繋ぐ線を描く。
for ( var i = 0; i <= n; i++ ) {

psetAA ( v2 );
v2 = v2.add ( dv );

}

}

}

function initialize () {

var px = dt * dt;

// テクスチャー全体を bgColor で塗りつぶして、ary を初期化する。
for ( var i = 0; i < px; i++ ) ary[i] = bgColor;

image = new SFImage ( dt, dt, 3, ary );

}

function set_hitTexCoord (v) {

// キャンバス上のポインタが触れている座標を hitPoint にセットする。
hitPoint = v;

// キャンバス上でポインタのボタンが押されており、尚かつ始点からのカーソルの移動距離が
// segmentLength よりも長いならば、線分を描く。
if ( penDownd && v.subtract(startPoint).length() >= segmentLength ) {

linesetAA( startPoint, v );
image = new SFImage ( dt, dt, 3, ary );

// 新しい線分の始点として現在のポインタ座標をセットする。
startPoint = v;

}

}

function penUpDowned (active) {

// キャンバス上でポインタのボタンが押されたならば penDownd を true、
// 離されたたなら penDownd を false にセットする。
penDownd = active;

if (active) {

// キャンバス上でポインタのボタンが押されたら、ポインタの座標を線分の始点とする。
startPoint = hitPoint;

}
else {

// キャンバス上でポインタのボタンが離されたら、始点からポインタの座標までの線分を描く。
linesetAA( startPoint, hitPoint );
image = new SFImage ( dt, dt, 3, ary );

}

}

"
}

ROUTE Sc.image TO PT.image

ROUTE ThS.hitTexCoord_changed TO Sc.set_hitTexCoord
ROUTE ThS.isActive TO Sc.penUpDowned