2022/11,江端さんの技術メモ

The target audiences of this primer are researchers and practitioners in the interdisciplinary fields of
transportation, who are specialized or interested in social science models, behavioral models, activitybased
travel demand models, lane use models, route choice models, human factors, and artificial
intelligence with applications in transportation.

こんな感じでemacsにコピペしたら、

  ← (見えないけど)半角のスペースが入っている。これで行末に" "'(半角スペース)が入る

としてから、

M-x replace-string C-q C-j (改行) (改行)

とすると、こうなる。

The target audiences of this primer are researchers and practitioners in the interdisciplinary fields of transportation, who are specialized or interested in social science models, behavioral models, activitybased travel demand models, lane use models, route choice models, human factors, and artificial intelligence with applications in transportation.

コマンド
タブ C-q C-i
改行 C-q C-j

これから濫用すると思うので、メモ

2022/11,江端さんの技術メモ

以前、

円周内の一様乱数

から、乱数の生成を作ることができました。

現在、斜め45度くらいに述びた街の位置情報を乱数で作る為に、楕円周内の一様乱数を調べていたのですが、見つけられていません。

累積密度関数が一様になるように計算式を作れば良い、ということは分かったのですが、そんなの面倒くさくてやっていられません。

ふと思ったのですが、円周内の一様乱数ではθとr を使って、乱数を使っていて、この場合、r=sqrt(乱数)を使っているのだから、θの値に応じた新しいr'を作ってそのまま放り込んでやればいいのでは、と思いつきました。

面倒なので、x軸方向の長さは1.0として、y軸方向の長さをbとすれば、

x=cosθ 、 y=b sinθ となるので、r'= sqrt(x^2+y^2) に放り込むだけ、上記の円周内の一様乱数と同じやり方でできるんじゃないの、と考えて、エクセルで試しました。ここではb=0.5にしています。

結論:上手くいきませんでした。

もう面倒くさくなったので、r' に sqrt(0~1)の乱数を使って、x=cosθ 、 y=b sinθ に乗算するだめの「なんちゃって楕円内の一様乱数」 を作って対応しました。

こんな感じです。

// 住民の居住区を計算
		shita := 2.0 * 3.141592654 * rand.Float64()
		x := 0.03296 * math.Cos(shita)
		y := 0.01893 * math.Sin(shita)
		r := math.Sqrt(rand.Float64())
		rx := r * x
		ry := r * y
		// 45度回転 + 基本座標
		x_map := 1.0/math.Sqrt(2.0)*(rx-ry) + 131.46231887576513
		y_map := 1.0/math.Sqrt(2.0)*(rx+ry) + 34.171317284454766

「ここは主戦場でなない」と腹を括って、逃げるのも、エンジニアには必須の技量です。

2022/10,江端さんの技術メモ

エクセルのコラムはこんな感じ

θ: RAND()*2*3.141592654

r: SQRT(RAND())

X: +B3*COS(A3)

Y: +B3*SIN(A3)

https://qiita.com/aa_debdeb/items/e416ae8a018692fc07eb

2022/10,江端さんの技術メモ

論文購読発表の準備をしていますが、このSEMというのがなかなか面白そうで、今、調べています、

「動かして理解する」が私の信条ですので、"R"と、"RStudio"をインストールして、

井出草平の研究ノート

を参考にして勉強させて頂いております。

install.packages("lavaan")
install.packages("semPlot")
In install.packages("XML") :
   パッケージ 'XML' のインストールは、ゼロでない終了値をもちました

→ が、上手くいかないなぁ

エラーメッセージは、

make: *** [C:/PROGRA~1/R/R-42~1.1/etc/x64/Makeconf:253: DocParse.o] Error 1
ERROR: compilation failed for package 'XML'
* removing 'C:/Users/ebata/AppData/Local/R/win-library/4.2/XML'
Warning in install.packages :
パッケージ ‘XML’ のインストールは、ゼロでない終了値をもちました

ですね。
MSYS2から、pacmanで libxml2-devel のインストールを試みています。

(ここまで)

$ pip install --upgrade pip

install.packages("XML", type = "binary") # これで上手くいった

以下、メモ

install.packages("AER")
install.packages("memisc")

library(lavaan) #lavaanパッケージの読み込み
library(AER) #AERパッケージの読み込み。
data(CPS1985) #データCPS1985の読み込み


2022/10,江端さんの技術メモ

変数や構造体などを一箇所に集めておいて、ソースコードから、#include "include.h" みたいに使うのは、Cの定番ですが、これだけのことをGoでやりたいだけなのに、酷く苦労しました。

大切なのは、ソースコードと違う場所に、そのインクルードファイルみたいなものを置きたい(×ソースコードの配下のディレクトリ)でなく、複数のgoのプロジェクトから共通して使いたい。

まあ、こんな感じです。

(人生最初のアイビスペイント+iPadの手書きです)

ソースファイルは、こんな感じです。

include.go

package include // この名前が、mail.goで使われるので覚えておいて下さい

type Person struct {  //  先頭大文字
	FirstName string // 先頭大文字 
	Age       int // 先頭大文字
}

main.go (これは、src,src2の両方で使っています)

package main

import (
	"fmt"
	"m/include"
)

func main() {
	var mike include.Person  // この "include.Person"という変数名が特徴です
	mike.FirstName = "Mike" // あとは構造体の属性を持ってくるだけ(先頭大文字を忘れないように)
	mike.Age = 20           // 同上
	fmt.Println(mike.FirstName, mike.Age) //=> Mike 20
}

で、ここからが重要なんですが、例えばsrcディレクトリの中で実行すると

PS C:\Users\ebata\dummy\go_test2\src> go run .
go: cannot find main module, but found .git/config in C:\Users\ebata
to create a module there, run:
cd ..\..\.. && go mod init

