Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
7
Help us understand the problem. What is going on with this article?
@pojiro

作って学ぶPhoenix LiveView、物理シミュレーションの可視化

More than 1 year has passed since last update.

Phoenix LiveViewを使って物理シミュレーションの可視化をしてみたので紹介します。
ソース: https://github.com/pojiro/ov_simulator

Peek 2020-02-03 14-12.gif

渋滞流のシミュレーションです。
渋滞はある一定以下の車間距離(言い換えると一定以上の密度)になると、
(相転移として)自然に発生することが研究により解明されています。

GIFでは渋滞のクラスタが後方に伝搬していく様子がみれます。

動機

Elixirはメッセージパッシングで(Elixir文脈の)プロセスの状態更新を行います。

Elixirを学び始めた頃、なんとなく、
プロセスの状態更新で物理シミュレーションの時間遷移を表現できそうだなとふわふわと思っていました。
このときはどのように実現するかの手段に対する知識がなくてアイデアで終わりました。

それからElixir、Phoenix、Phoenix LiveViewへと少しづつ学びを進めていた先日、
fukuokaexのもくもく会@piacerex さんが以下を紹介してくださったときに、手段が揃ったと思いました。
Phoenix LiveViewで物理シミュレーションの可視化ができます!

twitterで紹介されている動画のソースコードは https://github.com/pcorey/live_canvas

実現方法

状態更新による時間遷移

自身のプロセスへ状態更新のためのメッセージを投げて積分を回します。
これはサーバーサイトで行います。

  def handle_info(:update, %{assigns: %{particles: particles, step_size: step_size}} = socket) do
    particles = particles |> rk4(step_size)
    Process.send_after(self(), :update, 100)

    {:noreply, assign(socket, particles: particles)}
  end

粒子の状態(位置と速度)を持つparticlesをrk4(4次のルンゲクッタ)で積分し、粒子の状態を更新(時間遷移)させます。

描画

JavaScriptのcanvasを使ってクライアントサイドで行います。
particlesの状態はdatasetを使って、サーバーサイドからクライアントサイドへ渡します。
※データ渡しにdatasetを使う方法は https://github.com/pcorey/live_canvas で学びました。力技でびっくりしました。
 まだ、これがベストプラクティスなのかどうかはちょっと疑ってます。

データをサーバーから

      # particlesが更新されるとphx-hookが動作します
      # particlesはJSONエンコードされ文字列として、JavaScriptへ
      <div class="row" phx-hook="canvases"
        data-particles=<%= Jason.encode!(@particles)%> 
        data-space-size=<%= @space_size%>>
        <div class="column">
          <h2>circuit</h2>
          <canvas width="350px" height="350px" id="canvas1"></canvas>
        </div>
        <div class="column">
          <h2>limit cycle</h2>
          <canvas width="350px" height="350px" id="canvas2"></canvas>
        </div>
      </div>

クライアントへ、そしてcanvasで描画します。

hooks.canvases = {
  mounted() {
    let canvas1 = document.getElementById("canvas1")
    let canvas2 = document.getElementById("canvas2")
    let ctx1 = canvas1.getContext("2d")
    let ctx2 = canvas2.getContext("2d")

    Object.assign(this, {canvas1, ctx1, canvas2, ctx2})
  },
  updated(){
    let {canvas1, ctx1, canvas2, ctx2} = this
    let particles = JSON.parse(this.el.dataset.particles)
    let spaceSize = Number(this.el.dataset.spaceSize)

    let circuitRadius = 150
    let particleRadius = 10

    ctx1.clearRect(0, 0, canvas1.width, canvas1.height)
    ctx2.clearRect(0, 0, canvas2.width, canvas2.height)
    particles.forEach(particle => {
      let color_value = Math.round(particle.velocity / 2.0 * 200)
      // Draw Circuit
      ctx1.fillStyle = `rgba(${color_value}, 0, ${255 - color_value}, 1)`
      ctx1.beginPath()
      ctx1.arc(
        particleRadius + circuitRadius + circuitRadius * Math.cos(2 * Math.PI/spaceSize * particle.position),
        particleRadius + circuitRadius + circuitRadius * Math.sin(2 * Math.PI/spaceSize * particle.position),
        particleRadius, 0, 2 * Math.PI);
      ctx1.fill();

      // Draw limit cycle
      ctx2.fillStyle = `rgba(${color_value}, 0, ${255 - color_value}, 1)`
      ctx2.beginPath()
      ctx2.arc(
        particle.headway / 4.0 * 320,
        320 - particle.velocity / 2.0 * 320,
        particleRadius, 0, 2 * Math.PI);
      ctx2.fill();
    })
  }
};

おわり

Elixirを始めたころのアイデアを一つ実装することができました。

LiveViewを使うことで、サーバー-クライアント間のデータ授受がシンプルに実現できました。
また、数値計算のロジックをElixir、描画のみをcanvasで行うというように明確に分離されるので書きやすかったです。

この記事を読んで、これなら作れると思っていただけたら幸いです。

「いいね」よろしくお願いします。:wink:

7
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
fukuokaex
エンジニア/企業向けにElixirプロダクト開発・SI案件開発を支援する福岡のコミュニティ

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
7
Help us understand the problem. What is going on with this article?