〜canvasアナログ描画入門〜

ここではgetImageData()で得たImageの.dataをいじっていろいろ描画します

背景が白のテキストエリアのコードは改編して結果を見れます



〜注意!〜

※普通にcanvasの基本機能を使ったほうがはるかに速くて便利です。 どうしてもこの方法が必要な人以外はすみやかにお帰りください。

※コードは無駄があったり間違ってたりしますのでご了承ください。

※図でcanvasを使っています。IE9以上が必要です。無い人はchromeかfirefoxをダウンロードすれば見れます(無料)

※一度作った関数は使いまわしてるので順番に見てかないとわけわかんないかもです

※製作者も数式を完全には理解していません。「こうすれば何故かこう動いてくれる」程度の参考にしてください。



〜目次〜

クリックでとびます



























はじめに

canvas,ctxは配列にしています

番号は作った順なのでバラバラです

立ち上げが遅くなるので図は最初から表示されてはいません。



超基本

.dataは1ピクセルにつき4つのバイトデータをもつ一次元配列です。

赤、緑、青、透過度の順です。4つ目は複数のcanvasを重ねて使わない限り無意味です。

普通の配列で使えるメソッドはブラウザの種類やバージョンによって使えたり使えなかったりします

0〜255の整数値しか入れてはダメです。

lenghを超えていじると遅くなります



中心座標(tx,ty)と半径(han)を決め、forで縦横にすべてのピクセルを見て中心からの距離が半径以内なら色をつけます。

中心からの距離の出し方は (中心からのx座標の二乗+中心からのy座標の二乗)の0.5乗です。

↓ここのen0への引数の値を適当に変えてスタートを押してみてね
function en0(ime,tx,ty,han,c1,c2,c3)



このようにすべてのピクセルで計算して描画することを俺の脳内で勝手に

「アナログ」もしくは「全ピクセル計算」と呼んでいます 上の円はギザギザしていますね

そこでアンチエイリアス幅(er)を決め、中心からの距離がhan〜han+erなら

(han+er-距離)/er をアンチエイリアス度として

色×アンチエイリアス度 + 元の色×(1−アンチエイリアス度) を赤緑青でやります

function en1(ime,tx,ty,han,er,c1,c2,c3)
↑の「元の色と混ぜる処理」は長ったらしいので別に関数にしときます

function ant(ime,ban,c1,c2,c3,c4)

アンチエイリアス度が0以下と1以上の時はかけざんをしないように分岐しましょう 円描画時の値を 中心からの距離 から 半径からの距離 に変えることで輪っかになります。

ant()を使ってみます

function rin0(ime,tx,ty,han,haba,er,c1,c2,c3)
中心からの距離によって色1→色2→色1と折り返すグラデーションをかけます。

まず距離を適当に割って%2して値が1以上なら2-値します

その値×色1+(1-その値)×色2 を赤緑青でやります

これは線形補間と呼ばれています

function engra(ime,tx,ty,bai,i1,i2,i3,i4,i5,i6)
赤MAXで図形を描画し、赤の度合いに合わせてグラデーションをかければ

後付けでグラデーションをつけれます

当然図形以外は赤が0である必要があります

function gra0(ime,tx,ty,bai,i1,i2,i3,i4,i5,i6)
縦または横につぶれた楕円はty-aまたはtx-bに一定の数をかければできます。

それを回転させれば傾いた楕円になります。

回転後の中心からのx=(b-tx)×cos(角)+(a-ty)×sin(角)
回転後の中心からのy=(b-tx)×sin(角)−(a-ty)×cos(角)

で回転できます

サインコサインを使う際は角度を0〜360から0〜3.14159..*2(ラジアン)に直す必要があります

function en2(ime,tx,ty,han,kaku,tubu,er,c1,c2,c3)
中心からの距離に応じて回転角を増加させます

逐一半径と角度を求め sin(角+回転)*距離 cos(角+回転)*距離 でx,yを求めています

たぶんかなりの無駄があります

function en3(ime,tx,ty,han,tubu,soku,er,c1,c2,c3)
別の呼び名で紹介していましたが正しくは干渉線だとご指摘がありました。ありがとうございました。

グラデーションを密にすると値が同じじゃない場所に線が浮かびあがってきます。

下の例では 1÷半径からの距離 を値に足すことで半径近辺の値が馬鹿高くなってます。

function en4(ime,tx,ty,han,bai,i1,i2,i3,i4,i5,i6)
2色グラデーションでは値が0〜2で一周するグラデーションがかかっています。

そこで縦横を見るループ内でimageの配列をループさせ値に2/rituを加えていき色を決定すれば

値を決定するまでの計算が他のフレームで大幅に浮きます

ismove、count、timerはグローバル変数です

function endou(imehai,tx,ty,han,bai,i1,i2,i3,i4,i5,i6)

move()


〜このページ全体での動画の流れ〜

実際のmove()の中身はこうなってます

function move(){
clearTimeout(timer);
if(ismove==0)return;
try{
〜テキストエリアの中身の処理〜
}catch(e){
alert("実行時エラーです\n\n"+e); ismove=0;
};
}

もっかいスタートを押すかエラーを起こすとismoveが0になって止まります めんどいんで別に関数を作っておきます

値と色の配列を渡すと赤緑青の配列を返すようにします

色の数をNとして値を0〜Nの範囲に調整し、

色1をMath.floor(値)、 色2を色1+1、a=値%1 として線形補間します

function gra1(atai,col,typ)

まだ色に何か処理するかもしれないのでMath.floorしてません

typを1にすると色の変化がなめらかになります

Math.cos(0〜π)で1〜-1の間をゆるやかにカーブするからです

これをコサイン補間といいます 上のgra1を使ってしましまを作ってみます

縦横にsin(角)cos(角)の好きな方をかけて縦-横をgra1に渡します

function sen0(ime,tx,ty,kaku,col,bai)
ブレゼンハム線と呼ばれるすんばらしいアルゴリズムを使います。

横の増加量をr1縦の増加量をr2としてr1>r2の時、

横に一個ずつ進みながらbにr2をプラスし、r1を超えたらr1を引いて縦に一個進む を繰り返すとなぜか目的地に到達します

function sen0(ime,sx,sy,ex,ey,c1,c2,c3)
上の線はギザギザしてますね