てなことを言われますので、include, src, src2の上のディレクトリで、

go mod init m // "m"でも、"mm"でも、"mmm"でも、かまわん

を実行すると、go.modができます。中身はこれだけです。

go.mod の中に、includeが書き込まれているとか、そういうことはないようです(どうなっているか分かりません)が、とりあえずsrcに移動して、main.goが動きました。

2022/09,江端さんの技術メモ

C言語によるファジィ(Fuzzy)推論コード

をGo言語に移植してみました。

で、できるだけ、

「GO言語でクラスっぽいことをする」をレビューする

に近づけてみました。

コメントのPrint分は、C++のを残しています(面倒だったので)。あとenum型がないので、テキトーに文字列を使うことにしました。

以前のコードには、豪快なバグがありましたので、ソックリ差し替えします。

設定条件は以下の通りです(テーブルの内容は、負荷量です)

package main

import (
	"fmt"
	"os"
)

func max_2(a, b float64) float64 {
	if a > b {
		return a
	} else {
		return b
	}
}

func min_2(a, b float64) float64 {
	if a > b {
		return b
	} else {
		return a
	}
}

type condition_MF3 struct { // condition_MF3の基底クラス
	center  float64
	width   float64
	express string
}

func new_condition_MF3(_center, _width float64, _express string) *condition_MF3 {
	c3 := new(condition_MF3)
	c3.center = _center
	c3.width = _width
	c3.express = _express
	return c3
}

// 前件部メンバーシップ関数(山3つ)クラス
func (c3 *condition_MF3) func_X(_x float64) float64 {
	// x,yは、メンバーシップ関数上の座標を示す
	x := _x
	y := 0.0 // yの値は、必ず0以上1以下になる

	if c3.express == "LESS" {
		if x <= c3.center-c3.width {
			y = 1.0
		} else if x <= c3.center {
			y = -1.0 / c3.width * (x - c3.center)
		} else {
			y = 0.0
		}
	} else if c3.express == "COMMON" {
		if x <= c3.center-c3.width {
			y = 0.0
		} else if x <= c3.center {
			y = 1.0/c3.width*(x-c3.center) + 1.0
		} else if x <= c3.center+c3.width {
			y = -1.0/c3.width*(x-c3.center) + 1.0
		} else {
			y = 0.0
		}
	} else if c3.express == "MORE" {
		if x <= c3.center {
			y = 0.0
		} else if x <= c3.center+c3.width {
			y = 1.0 / c3.width * (x - c3.center)
		} else {
			y = 1.0
		}
	} else {
		fmt.Println("MF3: wrong expression")
		os.Exit(1)
	}
	return y
}

type condition_MF5 struct { // condition_MF5の基底クラス
	center  float64
	width   float64
	express string
}

func new_condition_MF5(_center, _width float64, _express string) *condition_MF5 {
	c5 := new(condition_MF5)
	c5.center = _center
	c5.width = _width
	c5.express = _express
	return c5
}

func (c5 *condition_MF5) func_X(_x float64) float64 {
	// 前件部メンバーシップ関数(山5つ)クラス
	// x,yは、メンバーシップ関数上の座標を示す
	x := _x
	y := 0.0 // yの値は、必ず0以上1以下になる

	if c5.express == "LESSLESS" {
		if x <= c5.center-2.0*c5.width {
			y = 1.0
		} else if x <= c5.center-c5.width {
			y = -1.0/c5.width*(x-(c5.center-2.0*c5.width)) + 1.0
		} else {
			y = 0.0
		}
	} else if c5.express == "LESS" {
		if x <= c5.center-2.0*c5.width {
			y = 0.0
		} else if x <= c5.center-c5.width {
			y = 1.0/c5.width*(x-(c5.center-c5.width)) + 1.0
		} else if x <= c5.center {
			y = -1.0/c5.width*(x-(c5.center-c5.width)) + 1.0
		} else {
			y = 0.0
		}
	} else if c5.express == "COMMON" {
		if x <= c5.center-c5.width {
			y = 0.0
		} else if x <= c5.center {
			y = 1.0/c5.width*(x-c5.center) + 1.0
		} else if x <= c5.center+c5.width {
			y = -1.0/c5.width*(x-c5.center) + 1.0
		} else {
			y = 0.0
		}
	} else if c5.express == "MORE" {
		if x <= c5.center {
			y = 0.0
		} else if x <= c5.center+c5.width {
			y = 1.0/c5.width*(x-(c5.center+c5.width)) + 1.0
		} else if x <= c5.center+2.0*c5.width {
			y = -1.0/c5.width*(x-(c5.center+c5.width)) + 1.0
		} else {
			y = 0.0
		}
	} else if c5.express == "MOREMORE" {
		if x <= c5.center+c5.width {
			y = 0.0
		} else if x <= c5.center+2.0*c5.width {
			y = 1.0/c5.width*(x-(c5.center+2.0*c5.width)) + 1.0
		} else {
			y = 1.0
		}
	} else {
		fmt.Println("MF5 func_X(): wrong expression")
		os.Exit(1)
	}

	return y
}

/////////////////////////////

type action_MF5 struct { // condition_MF5の基底クラス
	center  float64
	width   float64
	express string
	x       float64
	y       float64
}

func new_action_MF5(_center, _width float64, _express string) *action_MF5 {
	a5 := new(action_MF5)
	a5.center = _center
	a5.width = _width
	a5.express = _express

	if a5.express == "LESSLESS" {
		a5.x = a5.center - 2.0*a5.width
	} else if a5.express == "LESS" {
		a5.x = a5.center - a5.width
	} else if a5.express == "COMMON" {
		a5.x = a5.center
	} else if a5.express == "MORE" {
		a5.x = a5.center + a5.width
	} else if a5.express == "MOREMORE" {
		a5.x = a5.center + 2.0*a5.width
	} else {
		fmt.Println("new_action_MF5: wrong scale expression")
		os.Exit(-1)
	}

	a5.y = 0.0

	return a5
}

