feature image

2019年11月26日 | ブログ記事

電気力線をプロットしてみた【AdC2019 27日目】

ちょうど電磁気学の勉強をしているときにこんな問題を見つけました。


単位長さ当たりに帯電した無限長のn本の直線電荷があるときの電気力線の方程式を求めよ。ただし、各直線と電気力線上の点とを結ぶ直線が一定方向となす角をそれぞれとする。

答えは↓









答え


一定

皆さん解けましたか。僕は速攻で答えを見たのですが、この式を見ても全然ピンと来なかったです。

解説


一応解説を書いておきます。直線電荷を真上から見た図で考えます。
---1

まず、↑図のように適当な曲線PQ、なす角をとします。
線密度の直線電荷が出す電気力線の数は単位長さ当たりなので、曲線PQを通る電気力線の本数は
本です。n本の直線電荷があるときは、
。ここで、電気力線同士は交わることがないので、曲線PQが電気力線となるのは。積分して定数 が電気力線の方程式です。

以上が解説ですが、解説を読んでもいまいち納得できなかったです。というわけで、本当に電気力線になるのかプロットして確かめてみましょう。

プロット結果


完成するのに思ってたよりめちゃめちゃ時間がかかりましたが、なんとかできました。(本当に正しくコーディングできてるのかわからないけどそれっぽいので多分あってる?)。

2-33-1

プログラミング


ちょうど僕が参加しているプロジェクトの方でpixi.jsを使っていたので、それを使うことにしました。
とりあえずプログラムを置いておきます。実行するにはtypescriptでpixi.jsを使える環境を整えてください。

ココをクリックするとソースコードが出てきます

実行すると左クリックで電荷(真上から見た直線電荷)を置けます。Shiftを押しながらだと負電荷になります。BackSpaceで最近の電荷を消します。最大で8個の電荷を置けます。背景の画像用にbackground.pngを用意してください(用意しなくても動きます)。

main.tsimport * as PIXI from 'pixi.js'

const app = new PIXI.Application();
document.body.appendChild(app.view);
let shiftFlag: boolean = false;
let num = 0;
let filter: PIXI.Filter;

document.addEventListener('keydown',(event)=>{
    shiftFlag = event.shiftKey;
    if(event.keyCode === 8){
        num += 7;
        setNewCharge(0);
    }
})
document.addEventListener('keyup',(event)=>{
    shiftFlag = event.shiftKey;
})

function setNewCharge(q: number){
    var mouse = app.renderer.plugins.interaction.mouse.global;
    filter.uniforms.x[num % 8] = mouse.x;
    filter.uniforms.y[num % 8] = mouse.y;
    filter.uniforms.q[num % 8] = q;
}

const background = PIXI.Sprite.from('background.png');
background.width = app.screen.width;
background.height = app.screen.height;
app.stage.addChild(background);

background.interactive = true;
background.buttonMode = true;
background.on('pointerdown', () => {
    setNewCharge(shiftFlag ? -1 : 1);
    num++;
});

const shaderFrag = `
precision highp float;

varying vec2 vTextureCoord;

uniform vec2 mouse;
uniform vec4 inputSize;
uniform vec4 outputFrame;
uniform float x[8], y[8], q[8];

void main() {
    const float PI = 3.1415926535897932384626433832795;
    const float k = 30.0;//本数
    vec2 scr = vTextureCoord * inputSize.xy + outputFrame.xy;
    float sum = 0.0;
    for(int i = 0; i < 8; i++)
        sum += q[i] * atan(scr.x - x[i], scr.y - y[i]);
    float v = fract(sum * k / 2.0 / PI);
    v = min(v, 1.0 - v) * 2.0;
    gl_FragColor = vec4(pow(v, 4.0), 0.0, 0.0, 0.5);
}
`;
const container = new PIXI.Container();
container.filterArea = new PIXI.Rectangle(0, 0, app.screen.width, app.screen.height);
app.stage.addChild(container);

try{
     filter = new PIXI.Filter(null, shaderFrag, {
        mouse: new PIXI.Point(),
        x: [0,0,0,0,0,0,0,0],
        y: [0,0,0,0,0,0,0,0],
        q: [0,0,0,0,0,0,0,0]
    });
    container.filters = [filter];
}
catch{
    alert("GLSLのコードにエラーがあります。")
}

GLSLで書いてあるのはconst shaderFrag = から`で囲まれたところです。


GLSLではuniformとついた変数が、自由にユーザーが変更できる変数みたいです。電荷のデータは全てuniform変数で保管してます。
pos[0~7]が電荷の位置で、q[0~7]が各電荷の強さです。

今回は電気力線を描きたいのでSUM = (以下SUMで表す)という値を周期的に0から1までの値(色の各成分は0から1の実数で表現されるから)を行ったり来たりするように変換することを考えます。
まずfract関数(引数の値の少数点以下の値を返却してくれる)を使います。
graph1
しかし、これでは1のすぐ次に0をとってしまいます(これはこれできれいな電気力線になるのですが)。
そこでfract関数でとった値をvとして、2.0 * min(v, 1.0 -v)として連続関数にします。
graph2
あとは線の太さを少し細目にしたいので、先ほどの値を2乗、あるいは3乗してあげて、
graph3
こんなイメージです。
こうしてできた数値をgl_FragColorに代入して、色の出力ができます。

最後に


意外とそれっぽいものができて良かったです。
最後にプロットした画像を張っておきます。
76-1

明日はbonc256さんとFourmsushiさんの記事です。お楽しみに!

shioyakioh icon
この記事を書いた人
shioyakioh

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

2019年12月21日
モデリングを始めてみたい君へ、MagicaVoxelのススメ
isak icon isak
2019年12月13日
ゲーム紹介「League of Legends」【AdC2019 44日目】
Yataka_ML icon Yataka_ML
2019年12月25日
TensorFlow.jsでwasmを使ってみるためにコントリビュートした【AdC2019 56日目】
sappi_red icon sappi_red
2019年12月25日
無料でDTM環境構築 電子音系編
liquid1224 icon liquid1224
2019年12月4日
部内製チャットサービス「traQ」UIのこれまで 【AdC2019 35日目】
spa icon spa
2019年12月23日
無料でDTM環境構築 生音系編
kashiwade icon kashiwade
記事一覧 タグ一覧 Google アナリティクスについて 特定商取引法に基づく表記