そこでbの値によって横のマスにアンチエイリアスをかけてみます

斜めの時と水平垂直の時でアンチエイリアスの幅が変わるので先にやっつけます

function sen1(ime,sx,sy,ex,ey,er,c1,c2,c3)

※端っこは正確に描画されません。

ハイクヲリティーな線描画は後述の疑似imageと点線を見てください 線を3等分し、中の点を線に直角にずらします。

これを再帰で繰り返してsen1で結びます




〜ポイント〜

putするのはctx[10]で、その裏に隠れているctx[11]からgetしています。

これによりfillRect()する時間が短縮できます。

ループ用関数の中身


function ina(ime,sx,sy,ex,ey,hen,kai,er,c1,c2,c3)
ブレゼンハム円とよばれるすんばらしいアルゴリズムを使います

中心の上から中心の右へ右回りに描画する場合、次に描画する点は右か下です

なので右と下の点で半径に近いほうを選んで進んでいきます

中心の右を過ぎたら今度は下と左で見ていきます。これを繰り返します

jikan()を入れるとそこまでの処理時間が表示されます
rin0と比べてみてください

function enko0(ime,tx,ty,han,c1,c2,c3)

※1/8だけ描画して折り返していけばさらに高速になります 点1から点3まで点2に引っ張られながら線を描きます

整数の足し引きとビットシフトだけで描画できます

原理はここを見てね
http://studio-rain.cocolog-nifty.com/blog/2008/07/post_c4f5.html

ちなみに俺は理解できませんでした

※大きいcanvasだと値が馬鹿高くなってうまく描画できない場合があります

function bej0(ime,x1,y1,x2,y2,x3,y3,seido,c1,c2,c3)
ベジェ曲線にブレゼンハム円を組み合わせればぶっとくなります

function bej1(ime,x1,y1,x2,y2,x3,y3,seido,haba,c1,c2,c3)

※ブレゼンハム時に縦、横、斜めをみるとピクセル数が少なくなり高速になります
※円データをシングルトンにして直線等にも応用すると便利です 配列で点をわたすとすべての中点をbej1でやんわりつなぎます

function renbej0(ime,retu,seido,haba,c1,c2,c3)
連ベジェの輪っか版です

function wabej0(ime,retu,seido,haba,c1,c2,c3)
残念ながら俺の頭ではアンチエイリアスつきのベジェ曲線はできませんでした。

しかしImageを大きくとってから小さくすればどんな図形でもぼやかすことができます。

下の例では2×2ピクセルの平均を1ピクセルに入れています

function syk0(ime,bai) 1/baiにしたimageを返す
小さくしなくても周囲の値によってぼやかすことはできます

しかし汚いです

より正確なぼかし方は後述のガウスぼかしを見てください

function boya0(ime,bai)

同じサイズのimage.dataを作ってそっちに結果を入れています

そうしないと最初ピクセルの変更を最後までひきずります ランダムに色を決定すればホワイトノイズになります

乱数にrandom()では無く線形合同法というものを使ってみます

seed=(641*seed+3313)%131072;

これを131072回行うとseedの値に0〜131071が一回ずつ出現し元に戻ります

つまり最初のseedの値によって同じ乱数が得られるわけです

641,3313,131072の数値は別に他の組み合わせでもいいです

他の組み合わせの探し方は ググって ください

function pnoise0(yoko,tate,seed)
tate×yokoの二次元配列に乱数を入れ
グローバル変数rantankに格納


function beta0(ime,i1,i2,i3,i4,i5,i6)
乱数に合わせて2色グラ

このままでは特に使い道はありません 垂線からの角度を出し、

中心からの距離×cos(垂線からの角度)<一定の値 なら描画させます

一辺しかアンチエイリアスがかからずこのままでは使い道はないです。

function sankaku0(ime,tx,ty,han,er,kaku,toga,c1,c2,c3)

※〜角度Aと角度Bの成す角の出し方〜(単位:ラジアン)

C=AーB まず普通に引きます

C=C%(π×2) A、Bの値によっては一周以上するので%(π×2)します

if(C<0)C+=π×2 0〜π×2の範囲にします

if(C>π)C=π×2ーC 180度以上してたら逆から見たほうが近いのでπ×2から引きます 二等辺三角形がn個あると考えます。

絶対値(角+回転)%(π×2÷n)−π÷nで二等辺三角形の垂線からの角度が出ます。

あとは二等辺三角形の描画と考え方は同じです

function nkakkei(ime,tx,ty,N,han,kaku,er,c1,c2,c3)
正n角形を一行変えるだけでn芒星になります。

function nbousei(ime,tx,ty,N,han,kaku,er,c1,c2,c3)
この手法の正式な名称は知りません

上のn芒星と正n角形はほとんど同じコードですね

テキストエリアを使って上下に分け、追加したいコードを挟んでnew Function()すれば

似たような関数を動的に作れます



おなじみ正n


n芒星


cosで花びらに


sinでとがる




function sand(bun,ue,sita)
"pan"ue+bun+"pan"sitaで関数を作って返す


function ???(ime,tx,ty,N,han,bai,kaku,er,i1,i2,i3,i4,i5,i6)
(二色グラにした以外は正n角形とほぼ同じ)
テキストエリア id='pan0'

テキストエリア id='pan1'

ifで済む話ですが何万回もifを通すのが嫌な人用

※必ず ” ; ” を入れてね! 正nに何枚目かによって処理を加えると爆発っぽくなります

上のサンドイッチを何枚目かを計算するように変えて引数を増やします

配列tankに0〜1の疑似乱数を入れて何枚目かによって参照します


基本


sinでちょっと細くなる


輪っか


中心ほど値の差をすくなくすると干渉線が出にくい




テキストエリア id='pan3'

〜ポイント〜

引数seedの値によって同じ形を生成します

最後の(1-Math.abs(e/kaku2))は

とげの境界で0になるのできれいにつながります

ランダムな幅の光をランダムな間隔で放射状に描画してみます。

光の本数×2の配列を二つ作り一方に疑似乱数を格納しその合計を求めます。

2×π/合計を配列内にかけ、別の配列のそれぞれに前までの合計を入れます

後は気合で現在位置が何本目かがわかります