// 後件部メンバーシップ関数(山3つ)クラス
func (a5 *action_MF5) func_Y() float64 {
	// x,yは、メンバーシップ関数上の座標を示す

	return a5.y
}

func (a5 *action_MF5) func_Max(b float64) {
	a5.y = max_2(b, a5.y)
}

func (a5 *action_MF5) func_X() float64 {
	return a5.x
}

func complain_reasoning(dis, age float64) float64 {

	// Walking(前件部)
	Walk_Less := new_condition_MF3(1.4, 0.4, "LESS")
	Walk_Common := new_condition_MF3(1.4, 0.4, "COMMON")
	Walk_More := new_condition_MF3(1.4, 0.4, "MORE")

	// Age(前件部)
	Age_LessLess := new_condition_MF5(42, 15, "LESSLESS")
	Age_Less := new_condition_MF5(42, 15, "LESS")
	Age_Common := new_condition_MF5(42, 15, "COMMON") // 中央が 42歳
	Age_More := new_condition_MF5(42, 15, "MORE")
	Age_MoreMore := new_condition_MF5(42, 15, "MOREMORE")

	// Complain(後件部)
	Complain_LessLess := new_action_MF5(0.5, 0.25, "LESSLESS") // 不満の中央値が0.5   0.0/0.25/0.50/0.75/1.00 の5段階

	Complain_Less := new_action_MF5(0.5, 0.25, "LESS")
	Complain_Common := new_action_MF5(0.5, 0.25, "COMMON")
	Complain_More := new_action_MF5(0.5, 0.25, "MORE")
	Complain_MoreMore := new_action_MF5(0.5, 0.25, "MOREMORE")

	// [ルール00] 歩行距離:少0 年令:子供0
	Rule00 := min_2(Walk_Less.func_X(dis), Age_LessLess.func_X(age))
	Complain_LessLess.func_Max(Rule00) // 後件部は上書きされていく
	//fmt.Println("Rule00", Rule00)

	// [ルール01] 歩行距離:少0 年令:若年1
	Rule01 := min_2(Walk_Less.func_X(dis), Age_Less.func_X(age))
	Complain_LessLess.func_Max(Rule01) // 後件部は上書きされていく
	//fmt.Println("Rule01", Rule01)

	// [ルール02] 歩行距離:少0 年令:壮年2
	Rule02 := min_2(Walk_Less.func_X(dis), Age_Common.func_X(age))
	Complain_Common.func_Max(Rule02) // 後件部は上書きされていく
	//fmt.Println("Rule02", Rule02)

	// [ルール03] 歩行距離:少0 年令:高齢3
	Rule03 := min_2(Walk_Less.func_X(dis), Age_More.func_X(age))
	Complain_Common.func_Max(Rule03) // 後件部は上書きされていく
	//fmt.Println("Rule03", Rule03)

	// [ルール04] 歩行距離:少0 年令:老齢4
	Rule04 := min_2(Walk_Less.func_X(dis), Age_MoreMore.func_X(age))
	Complain_More.func_Max(Rule04) // 後件部は上書きされていく
	//fmt.Println("Rule04", Rule04)

	// [ルール10] 歩行距離:普通1 年令:子供0
	Rule10 := min_2(Walk_Common.func_X(dis), Age_LessLess.func_X(age))
	Complain_LessLess.func_Max(Rule10) // 後件部は上書きされていく
	//fmt.Println("Rule10", Rule10)

	// [ルール11] 歩行距離:普通1 年令:若年1
	Rule11 := min_2(Walk_Common.func_X(dis), Age_Less.func_X(age))
	Complain_Less.func_Max(Rule11) // 後件部は上書きされていく
	//fmt.Println("Rule11", Rule11)

	// [ルール12] 歩行距離:普通1 年令:壮年2
	Rule12 := min_2(Walk_Common.func_X(dis), Age_Common.func_X(age))
	Complain_Common.func_Max(Rule12) // 後件部は上書きされていく
	//fmt.Println("Rule12", Rule12)

	// [ルール13] 歩行距離:普通1 年令:高齢3
	Rule13 := min_2(Walk_Common.func_X(dis), Age_More.func_X(age))
	Complain_More.func_Max(Rule13) // 後件部は上書きされていく
	//fmt.Println("Rule13", Rule13)

	// [ルール14] 歩行距離:普通1 年令:老齢4
	Rule14 := min_2(Walk_Common.func_X(dis), Age_MoreMore.func_X(age))
	Complain_MoreMore.func_Max(Rule14) // 後件部は上書きされていく
	//fmt.Println("Rule14", Rule14)

	// [ルール20] 歩行距離:多2 年令:こども0
	Rule20 := min_2(Walk_More.func_X(dis), Age_LessLess.func_X(age))
	Complain_Less.func_Max(Rule20) // 後件部は上書きされていく
	//fmt.Println("Rule20", Rule20)

	// [ルール21] 歩行距離:多2 年令:若年1
	Rule21 := min_2(Walk_More.func_X(dis), Age_Less.func_X(age))
	Complain_Common.func_Max(Rule21) // 後件部は上書きされていく
	//fmt.Println("Rule21", Rule21)

	// [ルール22] 歩行距離:多2 年令:壮年2
	Rule22 := min_2(Walk_More.func_X(dis), Age_Common.func_X(age))
	Complain_More.func_Max(Rule22) // 後件部は上書きされていく
	//fmt.Println("Rule22", Rule22)

	// [ルール23] 歩行距離:多2 年令:高齢3
	Rule23 := min_2(Walk_More.func_X(dis), Age_More.func_X(age))
	Complain_MoreMore.func_Max(Rule23) // 後件部は上書きされていく
	//fmt.Println("Rule23", Rule23)

	// [ルール24] 歩行距離:多2 年令:老齢4
	Rule24 := min_2(Walk_More.func_X(dis), Age_MoreMore.func_X(age))
	Complain_MoreMore.func_Max(Rule24) // 後件部は上書きされていく
	//fmt.Println("Rule24", Rule24)

	// 推論計算
	numerator :=
		Complain_LessLess.func_X()*Complain_LessLess.func_Y() +
			Complain_Less.func_X()*Complain_Less.func_Y() +
			Complain_Common.func_X()*Complain_Common.func_Y() +
			Complain_More.func_X()*Complain_More.func_Y() +
			Complain_MoreMore.func_X()*Complain_MoreMore.func_Y()

	denominator :=
		Complain_LessLess.func_Y() +
			Complain_Less.func_Y() +
			Complain_Common.func_Y() +
			Complain_More.func_Y() +
			Complain_MoreMore.func_Y()

	complain := numerator / denominator

	return complain

}