function gok0(ime,tx,ty,hon,seed,c1,c2,c3)
後光に中心からの距離による処理をするとヒットスパークっぽくなります

function gok1(ime,tx,ty,hon,seed,han,
haba1,haba2,yose,c1,c2,c3)
なんとアンチエイリアスつきの普通の三角形の描画は爆発よりめんどいです

cos(垂線からの角度)×点からの距離÷垂線の長さ を3点でやり一番小さい値によって描画します

function sankaku1(ime,x1,y1,x2,y2,x3,y3,er,c1,c2,c3)


アンチエイリアス幅がでかいと細くした時に不自然になります

正確に描画するには角でアンチエイリアスを丸くする処理が必要です

これは普通にcanvasの機能で線ひいて塗りつぶしたほうがいいですね

円の描画時に別の円の半径内を描画しないようにすれば三日月型になります

function en7(ime,tx,ty,han,tx2,ty2,han2,er,c1,c2,c3)
ピンボールのはじくやつのことです

二つの円と台形で作ります

台形は回転させてx軸かy軸と平行にすればわかりやすいです

function en8(ime,tx,ty,han,han2,kaku,naga,er,c1,c2,c3)
以下の式で特殊な形が生成できますが特に使い道がないです

x,yは中心からの値を画面サイズの半分で割ったものです

|x|^(2/3)+|y|^(2/3) アステロイド
(xx+yy)^2-2xy 二葉線
x(3yy-xx)-(yy+xx)^2 三葉線
4xxyy-(xx+yy)^3 四葉線
xxx+yyy-3xy 正葉線

最後に画面サイズの半分をかけます

後述のリサージュ、バラ曲線で代用できるのでそちらを参照してください

木目、かべのしみ、水面などは平面上をなだらかに変化する乱数によって表されます

何回かに分けてその作り方を見ていきましょう

その1:でかくする

ホワイトノイズをでかくしてみます

function pnoise1(yoko,tate,seed,ss,es,wari)





その2:なめらかにする

グローバルに入れる時に位置によって隣のマスを見ながら値を変えます

範囲外は反対側を見ます

例えば x=6.6、y=4.8 の時、

x2=1.5-xの少数部分、y2=1.5-yの小数部分、x1=1-x2、y1=1-y2 として

[6][4]*x2*y2 + [6][5]*x2*y1 + [7][4]*x1*y2 + [7][5]*x1*y1 にします(バイリニア補間)

(小数点以下が0.5以下の時は 0.5+小数部分 を出して方向が逆になります)

さらに x2,y2を出す時にコサイン補間をかけるとさらになめらかになるそうですが

俺の頭では平面にコサイン補間をかけるのは無理でした

function pnoise2(yoko,tate,seed,ss)




その3:さらになめらかにする

octを隣のマスを見ながら値を変化させとけばさらになめらかになるそうです

隣を1/8、斜め隣を1/16、自分を1/4した値を別の二次元配列に入れます(ガウスぼかし)

範囲外は反対側を見ます

それと後々のために直接グローバルにいれずにローカルの二次元配列をreturnするようにします

function pnoise3(yoko,tate,seed,ss)





その4:フラクタル化

pnoise3で作った配列にその半分のマスで作ったものを加算、

さらに半分のものを・・と繰り返します

これで完成です

※一周させる都合上サイズが設定よりでかくなっております

function pnoise4(yoko,tate,seed,ss,es,bure,nou,yori)

加算していく時に倍率をbure倍していきます

2倍で4回加算した時は最後に(1+2+4+8)で割ります

なめらかにする処理で値が0.5によっているので1、0に近づける処理をします

その際ついでに濃淡の偏りも変えれるようにしてますが0.5固定でいいです

最後に製作途中の三次元配列をreturnしているのでこれを倍率を変えて加算して

別のノイズに利用すると便利です パーリンノイズ(以降ノイズと表記)を取得する際に範囲外だったら一周した位置を取得する関数を作っときます

function ranput0(x,y)
しましまの値にノイズを足してみます

function moja0(ime,kaku,bai,rr,nou,yori,i1,i2,i3,i4,i5,i6)

ノイズは80*80で作っていますが一周しているので200*200のimageでも大丈夫なのです ノイズをそのまま利用して雲を描画します

一定の値以下は描画しないようにし、

描画するとことの境はアンチエイリアスします

描画するとこは境〜1が0〜1になるように直して線形補間します

function beta1(ime,lim,gen,i1,i2,i3,i4,i5,i6)
今まではピクセルとノイズを1対1で対応させて見てましたが

多くの場合ノイズを縮めたり歪めた状態で値を取得します

その際小数点の位置のノイズを参照することになるので

バイリニア補完っぽいことをするようにranput0()を改造します

function ranput1(x,y)

注:周囲4ピクセルを見て色を決めるため4倍以上圧縮されてる箇所は精度が落ちます 雲1を縮めて描画します

ranput1()で値を取得します

function beta2(ime,tx,ty,baix,atuy,lim,gen,kaku,i1,i2,i3,i4,i5,i6)
円にノイズを合わせると島っぽくなります

ノイズそのままでも島っぽいですが円と合わせることで

一定以内が確実に陸地になり一定以上が確実に海になるので都合がよくなります

function sima0(ime,tx,ty,han,bai,rr,er,i1,i2,i3,i4,i5,i6)

半径に近いとこで遠くのノイズを参照します

陸と雲でシード値を変えましょう

function beta3(ime,tx,ty,rtx,rty,han,bai,er,lim,gen,i1,i2,i3,i4,i5,i6)
ノイズの座標を変化させて地球を回します

move()
n個下のノイズから順次みていき、値+(n-距離/n)>1だった場合

そこのノイズを参照することででっぱります

function beta4(ime,rtx,rty,nobi,bai,i1,i2,i3,i4,i5,i6)
雲1みたく小腸の一定以下を描画しないようにします

隣のノイズと比べて値によって色を小さくして影をつけます

function beta5(ime,rtx,rty,nobi,bai,lim,gen,ei,i1,i2,i3,i4,i5,i6)

でっぱったとこと後ろとのアンチエイリアスがまだ不完全です

影は単純に色を小さくしてますが、255ー(255ー色)×倍率 のほうがそれっぽくなります

乱数をぼやかしながら上にあげてくと炎っぽくなります

まず乱数を入れる二次元配列をimageに合わせて作り一番下の段に線形合同法で乱数を入れます

ループで下から見ながら(左下+下+右下+二個下)/4を入れていきます

(最初は二個下がないので下3つ÷3を入れます)

はじっこは反対側を見ます

最後に乱数の値+ノイズ*ノイズ倍率に合わせて 黒→赤→黄→白 でグラデーションをかけます

下段が荒いので乱数の配列を縦に余分に長く作り隠します

縦長になるので4より少し大きめで割るか黒の比率を多くします

ノイズをつけれるようにしてますが無しのほうがいいです

function honoo0(ime,seed,yowa,col,bai,rr,rtx,rty)
乱数の二次元配列の最下段を少しずつ乱数で変化させ、

何度も全体を上へあげればゆらめきます

重いです

普通の炎にノイズを強くかけたほうがいいかもです

function honoo1(ime,seed,yowa,col,bai,rr,rtx,rty,ritu)
honoo1は最後に乱数の配列をreturnしているのでこれをグローバルhotankにとっといて

下段を変えてぼやかしながら全体を1マスずつあげて描画します

これだけだと遅いのでノイズのy座標も変えます



function honoo2(ime,seed,yowa,col,bai,rr,rtx,rty,ritu)
炎を丸めて描画したら太陽っぽくなるかと思ったらそんなことありませんでした

honoo1で炎を作って半径を縦、角度を横に補正してranput1でhotankを参照します

一度imeに炎が描きこまれるのでかなり無駄です

function honoo3(ime,tx,ty,han1,han2,er,col)
x,yを同じ計算を繰り返して変化させ続けると次のいずれかになります

@0になる

A無限になる

B一定のルートを巡回し続ける

Cそれ以外

このCの状態でx,yの位置に点を打っていくとカオス図形というものができます

ただしPCは小数点以下が無限ではないので

本来カオスになるはずが同じとこに戻ってBになる可能性はあります



カオス描画用に点を打つ関数を作っときます

function ten0(ime,x,y,ki,c1,c2,c3)

次のx=y-sign(x)*√(|t1*x-t2|)
次のy=t3-x

で点を打つとホッパロンになります

計算回数で色分けしてます

sign()は0以下で-1、0以上で1、0で0を返します

注:xの計算結果は変数に入れといて後でxに入れないといきなりyに影響が出ちゃうよ

function kao0(ime,tx,ty,han,max,ki,t1,t2,t3)
ホッパロンはt1,t2,t3の値のわずかな変化によって激しく変化します

その様子を見てみましょう

止めてからスライダーで値をチェックできます

ループ用関数の中身

gtとgrはグローバル変数です 定数を4つにして計算式を

変数=t1*(x*x+y*y)+t2*x*(x*x-3*y*y)+t3
次のx=変数*x+t4*(x*x-y*y)
次のy=変数*y-2*t4*x*y