2020,江端さんの技術メモ

江端の環境でのローカルな場所 C:\Users\ebata\Desktop\bouncy2

C:\Users\ebata>cd C:\Users\ebata\Desktop\bouncy2
cd /c/Users/ebata/Desktop/bouncy2
python3 httpsrv.py
http://localhost:8080 で起動

 

  • 2020/03/14

     

  • 背景
    • ■これからは「C/C++ → Go」と「JavaScript → Webassembly」とシフトしながら色々やっていこうと思っている■正直、どっちも難しい。そういう時は、サンプルをパクって勉強するのが正解 ―― と思っている
  • 目的
  • 環境
    • ■Windows7 or 10
    • ■Go と python3 をインストールしてある
  • やってみたこと
    • ■サンプルプログラムをダウンロードして、コンパイルしてみた
      • ~ https://github.com/stdiopt/gowasm-experiments/tree/master/bouncy
      • コマンドプロンプトからこんな感じでコンパイルできた
        • $ set GOOS=js
        • $ set  GOARCH=wasm
        • $ go build -o main.wasm main.go
        • ちなみに、bashの環境があれば、build.shでコンパイルできる
      • 実際には、コンパイルしなくても、バイナリコード(main.wasm)も、ダウンロードの中に入っているので、コンパイルは不要だったが
    • ■ローカルサーバとしては、色々試してみた
      • python3 -m http.server 8080
      • Goでサーバも作ってみた
      • Perlでもやってみた
    • ■動画がどうしても出てこない
    • ■ここから丸2日間の格闘のスタート
      • もう、色々探しまくった
  • 確認していた問題点
    • ■Google Chromoから、→ 「その他のツール」 → 「ディベロッパーツール」 → 
      • Uncaught (in promise) TypeError: Failed to execute 'compile' on 'WebAssembly': Incorrect response MIME type. Expected 'application/wasm'.この「Expected 'application/wasm'.」がどうにも、不味いらしい ―― が、解決方法が、どうにも見つからない
    • ■kobore.netのサーバに上げても、改善が見られず
  • ローカルサーバを作ってみた
    • ■httpsrv.pyを作った
      #!/usr/bin/env python3
      import http.server
        import socketserver
        
        PORT =
      8080
        
        Handler = http.server.SimpleHTTPRequestHandler
        Handler.extensions_map.update({
        '.wasm': 'application/wasm',
        })
        
        socketserver.TCPServer.allow_reuse_address =
      True
      with socketserver.TCPServer(("", PORT), Handler) as httpd:
        httpd.allow_reuse_address =
      True
      print("serving at port", PORT)
        httpd.serve_forever()
      
  • ■httpsrv.pyを起動した
    • $ python3 httpsrv.py
  • http://localhost:8080 で起動
    • ■動いた
      ■動かなかったら、chromo → 「設定」 → 「閲覧履歴データの削除」でキャッシュをクリアてみること

以上

httpsrv.py

#!/usr/bin/env python3

import http.server
import socketserver

PORT = 8080

Handler = http.server.SimpleHTTPRequestHandler
Handler.extensions_map.update({
    '.wasm': 'application/wasm',
})

socketserver.TCPServer.allow_reuse_address = True
with socketserver.TCPServer(("", PORT), Handler) as httpd:
    httpd.allow_reuse_address = True
    print("serving at port", PORT)
    httpd.serve_forever()

main.go

//Wasming
// compile: GOOS=js GOARCH=wasm go build -o main.wasm ./main.go
package main

import (
	"fmt"
	"math"
	"math/rand"
	"strconv"
	"syscall/js"
)

var (
	width      float64
	height     float64
	mousePos   [2]float64
	ctx        js.Value    // "syscall/js"から引っ張られる
	lineDistSq float64 = 100 * 100
)