にするとチョ(ryになります

function kao1(ime,tx,ty,han,max,t1,t2,t3,t4)

※定数が限られた範囲の組み合わせでないとすぐに発散(値がどんどんでかくなり描画できなくなる)してしまいます

この「限られた範囲の組み合わせ」は連続して存在しないので偶然見つけるのが難しいです 次のx=y+t1*x+5*x*x/(1+x*x)+1-0.2*exp(-y*y)
次のy=-t2*x+t3*pow(-1,計算回数)

で鳥っぽくなります

exp(n)は自然対数のn乗です

function kao2(ime,tx,ty,x,y,han,max,ki,t1,t2,t3)

これもt1、t2、t3が限られた範囲の組み合わせじゃないとすぐに発散します

2匹の鳥が見えますが、回数%2が0の時だけ描画すると1匹になります 次のx=t1*x+t2*y+t3+t4/(1+x*x)
次のy=-x

でも鳥っぽくなります

function kao6(ime,tx,ty,x,y,han,max,ki,t1,t2,t3,t4)
形を成すのは一瞬です

ここで残念なお知らせがあります。

お気づきの方もいらっしゃるでしょうが、何度も同じピクセルをいじる時

その都度少数点を丸めているので正確な描画がなされていません。

何度も同じピクセルをいじる点描画だけこれを改善してみます。



まず少数点が使えるようにimage.dataを渡すと同じサイズの通常配列をつくり

データを移してオブジェクトに格納しwidthとheightもつけた疑似imageを返す関数を作ります

function utusi(ime)




そして疑似imageからimageに戻す関数を作ります

function modosi(ime,gj,ki)




そして少数点を丸めず範囲も気にしない点描画を作ります

ついでにバイリニアの要領で周囲4マスに光を振り分けます

あと色の合成を単純な加算にしました

function ten1(ime,x,y,c1,c2,c3)

従来の方法だと1以下は加算されませんがこれだと加算されます

光度は点を打つ時は考慮せずimageに戻す時にかけます


※最近のブラウザだと最後にMath.floorすればdataに小数点を入れてもエラーにならないようです x,y,zの点に対して

次のx=t1*(y-x)
次のy=t2*x-y-x*z
次のz=x*y-t3*z

で二点を回るカオスになります

ten1を使ってみます

function kao2(ime,tx,ty,x,y,han,max,ki,t1,t2,t3)

ロンゲクッタ法というものを使うと高精度になりますがコードが大変なことになります 上のローレンツアトラクタは点が密なとこと荒いとこでかなり差が出てしまいます。

これは点と点の間を点線で結ぶことで一様にすることができます。

グローバルを使って余りを調整すれば頭と前の線のけつが重なりません

function tensen0(ime,sx,sy,ex,ey,kan,c1,c2,c3)

※値がとびとびになるカオスには使えません では点線の威力を見てみましょう

引数に刻み幅を追加
function kao5(ime,tx,ty,x,y,han,max,kan,ki,t1,t2,t3)
zx,zyを変化させて動かしてみましょう

globalAlphaで薄めてgetImagedataすることで残像がつきます

move()
次のx=x*(t1-y)
次のy=x*x
パラメーター1個のこの式でナイスなカオスが出ます

ただし模様になるような値の範囲は狭いです

前の点からの距離で色分けしてます

function kao4(ime,tx,ty,x,y,han,max,ki,t1)
t1を1.8から2まで動かします

move()
漠然としてますが正式な名称です

次のx=?*x+?*y+?
次のy=?*y+?*y+?

これを4種類用意し、ひとつを原点近くに戻るようにして確率で選びながらx,yの点に描画していくと

3つに分岐していくフラクタルになります

原理は簡単ですが引数がすごい数になります

function kao6(ime,tx,ty,han,max,ki,c1,c2,c3,t0,t1,
t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,
t14,t15,t16,t17,t18,t19,t20,t21,t22)
式に使う27個の引数を変化させてみます

止めてからスライダーで値をチェックできます

move()








次のx=t0*x-t1*y+t2*x+t3*y
次のy=t1*x+t0*y+t3*x-t2*y



次のx=t4*(x-1)-t5*y+t6*(x-1)+t7*y+1
次のy=t5*(x-1)+t4*y+t7*(x-1)-t6*y

を同時に行い点を打っていきます

つまり一回ごとに計算量が2倍になっていきます

再帰だと重くなるのでだんだんでかくなる二次元配列を使ってループします



function kao8(ime,tx,ty,han,max,ki,c1,c2,c3,
t0,t1,t2,t3,t4,t5,t6,t6,t8)
引数8個をランダムに変化させます

これはマジでかっこいいです

ゲームのエフェクト等に使えます

move()
次のx=sin(t1*y)+t3*cos(t1*x)
次のy=sin(t2*x)+t4*cos(t2*y)

できれいな線が出ます

N個前の点との角度で色付けします

重ねても色が混ざりにくいので光度の低い点を大量に打てば打つほどきれいな画像ができます

function kao9(ime,tx,ty,han,max,ki,N,t1,t2,t3,t4)

ためしにmaxの桁を上げてkiの桁を下げてみてください

やればやるほどきれいになります

ただしPCは小数点以下が無限ではないのでやりすぎると精度が落ちます

画像をでかくとって縮小させれば無限に精度をあげることはできます 次のx=sin(t1*y)-cos(t2*x)
次のy=sin(t3*x)-cos(t4*y)

クリフォードとほぼ同じです

function kao10(ime,tx,ty,han,max,ki,N,t1,t2,t3,t4)

まだ最適化の余地があります

クリフォードやpeta de jongで時間をかけて緻密な描画をしたい人は

工夫してみてください 密集度と光度があってないと汚くなるのでランダムな動画には向いてません

move()

kao9のとこをkao10に変えるとpeta de jongになります 次のわけわからん式から生成されます

前の点との角度で色づけします

xx=-4*x+cos(kaku*t1)
yy=-4*y+sin(kaku*t1)
r=√√(xx*xx+yy*yy)
kaku=atan2(yy,xx)
if(Math.random()>0.5){c=1}else{c=-1}

として

次のx=-cos(t2*kaku)-r*cos(kaku/2)/2*c
次のy=-sin(t2*kaku)+r*sin(kaku/2)/2*c

function kao11(ime,seed,kai,bai,c1,c2,c3)
変数1=t1*x+2*(1-t1)*x*x/(1+x*x)
次のx=y+t2*(1-t3*y*y)*y+変数1
変数2=t1*次のx+2*(1-t1)*次のx*次のx/(1+次のx*次のx)
次のy=-x+変数2

から生成されます

今までと少し違うのはxの変化がそのターンのyに影響してます

変数2は次のターンの変数1と同じなのでうまく計算を減らせます

初期値に左右されるのでx,yも引数で変えれるようにします

function kao12(ime,tx,ty,han,max,ki,col,x,y,t1,t2,t3)
x,yの初期値とパラメーター3つをランダムに変化させます

実はカオスの形状には複素数というものが大きく関わっています

この複素数とは二つのパラメーターを持ち、足し算掛け算に変な法則があります

その性質が平面状に模様となって現れているようです

x=〜、y=〜という式は複素数の計算をむりやりコードに置き換えたものです

複素数クラスと複素数用の演算関数を作れば 超複雑な式も簡単な表記で再現できます

詳しくは ググって ください 適当にいじってたらたまたまタコっぽくなりました

式はコード参照

function kao13(ime,tx,ty,han,max,ki,t1,t2,t3,t4)

0で除算しない処理をしています

sign2は負の時-1、正の時1を返します 適当に作ってたらサインコサインを使わずにクリフォードっぽいのができました

ひょっとしたらすごいかもしれないので俺(通称じゃがりきん)の名をつけました




〜かっこよかった値の例〜
0.5, -0.5, -1.4, -1.3
0.92, -0.7, -1.2, -1
0.06, -0.8, -0.6, -0.4
2.3, -0.4, 4.5, 2.75
0.8, -1.5, -1.2, -1.4
0.6, 1.68, -1.38, 1
0.7, -0.94, -4.5, -1
1.38, -0.8, -2.6, -1.3
1.668, 0.5, -3.6, 1.6
0.55, 1.5, -0.5, 0.4 植物
0.8, -1.5, 4.6, 3.2 鳥
1.7, -1.3, -4.6, -1.3 鳥2
0.4, -0.4, 0.05, 0.4 深海魚
0.2, -0.6, -0.07, 0 クラインの壷
0.1, -0.7, -0.4, -0.9 胎児
0.7, -0.01, -1, 1.15 二匹の天使
0.57, -0.71, -2.64, -1.78 二匹の宇宙人
1.3, 0.8, -3.7, 1.9 ワッフル
0.5, 0.07, -0.7, -1.7 天狗
1.7, -0.15, -1.33, -1.33 鎖鎌を振り回す人
2.1, -0.6, 4.4, 2.6 ウルトラマン対怪獣


function kao14(ime,tx,ty,han,max,ki,col,t1,t2,t3,t4)

性質上xが常に右に1寄っているので最初にtxからhanを引いときます

0で除算しない処理をしていますが形状にこれが影響している場合があります パラメーターを-2〜2の範囲でランダムに変化させてみます


kao14をkao15に変えれば↓の宇宙アトラクタの動画になります 円形のアトラクタは意外と簡単に作れることが判明しました

計算の最後にxとyを 0.1+x*x+y*y で割るだけです

途中結果がでかいほど小さくなるので本来発散する式でもOKです

この宇宙アトラクタ(命名:俺)もループの中でループして適当に計算しまくってますが一定範囲に収まります

0.1の部分や反復回数を変えれるようにしてもよいです

function kao15(ime,tx,ty,han,max,ki,col,t1,t2,t3,t4)
縦横に見ながらそれぞれのピクセルで

次のx=rr*x*(1-x) を指定回数計算します

ただしrrはあらかじめ配列に入れておいた疑似乱数tank[n]が0ならrx,1ならryを入れます

rx,ryは縦横に連動した0〜4までの数じゃないと不自然になります

最後のxの値で色を割り振ります

function ria0(ime,seed,kai,bai,c1,c2,c3)

本来は最後のxの値からリアプノフ指数というのを計算するのですが

よくわからないのでそのまま使いました rx,ryを座標に連動させながら

次のx=x^2-y^2+rx、次のy=2xy+ry を無限に繰り返してx^2+y^2が4を超した時の回数を色分けすると模様ができます

x,yの初期値はその都度0にします




〜canvas内をクリックするとその位置を拡大します〜

 倍率: MAX:  C1: C2:  C3:



座標に連動したジュリア集合(後述)も表示します



function mandel0(ime,tx,ty,zoom,max,c1,c2,c3)

〜ポイント〜

maxをc1,c2,c3の公倍数にすると最大値が黒になります

メインの計算でx、yをその都度絶対値にすると

バーニングシップというフラクタルになりますが

細部が汚くあまり使えません あれをやってみます

最初に少し時間がかかります

うまくいかないときはmaisuuかmzoomを下げてね

ループ用関数move()の中身


マンデルブロさんは巨大ですね マンデルブロのrx,ryを固定にし、x,yの初期値を縦横にあわせるとこうなります

形状がマンデルブロの座標と連動しています

function mandel3(ime,tx,ty,rx,ry,zoom,max,sai,bai,c1,c2,c3)
回数が最大の時、値を変えて再計算するとこうなりました

細部は変わらないので重いだけであまり使えません

function mandel1(ime,tx,ty,zoom,max,sai,bai,c1,c2,c3)
マンデルブロの式は複素数で書くと

次の複(x,y)=複(x,y)^2+複(rx,ry) ですが

この2乗のとこを変えるとマンデルブロさんの頭が増えます

function mandel2(ime,kai,max,size)
マンデルブロのrx、ryをランダムに設定し、

計算して最後まで4を超えなかった場合に計算途中のx,yの点すべてに点を描画すると仏陀が出ます

kaiとmaxによって微妙に形が変わります

function mandel2(ime,kai,max,size)

常に全体を描画してしまうためマンデルブロのようなズームはできません ググったら複素数どうしの計算がなんとなくわかったので書いときます

一つ目のパラメーターをre、二つ目のパラメーターをimとします

・足し算引き算

複(a,b)+複(c,d)=複(a+c, b+d)
複(a,b)−複(c,d)=複(a-c, b-d)


中のreどうし、imどうしを足し引きするだけですね

・かけ算

中身を掛け合わせるわけですが、

reとreをかけたらreになる reとimをかけたらimになる imとimをかけたらプラマイを逆にしたreになる

という性質があるためこうなります

複(a,b)×複(c,d)=複(ac-bd, ad+bc)

・割り算

かけ算がわかれば割り算も出せます

複(a,b)÷複(c,d)=複(x,y) → 複(a,b)=複(c,d)×複(x,y) → 複(a,b)=複(cx-dy, dx+cy)

つまりa=cx-dy、b=dx+cyなのでこれを連立方程式でx=〜、y=〜になるまでがんばると

複(a,b)÷複(c,d)=複( (ac+db)/(cc+dd), (b-dx)/c )

となります(imのxの部分はreの(ac+db)/(cc+dd)のこと)

・平方根

かけ算がわかってるので√複素数も出せます

√(複(a,b))=複(x,y) → 複(a,b)=複(x,y)×複(x,y) → 複(a,b)=複(xx-yy,2xy)

つまり a=xx-yy、b=2xy なのでここからがんばると 4xxxx-4axx-b=0 の形が出せます

これはxxをxとおいた二次方程式と捉えると公式から解けて

√(複(a,b))=複( ±√((a±√(aa+bb))/2) , b/2x )

となります(imのxの部分はreの±√((a±√(aa+bb))/2)のこと)

内側の±は数学的にどっちでも同じことですがマイナスだとプログラム的にエラーになるのでプラスにします

外側の±はどっちでもいいです。aの符号に合わせるのがよいでしょう


違ったらごめん ニュートン法とは方程式の解をとく方法の名前であり、

これはそれを使ったフラクタルであってこれ自体の名称は特にないようです

ジュリア集合的に縦横を複素数ととらえ

次の複=(複^3−1)/(3×複^2)

を計算していきx,yが一定以下になる回数によって色わけします

function mandel5(ime,tx,ty,zoom,max,c1,c2,c3)
ジェネレーターとは線分をつなげたものです

各線分に相似なジェネレーターをはめこんでいくとフラクタルになります

有名なコッホ曲線とかC曲線とかいうやつです

ただコッホとかCは特定の形のジェネレーターの時にできるフラクタルを指す言葉で

この手法自体の正式な呼び名は無いようです

前述のイナズマも毎回ランダムなジェネレーターになるこれの一種です

function jen0(ime,retu,kai,kan,ki,c1,c2,c3)

だんだんでかくなる配列を使ってループさせています

普通に再帰を使えばこの1/3ぐらいのコードでできます

tensen0が前の余りによってスタートを調整するので、

つなぐ時に順番につなぐと精度があがります

これはおそらくcanvasの機能で線を引く時も同じです ジェネレーターを特定の線分の位置で逆にはめこんでできたものはドラゴン曲線と呼ばれています

function jen1(ime,retu,kai,kan,ki,c1,c2,c3,htank)
ジェネレーターの中点を動かしてみます

move()
半径と角速度(ラジアンに追加する値)の異なる円軌道を複数加算して点を打ってくとサイクロイドになります

function sai0(ime,tx,ty,han,han2,han3,max,ki,col,t1,t2,t3)

正確にはスピログラフと呼ぶそうです

スピログラフは商標なのでもっと正確にはトコロイドもしくはハイポロイドと呼ぶそうです

ただどれも半径によって角速度が決まるのでこれは厳密にはどれでもありません 頻繁に見かける手法ですが正式な名称は知りません

サイクロイドの描画を前の点との線にしただけです

function sai0(ime,tx,ty,han,han2,han3,max,ki,col,t1,t2,t3)


色わけがくっきり出てしまうので単色のほうがいいです

サイクロイドより少ない計算で密に描画できます 線の距離によって密度が変わるので変化を狭い範囲にしています

サイクロイドは円を重ねた式でしたが

これをサインコサインを使った適当な式にするとリサージュと呼ばれる図形になります

というかサイクロイドがリサージュの一種のようなもんです

確実にいつか元の位置に戻るのでカオスではないです

基本的な式は

x=sin(rad)
y=sin(rad2)

ですがこれでは面白みがないので

x=(cos(rad)+cos(rad2))/((cos(rad3)+sin(rad2))^2+0.5)
y=(sin(rad)+sin(rad2))/((sin(rad3)+cos(rad2))^2+0.5)

(radに対応した半径の比率をかける)
でやってみます

function sai2(ime,tx,ty,han,han2,han3,max,ki,col,t1,t2,t3)


〜かっこよかった値〜

-0.397, 0.233, 0.935

0.339, 0.791, 0.982

-0.282, 0.445, 0.564 玉座

0.158, 0.461, 0.606 玉座2

0.174, 0.354, 0.522 メビウスの輪

0.026, -0.359, -0.104 便座

バラ曲線とは

x=sin(rad2)*cos(rad)
y=sin(rad2)*sin(rad)

で描画される円形にした正弦波です
前述のn葉線もこれで描けます

これだけでは面白みがないのでsai2をサンドイッチにしていろいろなスーパーバラ曲線(命名:俺)を作ってみます












































textarea id='pan4'


textarea id='pan5'


〜考え方〜

基本的なバラ曲線だと真ん中が濃くなるので

b=sin(rad2)*han2+1
x=b*cos(rad)
y=b*sin(rad)

と波形に1以下をかけ+1して浮かせます そして

b=sin(rad2)*han2+1
x=b*cos(rad)+cos(rad3)*han3
y=b*sin(rad)+sin(rad3)*han3

とrad3の円軌道を足すと線がぶっとくなります さらに

b=sin(rad2)*han2+1
x=b*cos(rad)+cos(rad3)*han3*sin(rad2)
y=b*sin(rad)+sin(rad3)*han3*sin(rad2)

小さい円にsin(rad2)をかけることで
小さい円の大きさが波の真ん中で0になります
cosにすると波の上下で0になります

大きい円のradに

b=sin(rad2)*han2+1
x=b*cos(rad+sin(rad2*?))+cos(rad3)*han3*sin(rad2)
y=b*sin(rad+sin(rad2*?))+sin(rad3)*han3*sin(rad2)

のようにsin(rad2)を足すと螺旋状になります
cos(rad2)を足すと波が横に広がります
cosの時は?は奇数倍がいいです

また波形を

b=abs(sin(rad2))

とabsすると花びら型になり
-absだとトゲトゲになります また

b=sin(rad2)+sin(rad2*3)+1

と奇数倍したものを加算するとナイスな波になります
偶数倍を加算する時はcosのほうがいいです
小さい円にかける値は倍化したののほうがナイスです また

b=sin(rad2+sin(rad2*?)*?)+1

とsinの中でsinした式にするとFM変調した波形になります
FM変調は dx7 fm音源講座 でググってみるとよいです
とげが左右対称にならないので扱いが難しいです

radとrad2は近い公倍数を持つ値にしてrad3は適当に小数点をつけるとよいです
han2は0.5〜0.8 han3は0.1〜0.2ぐらいがよいです

スーパーバラ曲線では式が

大きい円×波形+小さい円

でしたがこれを

(大きい円+中くらいの円)×波形+小さい円

にするとハイパーバラ曲線(命名:俺)になります

中円の半径は小円を収縮させる周期と逆の位置で0にするとよいです

中円のラジアンに追加する値は大円と波のラジアンに追加する値から特定の値を出しときます

式は

t4=2*t2+t1

です

この値は波の左右対称を崩さずに大きな変化を与えます




































ハイパーは太さ0の状態(俺の脳内で勝手に骨と呼んでる)

でも味があって楽しめます

適当に図形を並べて万華鏡のように表示させても

似たようなものが作れるため繋がりがみえないほど

細かくさせると意味が無くなります

























ハイパーバラ曲線の中円のラジアンに追加する値は

t4=2*t2+t1 でしたが

この2の部分が偶数ならなんでもいけることが後から発覚しましたのでちょっとやってみます




































※同じ計算をループ内でやってしまっているので

かなり無駄があります








































わかりずらくなったので別にまとめました↓

〜スーパーバラ曲線を作ろう〜
http://www42.atwiki.jp/syugyou?cmd=upload&act=open&pageid=250&file=bar.html

二点からの1/(距離2乗)を足してルートして1を割った値によって描画すると

二つの円がつながった図形になります

本来は3Dに使われる手法で式はたぶん本家とぜんぜん違います

function meta0(ime,tx1,ty1,tx2,ty2,han,er,c1,c2,c3)
3個以上の点でメタボールを作ってみます

その際すべての点と距離を求めるわけですが、大量の点があると処理が重くなってしまいます

そこでx,yがある程度離れている点は計算しないように絞りこみます

一つの点が平面上に無限に影響しているので絞るほど精度は落ちます

function meta1(ime,retu,han,bai,er,gen,i1,i2,i3,i4,i5,i6)
液体っぽいです

大量の点を法則に沿って動かすことで水っぽいものを描画します

エセとついているのは本来なら使う流体力学をまったく使っていないからです

※まだ実験中の項目です。いっさいあてにしないでください

LV1:粒子を作る

グローバル変数のryuusiを配列にして粒子オブジェクトを格納します

function setryu0(max,c1,c2,c3)




LV2:粒子を動かす

毎ターン各粒子の座標に増加率を足して描画します

増加率に重力を足していきます

はじにあたったら反射させて増加率を下げます

function ryumove0(ime)


move()

どんどん平べったくなってしまいます



LV3:重ならないようにする

平べったくならないように重なりにくくします

平面を等間隔にわけて粒子を登録させます。

その際すでに粒子が登録されてたら別な場所にワープさせます


rytankというグローバル変数が用意されているのでこれを二次元配列にして粒子を登録します

グローバル変数ryrateで1ピクセルを何分割するか決めてます

各初期値は-1にします
function setrytank(x,y)



rytankに粒子を登録する関数を作ります

function touroku(ban)



毎ターンごとに各粒子の登録と解約をするようにryumove0を改造します

function ryumove1(ime)



では動かしてみましょう


縦にしか動かしてないはずなのに横に広がりましたね

しかしこれは登録時のワープによるもので力学的なものではありません



LV4:圧力と粘り気を加える

隣接する粒子によってrx,ryを変化させます

function atu0(lv)

隣に隣接してたら0.2、斜めに隣接してたら0.1逆方向に補正します



function nen0(lv)

隣接する粒子の方向を加算します

lv(0〜1)で影響度を決めます

斜めは半分の影響にします

直接rx,ryを変えずに一度値をとっておいて

ループが終わってからもっかい回して変えます



では動かしてみましょう


うーん

なんか違いますね

沸騰してるし



LV5:もっと遠くの粒子を参照する

nen0は粒子が他と接触していないと遅くなり洗剤みたいになっていたようです

でも面白いんで残しておきます

それを解消しつつ広範囲の粒子を見るようatu0とnen0を改造します

function atu1(lv,han)


function nen1(lv,han)



では動かしてみましょう




ミルククラウンのようなものが出ました

〜結論〜

重いわりに動きが悪いです

少ない粒子でメタボールを使うか

別言語でちゃんとした粒子法をやったほうがいいです 10000個の粒子をピクセルに対応したノイズに合わせて飛ばす技法です

x,yの加速度にノイズ-0.5を加算していきます

ノイズが二つ必要ですが設定ミスでひとつしか用意できないので縦に倍長くノイズをとります

画面外に行ったら反対側に移動させます

どこまでも加速しないようある程度速くなったら1以下の適当な数をかけます

globalalphaで薄めた画面をgetすることで残像がつきます



function liq0(ime)


action script3でこれの派生形がたくさん公開されています

現在は100000個の粒子を高速で飛ばせるようです

本来はパーティクル(小さい円)を使いますがcanvasでは重くなってしまいます Liquid10000の要領でノイズに合わせてtensen0を引くとフレアっぽくなります

function liq1(ime,kai,rx,ry,c1,c2,c3)


画面外からひかれる線がないのではじっこが薄くなってしまいます これはアトラクタと呼ぶか謎ですが

全てのピクセルで式を一回計算し次の点との角度で色づけすれば全体に色がつきます(当然ですが)

宇宙アトラクタの式でやってみます

function mandel6(ime,tx,ty,zoom,col,t1,t2,t3,t4)


単色にしたほうがきれいです

本来形を成さない式でもなんらかの模様が出るのである意味無限の可能性がある手法です ゲーム等でimageDataを使う場合、いちいち計算せずに最初に作って使いまわします

その際どことアンチエイリアスがかかるかわからないわけですから、

ここまでのアンチエイリアス技術はまったく役に立ちません><

動的にアンチエイリアスをかけるには透明なcanvasを取得し、

アンチエイリアス部分は色はそのままに4つ目のバイトデータを変えます

0〜255の整数値に直して入れます

上のen1()を直すとこうなります

function en5(ime,tx,ty,han,er,c1,c2,c3)


おー、うまく透過でき・・あれ!?

重なってる部分がおかしいですね

単品ならこの処理でいいのですが、すでに何か描画されている場合は↓こんな処理が必要です


自分の透過度(0〜1)をa、相手の透過度(0〜1)をbとして

(自分の色×a + 相手の色×(1−a)×b)÷(a+(1−a)×b)

に色を変え、透過度を

(1−(1−a)×(1−b))×255

にする


これはめんどいんで別に関数を作っとくとよいです↓

function suke(ime,ban,c1,c2,c3,c4)


自分の透過度が0か1の時、相手の透過度が0か1の時でifでわけることで大幅に計算を減らせますが

そこは自分で考えてください sukeを使ってすでに何か描画されてる時の透過をみてみましょう

function en6(ime,tx,ty,han,er,c1,c2,c3)


描画処理を書かなくていいぶんコンパクトになりました



正nサンドイッチも透過用に下だけ変えるとこうなります

テキストエリア id='pan2'
透明じゃないimageDataに透過用のimageDataを透過させます

計算せずに同じ図形を描画できます

canvasを大量に使わなくてすみます

function gousei0(ime1,ime2,x,y)
sukeで透過用のimageDataどうしを合わせて透過用のimageDataを作ります

普通の合成もこれでできますが

四つ目のバイトデータも変えるためgousei0より時間がかかります

画像を文字列化します。

canvasに画像を読み込み

a=canvas.toDataURL()

これだけです。

ただしセキュリティーの都合上デフォルトの状態では読み込んだ画像は文字列化できません。

(getImagedata()もできません。)

クロームの場合:
クロームのショートカットを右クリ→プロパティ→リンク先→
〜.exe" の後に --allow-file-access-from-files をつける(先頭とケツに半角スペースをつけてね)
すでにクロームを開いている場合はいったん閉じる

ファイアーフォックスの場合:
アドレスバーにabout:configと入力してエンター→
security.fileuri.strict_origin_policyってとこでダブルクリック

バージョンによって違うかもしれません。







textarea id='kekka'


一度「失敗しました><」が出るとどうやっても更新するまで失敗し続けます

下のcanvasには文字列から描画しています

文字列はブラウザによって異なったりしますが

なぜか同じものが描画されます 考え出したもののしょぼかったもの

・アトラクタカーテン
線形になるアトラクタを座標を変えながら何度も描画してオーロラみたいにする
無駄に重い

・全ピクセルアトラクタ2
1ピクセルずつ計算しながら次の点の位置に点描画
画面の四角が反映されてしまう

・全ピクセルアトラクタ線
1ピクセルずつ計算しながら次の点と線で結ぶ
非常に重い

・全ピクセルアトラクタフレア
1ピクセルずつ計算しながら次の点の方向に少しずつ線をひく
非常に重い



目次に戻る


〜まだ制作中〜

苦情は じゃがりきん まで