func main() {

	// Init Canvas stuff
	doc := js.Global().Get("document")
	canvasEl := doc.Call("getElementById", "mycanvas")
	width = doc.Get("body").Get("clientWidth").Float()
	height = doc.Get("body").Get("clientHeight").Float()
	canvasEl.Set("width", width)
	canvasEl.Set("height", height)
	ctx = canvasEl.Call("getContext", "2d")

	done := make(chan struct{}, 0)

	dt := DotThing{speed: 160, size: 6}

	mouseMoveEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		e := args[0]
		mousePos[0] = e.Get("clientX").Float()
		mousePos[1] = e.Get("clientY").Float()
		return nil
	})
	defer mouseMoveEvt.Release()

	// Event handler for count range
	countChangeEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		evt := args[0]
		intVal, err := strconv.Atoi(evt.Get("target").Get("value").String())
		if err != nil {
			println("Invalid value", err)
			return nil
		}
		dt.SetNDots(intVal)
		return nil
	})
	defer countChangeEvt.Release()

	// Event handler for speed range
	speedInputEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		evt := args[0]
		fval, err := strconv.ParseFloat(evt.Get("target").Get("value").String(), 64)
		if err != nil {
			println("invalid value", err)
			return nil
		}
		dt.speed = fval
		return nil
	})
	defer speedInputEvt.Release()

	// Event handler for size
	sizeChangeEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		evt := args[0]
		intVal, err := strconv.Atoi(evt.Get("target").Get("value").String())
		if err != nil {
			println("invalid value", err)
			return nil
		}
		dt.size = intVal
		return nil
	})
	defer sizeChangeEvt.Release()

	// Event handler for lines toggle
	lineChangeEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		evt := args[0]
		dt.lines = evt.Get("target").Get("checked").Bool()
		return nil
	})
	defer lineChangeEvt.Release()

	// Event handler for dashed toggle
	dashedChangeEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		evt := args[0]
		dt.dashed = evt.Get("target").Get("checked").Bool()
		return nil
	})
	defer dashedChangeEvt.Release()

	doc.Call("addEventListener", "mousemove", mouseMoveEvt)
	doc.Call("getElementById", "count").Call("addEventListener", "change", countChangeEvt)
	doc.Call("getElementById", "speed").Call("addEventListener", "input", speedInputEvt)
	doc.Call("getElementById", "size").Call("addEventListener", "input", sizeChangeEvt)
	doc.Call("getElementById", "dashed").Call("addEventListener", "change", dashedChangeEvt)
	doc.Call("getElementById", "lines").Call("addEventListener", "change", lineChangeEvt)

	dt.SetNDots(100)
	dt.lines = false
	var renderFrame js.Func
	var tmark float64
	var markCount = 0
	var tdiffSum float64

	renderFrame = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		now := args[0].Float()
		tdiff := now - tmark
		tdiffSum += now - tmark
		markCount++
		if markCount > 10 {
			doc.Call("getElementById", "fps").Set("innerHTML", fmt.Sprintf("FPS: %.01f", 1000/(tdiffSum/float64(markCount))))
			tdiffSum, markCount = 0, 0
		}
		tmark = now

		// Pull window size to handle resize
		curBodyW := doc.Get("body").Get("clientWidth").Float()
		curBodyH := doc.Get("body").Get("clientHeight").Float()
		if curBodyW != width || curBodyH != height {
			width, height = curBodyW, curBodyH
			canvasEl.Set("width", width)
			canvasEl.Set("height", height)
		}
		dt.Update(tdiff / 1000)

		js.Global().Call("requestAnimationFrame", renderFrame)
		return nil
	})
	defer renderFrame.Release()

	// Start running
	js.Global().Call("requestAnimationFrame", renderFrame)

	<-done

}

// DotThing manager
type DotThing struct {
	dots   []*Dot
	dashed bool
	lines  bool
	speed  float64
	size   int
}

// Update updates the dot positions and draws
func (dt *DotThing) Update(dtTime float64) {
	if dt.dots == nil {
		return
	}
	ctx.Call("clearRect", 0, 0, width, height)

	// Update
	for i, dot := range dt.dots {
		dir := [2]float64{}
		// Bounce
		if dot.pos[0] < 0 {
			dot.pos[0] = 0
			dot.dir[0] *= -1
		}
		if dot.pos[0] > width {
			dot.pos[0] = width
			dot.dir[0] *= -1
		}

		if dot.pos[1] < 0 {
			dot.pos[1] = 0
			dot.dir[1] *= -1
		}

		if dot.pos[1] > height {
			dot.pos[1] = height
			dot.dir[1] *= -1
		}
		dir = dot.dir

		ctx.Set("globalAlpha", 0.5)
		ctx.Call("beginPath")
		ctx.Set("fillStyle", fmt.Sprintf("#%06x", dot.color))
		ctx.Set("strokeStyle", fmt.Sprintf("#%06x", dot.color))
		// Dashed array ref: https://github.com/golang/go/blob/release-branch.go1.11/src/syscall/js/js.go#L98
		ctx.Call("setLineDash", []interface{}{})
		if dt.dashed {
			ctx.Call("setLineDash", []interface{}{5, 10})
		}
		ctx.Set("lineWidth", dt.size)
		ctx.Call("arc", dot.pos[0], dot.pos[1], dt.size, 0, 2*math.Pi)
		ctx.Call("fill")

		mdx := mousePos[0] - dot.pos[0]
		mdy := mousePos[1] - dot.pos[1]
		d := math.Sqrt(mdx*mdx + mdy*mdy)
		if d < 200 {
			ctx.Set("globalAlpha", 1-d/200)
			ctx.Call("beginPath")
			ctx.Call("moveTo", dot.pos[0], dot.pos[1])
			ctx.Call("lineTo", mousePos[0], mousePos[1])
			ctx.Call("stroke")
			if d > 100 { // move towards mouse
				dir[0] = (mdx / d) * 2
				dir[1] = (mdy / d) * 2
			} else { // do not move
				dir[0] = 0
				dir[1] = 0
			}
		}

		if dt.lines {
			for _, dot2 := range dt.dots[i+1:] {
				mx := dot2.pos[0] - dot.pos[0]
				my := dot2.pos[1] - dot.pos[1]
				d := mx*mx + my*my
				if d < lineDistSq {
					ctx.Set("globalAlpha", 1-d/lineDistSq)
					ctx.Call("beginPath")
					ctx.Call("moveTo", dot.pos[0], dot.pos[1])
					ctx.Call("lineTo", dot2.pos[0], dot2.pos[1])
					ctx.Call("stroke")
				}
			}
		}

		dot.pos[0] += dir[0] * dt.speed * dtTime
		dot.pos[1] += dir[1] * dt.speed * dtTime
	}
}

// SetNDots reinitializes dots with n size
func (dt *DotThing) SetNDots(n int) {
	dt.dots = make([]*Dot, n)
	for i := 0; i < n; i++ {
		dt.dots[i] = &Dot{
			pos: [2]float64{
				rand.Float64() * width,
				rand.Float64() * height,
			},
			dir: [2]float64{
				rand.NormFloat64(),
				rand.NormFloat64(),
			},
			color: uint32(rand.Intn(0xFFFFFF)),
			size:  10,
		}
	}
}

// Dot represents a dot ...
type Dot struct {
	pos   [2]float64
	dir   [2]float64
	color uint32
	size  float64
}

build.sh

#!/bin/sh

GOOS=js GOARCH=wasm go build -o main.wasm ./main.go

wasm_exec.js

// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

(() => {
	// Map multiple JavaScript environments to a single common API,
	// preferring web standards over Node.js API.
	//
	// Environments considered:
	// - Browsers
	// - Node.js
	// - Electron
	// - Parcel

	if (typeof global !== "undefined") {
		// global already exists
	} else if (typeof window !== "undefined") {
		window.global = window;
	} else if (typeof self !== "undefined") {
		self.global = self;
	} else {
		throw new Error("cannot export Go (neither global, window nor self is defined)");
	}

	if (!global.require && typeof require !== "undefined") {
		global.require = require;
	}

	if (!global.fs && global.require) {
		global.fs = require("fs");
	}

	const enosys = () => {
		const err = new Error("not implemented");
		err.code = "ENOSYS";
		return err;
	};

	if (!global.fs) {
		let outputBuf = "";
		global.fs = {
			constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
			writeSync(fd, buf) {
				outputBuf += decoder.decode(buf);
				const nl = outputBuf.lastIndexOf("\n");
				if (nl != -1) {
					console.log(outputBuf.substr(0, nl));
					outputBuf = outputBuf.substr(nl + 1);
				}
				return buf.length;
			},
			write(fd, buf, offset, length, position, callback) {
				if (offset !== 0 || length !== buf.length || position !== null) {
					callback(enosys());
					return;
				}
				const n = this.writeSync(fd, buf);
				callback(null, n);
			},
			chmod(path, mode, callback) { callback(enosys()); },
			chown(path, uid, gid, callback) { callback(enosys()); },
			close(fd, callback) { callback(enosys()); },
			fchmod(fd, mode, callback) { callback(enosys()); },
			fchown(fd, uid, gid, callback) { callback(enosys()); },
			fstat(fd, callback) { callback(enosys()); },
			fsync(fd, callback) { callback(null); },
			ftruncate(fd, length, callback) { callback(enosys()); },
			lchown(path, uid, gid, callback) { callback(enosys()); },
			link(path, link, callback) { callback(enosys()); },
			lstat(path, callback) { callback(enosys()); },
			mkdir(path, perm, callback) { callback(enosys()); },
			open(path, flags, mode, callback) { callback(enosys()); },
			read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
			readdir(path, callback) { callback(enosys()); },
			readlink(path, callback) { callback(enosys()); },
			rename(from, to, callback) { callback(enosys()); },
			rmdir(path, callback) { callback(enosys()); },
			stat(path, callback) { callback(enosys()); },
			symlink(path, link, callback) { callback(enosys()); },
			truncate(path, length, callback) { callback(enosys()); },
			unlink(path, callback) { callback(enosys()); },
			utimes(path, atime, mtime, callback) { callback(enosys()); },
		};
	}

	if (!global.process) {
		global.process = {
			getuid() { return -1; },
			getgid() { return -1; },
			geteuid() { return -1; },
			getegid() { return -1; },
			getgroups() { throw enosys(); },
			pid: -1,
			ppid: -1,
			umask() { throw enosys(); },
			cwd() { throw enosys(); },
			chdir() { throw enosys(); },
		}
	}

	if (!global.crypto) {
		const nodeCrypto = require("crypto");
		global.crypto = {
			getRandomValues(b) {
				nodeCrypto.randomFillSync(b);
			},
		};
	}

	if (!global.performance) {
		global.performance = {
			now() {
				const [sec, nsec] = process.hrtime();
				return sec * 1000 + nsec / 1000000;
			},
		};
	}

	if (!global.TextEncoder) {
		global.TextEncoder = require("util").TextEncoder;
	}

	if (!global.TextDecoder) {
		global.TextDecoder = require("util").TextDecoder;
	}

	// End of polyfills for common API.

	const encoder = new TextEncoder("utf-8");
	const decoder = new TextDecoder("utf-8");

	global.Go = class {
		constructor() {
			this.argv = ["js"];
			this.env = {};
			this.exit = (code) => {
				if (code !== 0) {
					console.warn("exit code:", code);
				}
			};
			this._exitPromise = new Promise((resolve) => {
				this._resolveExitPromise = resolve;
			});
			this._pendingEvent = null;
			this._scheduledTimeouts = new Map();
			this._nextCallbackTimeoutID = 1;

			const setInt64 = (addr, v) => {
				this.mem.setUint32(addr + 0, v, true);
				this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
			}

			const getInt64 = (addr) => {
				const low = this.mem.getUint32(addr + 0, true);
				const high = this.mem.getInt32(addr + 4, true);
				return low + high * 4294967296;
			}

			const loadValue = (addr) => {
				const f = this.mem.getFloat64(addr, true);
				if (f === 0) {
					return undefined;
				}
				if (!isNaN(f)) {
					return f;
				}

				const id = this.mem.getUint32(addr, true);
				return this._values[id];
			}

			const storeValue = (addr, v) => {
				const nanHead = 0x7FF80000;

				if (typeof v === "number") {
					if (isNaN(v)) {
						this.mem.setUint32(addr + 4, nanHead, true);
						this.mem.setUint32(addr, 0, true);
						return;
					}
					if (v === 0) {
						this.mem.setUint32(addr + 4, nanHead, true);
						this.mem.setUint32(addr, 1, true);
						return;
					}
					this.mem.setFloat64(addr, v, true);
					return;
				}

				switch (v) {
					case undefined:
						this.mem.setFloat64(addr, 0, true);
						return;
					case null:
						this.mem.setUint32(addr + 4, nanHead, true);
						this.mem.setUint32(addr, 2, true);
						return;
					case true:
						this.mem.setUint32(addr + 4, nanHead, true);
						this.mem.setUint32(addr, 3, true);
						return;
					case false:
						this.mem.setUint32(addr + 4, nanHead, true);
						this.mem.setUint32(addr, 4, true);
						return;
				}

				let id = this._ids.get(v);
				if (id === undefined) {
					id = this._idPool.pop();
					if (id === undefined) {
						id = this._values.length;
					}
					this._values[id] = v;
					this._goRefCounts[id] = 0;
					this._ids.set(v, id);
				}
				this._goRefCounts[id]++;
				let typeFlag = 1;
				switch (typeof v) {
					case "string":
						typeFlag = 2;
						break;
					case "symbol":
						typeFlag = 3;
						break;
					case "function":
						typeFlag = 4;
						break;
				}
				this.mem.setUint32(addr + 4, nanHead | typeFlag, true);
				this.mem.setUint32(addr, id, true);
			}

			const loadSlice = (addr) => {
				const array = getInt64(addr + 0);
				const len = getInt64(addr + 8);
				return new Uint8Array(this._inst.exports.mem.buffer, array, len);
			}

			const loadSliceOfValues = (addr) => {
				const array = getInt64(addr + 0);
				const len = getInt64(addr + 8);
				const a = new Array(len);
				for (let i = 0; i < len; i++) {
					a[i] = loadValue(array + i * 8);
				}
				return a;
			}

			const loadString = (addr) => {
				const saddr = getInt64(addr + 0);
				const len = getInt64(addr + 8);
				return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
			}

			const timeOrigin = Date.now() - performance.now();
			this.importObject = {
				go: {
					// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
					// may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
					// function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
					// This changes the SP, thus we have to update the SP used by the imported function.

					// func wasmExit(code int32)
					"runtime.wasmExit": (sp) => {
						const code = this.mem.getInt32(sp + 8, true);
						this.exited = true;
						delete this._inst;
						delete this._values;
						delete this._goRefCounts;
						delete this._ids;
						delete this._idPool;
						this.exit(code);
					},

					// func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
					"runtime.wasmWrite": (sp) => {
						const fd = getInt64(sp + 8);
						const p = getInt64(sp + 16);
						const n = this.mem.getInt32(sp + 24, true);
						fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
					},

					// func resetMemoryDataView()
					"runtime.resetMemoryDataView": (sp) => {
						this.mem = new DataView(this._inst.exports.mem.buffer);
					},

					// func nanotime1() int64
					"runtime.nanotime1": (sp) => {
						setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
					},

					// func walltime1() (sec int64, nsec int32)
					"runtime.walltime1": (sp) => {
						const msec = (new Date).getTime();
						setInt64(sp + 8, msec / 1000);
						this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
					},

					// func scheduleTimeoutEvent(delay int64) int32
					"runtime.scheduleTimeoutEvent": (sp) => {
						const id = this._nextCallbackTimeoutID;
						this._nextCallbackTimeoutID++;
						this._scheduledTimeouts.set(id, setTimeout(
							() => {
								this._resume();
								while (this._scheduledTimeouts.has(id)) {
									// for some reason Go failed to register the timeout event, log and try again
									// (temporary workaround for https://github.com/golang/go/issues/28975)
									console.warn("scheduleTimeoutEvent: missed timeout event");
									this._resume();
								}
							},
							getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early
						));
						this.mem.setInt32(sp + 16, id, true);
					},

					// func clearTimeoutEvent(id int32)
					"runtime.clearTimeoutEvent": (sp) => {
						const id = this.mem.getInt32(sp + 8, true);
						clearTimeout(this._scheduledTimeouts.get(id));
						this._scheduledTimeouts.delete(id);
					},

					// func getRandomData(r []byte)
					"runtime.getRandomData": (sp) => {
						crypto.getRandomValues(loadSlice(sp + 8));
					},

					// func finalizeRef(v ref)
					"syscall/js.finalizeRef": (sp) => {
						const id = this.mem.getUint32(sp + 8, true);
						this._goRefCounts[id]--;
						if (this._goRefCounts[id] === 0) {
							const v = this._values[id];
							this._values[id] = null;
							this._ids.delete(v);
							this._idPool.push(id);
						}
					},

					// func stringVal(value string) ref
					"syscall/js.stringVal": (sp) => {
						storeValue(sp + 24, loadString(sp + 8));
					},

					// func valueGet(v ref, p string) ref
					"syscall/js.valueGet": (sp) => {
						const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
						sp = this._inst.exports.getsp(); // see comment above
						storeValue(sp + 32, result);
					},

					// func valueSet(v ref, p string, x ref)
					"syscall/js.valueSet": (sp) => {
						Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
					},

					// func valueDelete(v ref, p string)
					"syscall/js.valueDelete": (sp) => {
						Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
					},

					// func valueIndex(v ref, i int) ref
					"syscall/js.valueIndex": (sp) => {
						storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
					},

					// valueSetIndex(v ref, i int, x ref)
					"syscall/js.valueSetIndex": (sp) => {
						Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
					},

					// func valueCall(v ref, m string, args []ref) (ref, bool)
					"syscall/js.valueCall": (sp) => {
						try {
							const v = loadValue(sp + 8);
							const m = Reflect.get(v, loadString(sp + 16));
							const args = loadSliceOfValues(sp + 32);
							const result = Reflect.apply(m, v, args);
							sp = this._inst.exports.getsp(); // see comment above
							storeValue(sp + 56, result);
							this.mem.setUint8(sp + 64, 1);
						} catch (err) {
							storeValue(sp + 56, err);
							this.mem.setUint8(sp + 64, 0);
						}
					},

					// func valueInvoke(v ref, args []ref) (ref, bool)
					"syscall/js.valueInvoke": (sp) => {
						try {
							const v = loadValue(sp + 8);
							const args = loadSliceOfValues(sp + 16);
							const result = Reflect.apply(v, undefined, args);
							sp = this._inst.exports.getsp(); // see comment above
							storeValue(sp + 40, result);
							this.mem.setUint8(sp + 48, 1);
						} catch (err) {
							storeValue(sp + 40, err);
							this.mem.setUint8(sp + 48, 0);
						}
					},

					// func valueNew(v ref, args []ref) (ref, bool)
					"syscall/js.valueNew": (sp) => {
						try {
							const v = loadValue(sp + 8);
							const args = loadSliceOfValues(sp + 16);
							const result = Reflect.construct(v, args);
							sp = this._inst.exports.getsp(); // see comment above
							storeValue(sp + 40, result);
							this.mem.setUint8(sp + 48, 1);
						} catch (err) {
							storeValue(sp + 40, err);
							this.mem.setUint8(sp + 48, 0);
						}
					},

					// func valueLength(v ref) int
					"syscall/js.valueLength": (sp) => {
						setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
					},

					// valuePrepareString(v ref) (ref, int)
					"syscall/js.valuePrepareString": (sp) => {
						const str = encoder.encode(String(loadValue(sp + 8)));
						storeValue(sp + 16, str);
						setInt64(sp + 24, str.length);
					},

					// valueLoadString(v ref, b []byte)
					"syscall/js.valueLoadString": (sp) => {
						const str = loadValue(sp + 8);
						loadSlice(sp + 16).set(str);
					},

					// func valueInstanceOf(v ref, t ref) bool
					"syscall/js.valueInstanceOf": (sp) => {
						this.mem.setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16));
					},

					// func copyBytesToGo(dst []byte, src ref) (int, bool)
					"syscall/js.copyBytesToGo": (sp) => {
						const dst = loadSlice(sp + 8);
						const src = loadValue(sp + 32);
						if (!(src instanceof Uint8Array)) {
							this.mem.setUint8(sp + 48, 0);
							return;
						}
						const toCopy = src.subarray(0, dst.length);
						dst.set(toCopy);
						setInt64(sp + 40, toCopy.length);
						this.mem.setUint8(sp + 48, 1);
					},

					// func copyBytesToJS(dst ref, src []byte) (int, bool)
					"syscall/js.copyBytesToJS": (sp) => {
						const dst = loadValue(sp + 8);
						const src = loadSlice(sp + 16);
						if (!(dst instanceof Uint8Array)) {
							this.mem.setUint8(sp + 48, 0);
							return;
						}
						const toCopy = src.subarray(0, dst.length);
						dst.set(toCopy);
						setInt64(sp + 40, toCopy.length);
						this.mem.setUint8(sp + 48, 1);
					},

					"debug": (value) => {
						console.log(value);
					},
				}
			};
		}

		async run(instance) {
			this._inst = instance;
			this.mem = new DataView(this._inst.exports.mem.buffer);
			this._values = [ // JS values that Go currently has references to, indexed by reference id
				NaN,
				0,
				null,
				true,
				false,
				global,
				this,
			];
			this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id
			this._ids = new Map();  // mapping from JS values to reference ids
			this._idPool = [];      // unused ids that have been garbage collected
			this.exited = false;    // whether the Go program has exited

			// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
			let offset = 4096;

			const strPtr = (str) => {
				const ptr = offset;
				const bytes = encoder.encode(str + "\0");
				new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes);
				offset += bytes.length;
				if (offset % 8 !== 0) {
					offset += 8 - (offset % 8);
				}
				return ptr;
			};

			const argc = this.argv.length;

			const argvPtrs = [];
			this.argv.forEach((arg) => {
				argvPtrs.push(strPtr(arg));
			});
			argvPtrs.push(0);

			const keys = Object.keys(this.env).sort();
			keys.forEach((key) => {
				argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
			});
			argvPtrs.push(0);

			const argv = offset;
			argvPtrs.forEach((ptr) => {
				this.mem.setUint32(offset, ptr, true);
				this.mem.setUint32(offset + 4, 0, true);
				offset += 8;
			});

			this._inst.exports.run(argc, argv);
			if (this.exited) {
				this._resolveExitPromise();
			}
			await this._exitPromise;
		}

		_resume() {
			if (this.exited) {
				throw new Error("Go program has already exited");
			}
			this._inst.exports.resume();
			if (this.exited) {
				this._resolveExitPromise();
			}
		}

		_makeFuncWrapper(id) {
			const go = this;
			return function () {
				const event = { id: id, this: this, args: arguments };
				go._pendingEvent = event;
				go._resume();
				return event.result;
			};
		}
	}

	if (
		global.require &&
		global.require.main === module &&
		global.process &&
		global.process.versions &&
		!global.process.versions.electron
	) {
		if (process.argv.length < 3) {
			console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
			process.exit(1);
		}

		const go = new Go();
		go.argv = process.argv.slice(2);
		go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env);
		go.exit = process.exit;
		WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
			process.on("exit", (code) => { // Node.js exits if no event handler is pending
				if (code === 0 && !go.exited) {
					// deadlock, make Go print error and stack traces
					go._pendingEvent = { id: 0 };
					go._resume();
				}
			});
			return go.run(result.instance);
		}).catch((err) => {
			console.error(err);
			process.exit(1);
		});
	}
})();