Contents My Feed Description 2020-01-24T23:10:00+09:00 Tosh Morry https://tm23forest.com/contents QGIS3.10 LTS長期サポート版をインストールした https://tm23forest.com/contents/qgis-3.10-lts-installed 2020-01-24T23:10:00+09:00 2020-01-24T23:10:00+09:00

QGIS(https://www.qgis.org/)の3.10をインストールしたことをメモしておく。結論から言うと重複インストールすることなく勝手に前バージョンをアンインストールしてくれるのでHappyだった。

QGIS 3.10.1をインストール

前バージョンが残っていると次のような流れになった。ちなみにCoruñaはn以降が文字化けしている。なんでそんな文字使うねん。

QGISインストーラ1番目の画面

QGISインストーラ2番目の画面

QGISインストーラ3番目の画面

QGISインストーラ4番目の画面

QGISインストーラ5番目の画面

QGISインストーラ6番目の画面

QGISインストーラ7番目の画面

QGISインストーラ8番目の画面

QGISインストーラ9番目の画面

QGISインストーラ10番目の画面

QGISインストーラ11番目の画面

QGISインストーラ12番目の画面

QGISインストーラ13番目の画面

最後に前バージョンをアンインストールしてくれるようだ。ありがたい。

QGIS 3.10.2のインストール

気づいたらLTS(Long Term Support)版の3.10.2がリリースされていたのでインストールした。このときも前バージョンをアンインストールすることなくインストーラを起動したところ次のようになった。前回は無かったがWindows Defender SmartScreenが発動してしまった。

Windows Defender SmartScreenが起動した画面

Windows Defender SmartScreenが起動した画面(詳細情報を押した)

QGIS3.10.2インストーラの既存インストールの確認

QGIS3.10.2インストーラ1番目の画面

QGIS3.10.2インストーラ2番目の画面

QGIS3.10.2インストーラ3番目の画面

QGIS3.10.2インストーラ4番目の画面

QGIS3.10.2インストーラ5番目の画面

QGIS3.10.2インストーラ6番目の画面

QGIS3.10.2インストーラ7番目の画面

QGIS3.10.2インストーラ8番目の画面

QGIS3.10.2インストーラ9番目の画面

QGIS3.10.2インストーラ10番目の画面

QGIS3.10.2インストーラ11番目の画面

QGIS3.10.2インストーラ12番目の画面

QGIS3.10.2インストーラ13番目の画面

QGIS3.10.2インストーラ14番目の画面

こんどは先に前バージョンがアンインストールされた。

]]>
グラフラプラシアンの解釈と接続行列 https://tm23forest.com/contents/graph-laplacian-incidence-matrix 2020-01-24T12:34:00+09:00 2020-01-24T12:34:00+09:00 グラフラプラシアン(Graph Laplacian)がなぜラプラシアンと呼ばれるのかを、普通の関数に対するラプラシアンオペレータと比較して直観的に説明する。ついでに普通の関数のときにラプラシアンとまとめて紹介されるナブラについて、それに対応する接続行列について紹介する。

グラフラプラシアンってカタカナで書くとめちゃくちゃ読みにくいな。Graph Laplacianなら全然読めるけど両方カタカナで空白つけないのが良くない。こういうのGraphが漢字なら日本語にしたとき丁度よいのだが。

Graph Laplacianの定義

無向グラフ\(G=(V,E)\)\(|V|=n\)に対して次の\(n\times n\)行列\(L(G)\)をGraph Laplacianといい、グラフ\(G\)が何を指すか明らかなときは単に\(L\)とかく。もしくは\(\mathcal{L}\)のときもある。

\[ \begin{equation} L(G)_{ij}=\left\{\begin{matrix} \deg(v_i) & (i=j)\\ -1 & (i\neq j\text{ and }\{v_i,v_j\}\in E)\\ 0 & (i\neq j\text{ and }\{v_i,v_j\}\notin E) \end{matrix}\right. \end{equation}\]

\(L(G)_{ij}\)はGraph Laplacianの\((i, j)\)要素を示し、\(v_i\in V\)\(i\)番目の頂点を示す。\(\deg\)は頂点の次数。念のため次数とはその頂点につながっている辺の数のこと。

グラフの次数行列\(D\)と隣接行列\(A\)を用いるとグラフラプラシアンは次のようにかける。

\[ \begin{equation} L=D-A \end{equation}\]

いずれも\(G\)によって決まる行列だが明らかなので省略する。次数行列(degree matrix)とは対角成分に頂点の次数を持つような対角行列(非対角成分がゼロ)、隣接行列(adjacency matrix)とは\(\{0,1\}\)のみを要素にもつ行列で\(i,j\)間のエッジが存在すれば\(1\)、そうでなければ\(0\)を取る。

\[ \begin{gather} D_{ij}=\left\{\begin{matrix} \deg(v_i) & (i=j)\\ 0 & (i\neq j) \end{matrix}\right.\\ A_{ij}=\left\{\begin{matrix} 1 & (\{v_i,v_j\}\in E)\\ 0 & (\{v_i,v_j\}\notin E) \end{matrix}\right.\\ \end{gather}\]

Graphラプラシアンの例

無向グラフの例

こういうグラフの場合を考えると、それぞれ次のようになる。

\[ \begin{gather} D=\begin{bmatrix} 2 & 0 & 0 & 0 & 0 & 0\\ 0 & 4 & 0 & 0 & 0 & 0\\ 0 & 0 & 2 & 0 & 0 & 0\\ 0 & 0 & 0 & 3 & 0 & 0\\ 0 & 0 & 0 & 0 & 3 & 0\\ 0 & 0 & 0 & 0 & 0 & 2 \end{bmatrix},\quad A=\begin{bmatrix} 0 & 1 & 1 & 0 & 0 & 0\\ 1 & 0 & 1 & 1 & 1 & 0\\ 1 & 1 & 0 & 0 & 0 & 0\\ 0 & 1 & 0 & 0 & 1 & 1\\ 0 & 1 & 0 & 1 & 0 & 1\\ 0 & 0 & 0 & 1 & 1 & 0 \end{bmatrix}\\ L=\begin{bmatrix} 2 & -1 & -1 & 0 & 0 & 0\\ -1 & 4 & -1 & -1 & -1 & 0\\ -1 & -1 & 2 & 0 & 0 & 0\\ 0 & -1 & 0 & 3 & -1 & -1\\ 0 & -1 & 0 & -1 & 3 & -1\\ 0 & 0 & 0 & -1 & -1 & 2 \end{bmatrix} \end{gather}\]

ここまでは普通の話。(ここからがマグマなんです)

Laplacianといわれる理由

名前からして実関数に対するラプラシアンオペレータと関係とおかしいだろうということで実際関係していることを直観的に示していく。簡単な場合を使って2変数関数\(f(x,y)\)に対するラプラシアンを考える。

\[ \begin{equation} \Delta\equiv\frac{\partial^2}{\partial x^2}+\frac{\partial^2}{\partial y^2} \end{equation}\]

とおくと、これは関数に作用して関数値を返す演算子となり

\[ \begin{equation} \Delta f(x,y)= \frac{\partial^2 f(x,y)}{\partial x^2}+\frac{\partial^2 f(x,y)}{\partial y^2} \end{equation}\]

となる。こっちのラプラシアンをGraph Laplacianと結びつけるために、この値を離散化して数値計算しようと試みてみる。もっとも単純な離散化のやり方は次だろう。

\[ \begin{align} \frac{\partial^2 f(x,y)}{\partial x^2} &=\frac{1}{h}\left( \frac{f(x+h,y)-f(x,y)}{h}-\frac{f(x,y)-f(x-h,y)}{h} \right)\\ &=\frac{f(x+h,y)-2(x,y)+f(x-h,y)}{h^2} \end{align}\]

小さい\(h\)をとって素朴に前進差分と後退差分を使って勾配を計算し、さらにその差から2階微分を求めている。\(y\)についても同じことをやると次がすぐにわかる。

\[ \begin{multline} \frac{\partial^2 f(x,y)}{\partial x^2} +\frac{\partial^2 f(x,y)}{\partial y^2}\\ =\frac{f(x+h,y)+f(x-h,y)+f(x,y+h)+f(x,y-h)-4f(x,y)}{h^2} \end{multline}\]

もう言いたいこと分かったと思うがこれは実平面を拡張したような次のグラフに対応している。

Graph Laplacianのイメージ

直観的に符号が定義と異なるが、このグラフから向きという概念をなくすということを考えればいい。このグラフはもともとの実平面の性質から数字の大小という意味で「向き」をもっているが、無向グラフの世界ではそのような概念は無いのでラプラシアンをグラフに作用させることを考えるにあたって「自分引く相手の総和」を用いることはまぁアナロジーとして許せる範囲だろう。

ついでにグラフ上の調和関数を考える時はゼロかゼロでないかが重要なので符号が逆転していても構わない。ということでGraph Laplacianは普通のラプラシアンのグラフに対する拡張っぽくなっていることが気持ち的には分かった。もっとうまい説明は無数にある気がしているが次にいく。

グラフに対するLaplacianと調和関数

ここまでの話を踏まえてグラフの頂点を定義域とする関数\(f:V\to\mathbb{R}\)を考えると、実関数に対するラプラシアンから得た「自分引く相手の総和」というアナロジーから次の値をおもいつく。

\[ \begin{equation} \Delta f(v)=\sum_{(v,e)\in E}(f(v)-f(e))\qquad(v\in V) \end{equation}\]

書き換えると

\[ \begin{equation} \Delta f(v_i)=\sum_{j=1}^n \mathbb{I}(\{v_i,v_j\}\in E)(f(v_i)-f(v_j)) \end{equation}\]

ここで、\(\mathbb{I}\)は指示関数(indicator function)で引数内が成り立つなら1、それ以外の場合にはゼロとなる便利なヤツ。実関数にたいするLaplacianがある種の演算子であったことを考えるとグラフに対するLaplacianも\(f\)という関数を介することなく定義されるべきだろう。そこで2番目のラプラシアンの定義式より、各\(f(v_j)\)の係数を抜き出しておいたものを\(L\)とおくとこれはGraph Laplacianの定義そのもの。これは式をいじるまでもなく簡単に確かめられる。あとは

\[ \begin{equation} Lf(v_i)\equiv\sum_{j=1}^n L_{ij}f(v_j),\quad i=1,2,\dots,n \end{equation}\]

とでも定義してやれば同じように使える。注意しておくとこれは行列の計算でもなんでもなくてただ\(Lf\)をこう定義しただけ。ようは\(L\)\(f\)によらずに決定されていてこの\(L\)を用いることで\(i=1,\dots,n\)に対する\(f(v_i)\)のラプラシアンが計算できていればいい。

関数\(f:V\to\mathbb{R}\)についても調和関数かどうかがこれによって定義できる。\(f:V\to\mathbb{R}\)がグラフ\(G\)上で調和であるとは\(i=1,2,\dots,n\)について

\[ \begin{equation} Lf(v_i)=0 \end{equation}\]

を満たすこと。まぁそらそうだよな。いくらでも他の書き方は存在します。

勾配に対応する接続行列

実関数の場合には\(\Delta=\nabla\cdot\nabla\)というような式があったがこれに対応するものが有向グラフの場合にはある。もう一度いうと有向グラフの場合にはある。無向グラフには向きという概念が無いから勾配という概念に対応するものも無くてまぁ不思議ではないね。

接続行列の定義

無向グラフ\(G=(V,E)\)\(|V|=n,|E|=m\)に対する接続行列(incidence matrix)\(B\)はそれぞれの要素が次のようになる\(n\times m\)行列

\[ \begin{equation} B_{ij}=\left\{\begin{matrix} 1 & (v_i\in e_j)\\ 0 & (v_i\notin e_j) \end{matrix}\right. \end{equation}\]

\(v_i, e_j\)はそれぞれ\(i\)番目の頂点と\(j\)番目の辺のこと。有向グラフの場合には次のようにやるのが自然だろう。

\[ \begin{equation} B_{ij}=\left\{\begin{matrix} 1 & (e_j=(v, v_i)\text{ for some }v)\\ -1 & (e_j=(v_i, v)\text{ for some }v)\\ 0 & (\text{otherwise}) \end{matrix}\right. \end{equation}\]

少しだけ説明すると、有向グラフでは辺\(e=\{v, w\}\)において\(v\)\(w\)の順番に意味があるので集合ではなくベクトルのような記法\(e=(v,w)\)を用いることが多い。集合の場合には要素の順番は考慮されないがベクトルの場合は考慮するので。ただ、完全にベクトルというわけではなく有向グラフの場合にも\(v\in e\)なんかの使い方はゆるす。

接続行列の例

例をみたほうが早いだろう。Graphラプラシアンのときと同じグラフを用いる。...

]]>
衝突積分の対称関係式 https://tm23forest.com/contents/boltzmann-collision-integral-symmetric 2020-01-16T20:20:00+09:00 2020-01-16T20:20:00+09:00 ボルツマン方程式の衝突項まわりで現れる衝突積分の対象関係式を導出する。

衝突項

分子運動論だとかに現れるボルツマン(Boltzmann)方程式の右辺のことを衝突項という。(重力などの全ての粒子に一様に働くような)外力が無い場合のボルツマン方程式は

\[ \begin{equation} \frac{\partial f}{\partial t}+\xi_i\frac{\partial f}{\partial X_i}=\Delta f_{\mathrm{collision}} \end{equation}\]

みたいな感じで右辺が衝突項になっている。この書き方は総和の規約が使われているので同じ添え字のでてくる左辺第2項は和をとるものとする。2体衝突だとか3体衝突だとかの違いで衝突項の取り方にもいろいろあるらしいがとにかく分子同士の衝突を表す項のことを衝突項といっている。考えるのはこの衝突項について。

衝突積分作用素

ここでは2体衝突のみを考慮した次のタイプの衝突項を考える。

\[ \begin{equation} J(f,f)=\frac{\sigma^2}{2m} \int_{\mathbb{R}^3\times\mathbb{S}^2} [f(\boldsymbol{\xi}'_*)f(\boldsymbol{\xi}')-f(\boldsymbol{\xi}_*)f(\boldsymbol{\xi})] \left|\boldsymbol{e}\cdot(\boldsymbol{\xi}_*-\boldsymbol{\xi})\right| d\Omega(\boldsymbol{e})d\boldsymbol{\xi}_* \end{equation}\]

ここに、\(f(\xi)=f(\boldsymbol{X},\boldsymbol{\xi},t)\)は分子の位置・速度に関する密度関数、\(\boldsymbol{X},\boldsymbol{\xi},t\)はそれぞれ位置、速度、時刻で位置と時刻は出てこないので省略してかかれている。太字は\(\mathbb{R}^3\)である。また、

  • \(\sigma\)は分子の直径、\(m\)は分子の質量
  • \(\boldsymbol{e}\in\mathbb{S}^2\)は単位球上の点であり\(|e|=1\)\(d\Omega(e)\)\(e\)方向の立体角素(面積素の3次元角バージョン)

さらに

\[ \begin{align} \boldsymbol{\xi}'&\equiv\boldsymbol{\xi}'(\boldsymbol{\xi},\boldsymbol{\xi}_*, \boldsymbol{e}) =\boldsymbol{\xi}+[(\boldsymbol{\xi}_*-\boldsymbol{\xi})\cdot \boldsymbol{e}]\boldsymbol{e}\\ \boldsymbol{\xi}'_*&\equiv\boldsymbol{\xi}'(\boldsymbol{\xi},\boldsymbol{\xi}_*, \boldsymbol{e}) =\boldsymbol{\xi}_*-[(\boldsymbol{\xi}_*-\boldsymbol{\xi})\cdot \boldsymbol{e}]\boldsymbol{e} \end{align}\]

とする。\(\boldsymbol{\xi}'\)\(\boldsymbol{\xi}'_*\)の2つはいずれも\(\boldsymbol{\xi},\boldsymbol{\xi}_*,\boldsymbol{e}\)によってきまる。\(J(f,f)\)の式に直接入れ込むこともできるが式が長くなるので。ついでに\(J(f,f)\)自体はいぜんとして\(\boldsymbol{X},\boldsymbol{\xi},t\)についての関数であるので注意しておく。この衝突項を用いた(重力などの全ての粒子に一様に働くような)外力が無い場合のボルツマン方程式はすごく丁寧にかくと次のようにかける。

\[ \begin{equation} \frac{\partial f(\boldsymbol{X},\xi,t)}{\partial t} +\sum_{i=1}^3\xi_i\frac{\partial f(\boldsymbol{X},\xi,t)}{\partial X_i} =J[f,f](\boldsymbol{X},\xi,t) \end{equation}\]

外力無しの場合なので他で見るかもしれない形とくらべて左辺の第3項が無い。こう見ると確かに\(J\)\(f\)に作用する積分作用素。ちなみに\(J(f)\)でなく\(J(f,f)\)になっている理由は

\[ \begin{equation} J(f,g)\equiv \frac{1}{4m}\int_{\mathbb{R}^3\times\mathbb{S}^2} [f(\boldsymbol{\xi}')g(\boldsymbol{\xi}'_*) +f(\boldsymbol{\xi}'_*)g(\boldsymbol{\xi}') -f(\boldsymbol{\xi})g(\boldsymbol{\xi}_*) -f(\boldsymbol{\xi}_*)g(\boldsymbol{\xi})] \left|\boldsymbol{e}\cdot(\boldsymbol{\xi}_*-\boldsymbol{\xi})\right| d\Omega(\boldsymbol{e})d\boldsymbol{\xi}_* \end{equation}\]

という一般化された衝突積分を考えると都合がいい場合がまぁ別な機会にあるから。\(f=g\)としてみると確かに最初にあげた衝突積分(collision integral)に一致していることがわかる。

対称関係式

任意の関数\(\phi=\phi(\boldsymbol{\xi})\)についてのつぎの関係を対称関係式という。正確にはここでいうところの衝突積分作用素についての対称関係式だが例えば線形化された衝突作用素(linearized collision integral)なんかでも同じことが出来る。

\[ \begin{multline} \int_{\mathbb{R}^3}\phi(\boldsymbol{\xi})J(f,f)d\boldsymbol{\xi}\\ =\frac{\sigma^2}{8m}\int_{\mathbb{R}^3\times\mathbb{R}^3\times\mathbb{S}^2} (\phi(\boldsymbol{\xi})+\phi(\boldsymbol{\xi}_*) -\phi(\boldsymbol{\xi}')-\phi(\boldsymbol{\xi}'_*)) (f(\boldsymbol{\xi}'_*)f(\boldsymbol{\xi}') -f(\boldsymbol{\xi}_*)f(\boldsymbol{\xi})) \left|\boldsymbol{e}\cdot(\boldsymbol{\xi}_*-\boldsymbol{\xi})\right| d\Omega(\boldsymbol{e})d\boldsymbol{\xi}_*d\boldsymbol{\xi} \end{multline}\]

これを示す。まずは左辺を書き下す。

\[ \begin{align} =\frac{\sigma^2}{2m} \int_{\mathbb{R}^3\times\mathbb{R}^3\times\mathbb{S}^2} \phi(\boldsymbol{\xi}) (f(\boldsymbol{\xi}'_*)f(\boldsymbol{\xi}') -f(\boldsymbol{\xi}_*)f(\boldsymbol{\xi})) \left|\boldsymbol{e}\cdot(\boldsymbol{\xi}_*-\boldsymbol{\xi})\right| d\Omega(\boldsymbol{e})d\boldsymbol{\xi}_*d\boldsymbol{\xi} \end{align}\]

この式は\(\boldsymbol{\xi},\boldsymbol{\xi}_*\)の両方で積分しているので、被積分関数の\(\boldsymbol{\xi}\)\(\boldsymbol{\xi}_*\)を形式的に入れ替えたものも同じ値になるはずである。ところが\(\phi(\boldsymbol{\xi})\)に関する部分以外はいずれも\(\boldsymbol{\xi},\boldsymbol{\xi}_*\)の対称式の形をしている。これに注目するとこの式は次のように変形できる。

\[ \begin{align} =\frac{\sigma^2}{2m} \int_{\mathbb{R}^3\times\mathbb{R}^3\times\mathbb{S}^2} \frac{\phi(\boldsymbol{\xi})+\phi(\boldsymbol{\xi}_*)}{2} (f(\boldsymbol{\xi}'_*)f(\boldsymbol{\xi}') -f(\boldsymbol{\xi}_*)f(\boldsymbol{\xi})) \left|\boldsymbol{e}\cdot(\boldsymbol{\xi}_*-\boldsymbol{\xi})\right| d\Omega(\boldsymbol{e})d\boldsymbol{\xi}_*d\boldsymbol{\xi} \end{align}\]

つまり、同じ項を2で割って分割し、片方の変数を形式的に入れ替えてからもう一度足し合わせてくくったものである。この操作をもう一度行う。

\[ \begin{align} =&\frac{\sigma^2}{2m} \int_{\mathbb{R}^3\times\mathbb{R}^3\times\mathbb{S}^2} \frac{\phi(\boldsymbol{\xi})+\phi(\boldsymbol{\xi}_*)+\phi(\boldsymbol{\xi})+\phi(\boldsymbol{\xi}_*)}{4} (f(\boldsymbol{\xi}'_*)f(\boldsymbol{\xi}') -f(\boldsymbol{\xi}_*)f(\boldsymbol{\xi})) \left|\boldsymbol{e}\cdot(\boldsymbol{\xi}_*-\boldsymbol{\xi})\right| d\Omega(\boldsymbol{e})d\boldsymbol{\xi}_*d\boldsymbol{\xi}\\ =&\frac{\sigma^2}{8m} \int_{\mathbb{R}^3\times\mathbb{R}^3\times\mathbb{S}^2} (\phi(\boldsymbol{\xi})+\phi(\boldsymbol{\xi}_*) +\phi(\boldsymbol{\xi})+\phi(\boldsymbol{\xi}_*)) (f(\boldsymbol{\xi}'_*)f(\boldsymbol{\xi}') -f(\boldsymbol{\xi}_*)f(\boldsymbol{\xi})) \left|\boldsymbol{e}\cdot(\boldsymbol{\xi}_*-\boldsymbol{\xi})\right| d\Omega(\boldsymbol{e})d\boldsymbol{\xi}_*d\boldsymbol{\xi} \end{align}\]

結構近づいてきたことがわかる。ここで項を前半と後半に分割する。

\[ \begin{align} =&\frac{\sigma^2}{8m} \int_{\mathbb{R}^3\times\mathbb{R}^3\times\mathbb{S}^2} (\phi(\boldsymbol{\xi})+\phi(\boldsymbol{\xi}_*) +\textcolor{red}{\phi(\boldsymbol{\xi})+\phi(\boldsymbol{\xi}_*)}) (f(\boldsymbol{\xi}'_*)f(\boldsymbol{\xi}') -f(\boldsymbol{\xi}_*)f(\boldsymbol{\xi})) \left|\boldsymbol{e}\cdot(\boldsymbol{\xi}_*-\boldsymbol{\xi})\right| d\Omega(\boldsymbol{e})d\boldsymbol{\xi}_*d\boldsymbol{\xi} \end{align}\]

前半は完成しているのでこの赤で示した部分に注目して\(\boldsymbol{\xi},\boldsymbol{\xi}_*\)の積分を\(\boldsymbol{\xi}',\boldsymbol{\xi}'_*\)の積分に書き換えるという操作を行う。そのためにはまずヤコビアンを計算するのだが、実はこれは6変数の積分でめんどうなので結果をしめすと

\[ \begin{gather} \left|\frac{\partial(\boldsymbol{\xi}',\boldsymbol{\xi}'_*)}{\partial(\boldsymbol{\xi},\boldsymbol{\xi}_*)}\right| =1\\ \therefore\quad d\boldsymbol{\xi}_*d\boldsymbol{\xi} =d\boldsymbol{\xi}'_*d\boldsymbol{\xi}' \end{gather}\]

ちゃんとやるなら物理的な対称性から一般性を失わずに\(\boldsymbol{e}\)を特定の成分だけ1のベクトルにするとか、極座標つかって真面目にやるとかすればいいんじゃなかろうか。これについては物理的な意味を考えた方がわかると思うが図が必要なのでやめる。次に

\[ \begin{align} \boldsymbol{\xi}'&\equiv\boldsymbol{\xi}'(\boldsymbol{\xi},\boldsymbol{\xi}_*, \boldsymbol{e}) =\boldsymbol{\xi}+[(\boldsymbol{\xi}_*-\boldsymbol{\xi})\cdot \boldsymbol{e}]\boldsymbol{e}\\ \boldsymbol{\xi}'_*&\equiv\boldsymbol{\xi}'(\boldsymbol{\xi},\boldsymbol{\xi}_*, \boldsymbol{e}) =\boldsymbol{\xi}_*-[(\boldsymbol{\xi}_*-\boldsymbol{\xi})\cdot \boldsymbol{e}]\boldsymbol{e} \end{align}\]

という式の差を取って両辺に\(\boldsymbol{e}\)かけると

\[ \begin{align} \boldsymbol{\xi}'-\boldsymbol{\xi}'_* &=\boldsymbol{\xi}-\boldsymbol{\xi}_* +2[(\boldsymbol{\xi}_*-\boldsymbol{\xi})\cdot \boldsymbol{e}]\boldsymbol{e}\\ (\boldsymbol{\xi}'-\boldsymbol{\xi}'_*)\cdot\boldsymbol{e} &=-(\boldsymbol{\xi}_*-\boldsymbol{\xi})\cdot\boldsymbol{e} +2(\boldsymbol{\xi}_*-\boldsymbol{\xi})\cdot \boldsymbol{e}\\ (\boldsymbol{\xi}'-\boldsymbol{\xi}'_*)\cdot\boldsymbol{e} &=(\boldsymbol{\xi}_*-\boldsymbol{\xi})\cdot\boldsymbol{e}\\ \end{align}\]

\(\boldsymbol{e}\cdot\boldsymbol{e}=1\)をつかえるのでこうなる。地味にアスタリスクがついてるほうとの符号の付き方が入れ替わっていることに注目。これを踏まえると

\[ \begin{align} \boldsymbol{\xi}& =\boldsymbol{\xi}'+[(\boldsymbol{\xi}'_*-\boldsymbol{\xi}')\cdot \boldsymbol{e}]\boldsymbol{e}\\ \boldsymbol{\xi}_*& =\boldsymbol{\xi}'_*-[(\boldsymbol{\xi}'_*-\boldsymbol{\xi}')\cdot \boldsymbol{e}]\boldsymbol{e} \end{align}\]

がすぐにわかる。移項しただけ。これで赤字のところを変数変換する準備はできた。

\[ \begin{align} &\frac{\sigma^2}{8m} \int_{\mathbb{R}^3\times\mathbb{R}^3\times\mathbb{S}^2} (\textcolor{red}{\phi(\boldsymbol{\xi})+\phi(\boldsymbol{\xi}_*)}) (f(\boldsymbol{\xi}'_*)f(\boldsymbol{\xi}') -f(\boldsymbol{\xi}_*)f(\boldsymbol{\xi})) \left|\boldsymbol{e}\cdot(\boldsymbol{\xi}_*-\boldsymbol{\xi})\right| d\Omega(\boldsymbol{e})d\boldsymbol{\xi}_*d\boldsymbol{\xi}\\ &=\frac{\sigma^2}{8m} \int_{\mathbb{R}^3\times\mathbb{R}^3\times\mathbb{S}^2} (\textcolor{red}{\phi(\boldsymbol{\xi})+\phi(\boldsymbol{\xi}_*)}) (f(\boldsymbol{\xi}'_*)f(\boldsymbol{\xi}') -f(\boldsymbol{\xi}_*)f(\boldsymbol{\xi})) \left|\boldsymbol{e}\cdot(\boldsymbol{\xi}'-\boldsymbol{\xi}'_*)\right| d\Omega(\boldsymbol{e})d\boldsymbol{\xi}'_*d\boldsymbol{\xi}' \end{align}\]

といっても書き下さなければ最後の方がかわっただけでそんなに変数変換をやった感じはない。ここでも対称性を利用して全てのダッシュ付きとダッシュ無しを入れ替える。すると

\[ \begin{align} &=\frac{\sigma^2}{8m} \int_{\mathbb{R}^3\times\mathbb{R}^3\times\mathbb{S}^2} (\textcolor{red}{\phi(\boldsymbol{\xi})+\phi(\boldsymbol{\xi}_*)}) (f(\boldsymbol{\xi}'_*)f(\boldsymbol{\xi}') -f(\boldsymbol{\xi}_*)f(\boldsymbol{\xi})) \left|\boldsymbol{e}\cdot(\boldsymbol{\xi}'-\boldsymbol{\xi}'_*)\right| d\Omega(\boldsymbol{e})d\boldsymbol{\xi}'_*d\boldsymbol{\xi}'\\ &=\frac{\sigma^2}{8m} \int_{\mathbb{R}^3\times\mathbb{R}^3\times\mathbb{S}^2} (\textcolor{red}{\phi(\boldsymbol{\xi}')+\phi(\boldsymbol{\xi}'_*)}) (f(\boldsymbol{\xi}_*)f(\boldsymbol{\xi}) -f(\boldsymbol{\xi}'_*)f(\boldsymbol{\xi}')) \left|\boldsymbol{e}\cdot(\boldsymbol{\xi}-\boldsymbol{\xi}_*)\right| d\Omega(\boldsymbol{e})d\boldsymbol{\xi}_*d\boldsymbol{\xi}\\ &=\frac{\sigma^2}{8m} \int_{\mathbb{R}^3\times\mathbb{R}^3\times\mathbb{S}^2} (\textcolor{red}{-\phi(\boldsymbol{\xi}')-\phi(\boldsymbol{\xi}'_*)}) (f(\boldsymbol{\xi}'_*)f(\boldsymbol{\xi}') -f(\boldsymbol{\xi}_*)f(\boldsymbol{\xi})) \left|\boldsymbol{e}\cdot(\boldsymbol{\xi}-\boldsymbol{\xi}_*)\right| d\Omega(\boldsymbol{e})d\boldsymbol{\xi}_*d\boldsymbol{\xi} \end{align}\]

ほんとに形式的に逆にしただけね。一応\(\boldsymbol{\xi},\boldsymbol{\xi}_*\)の中身もいれかわっていることに注意。すべて入れ替えたうえでもの変数の使い方と変わっていなくて唯一変わったのは赤字の部分だけ。これをもとの部分にいれると最初の対称関係式を得る。

]]>
Self-HostなGoogle Fontsを使ってPage Speedに怒られないようにする https://tm23forest.com/contents/selfhosted-googlewebfonts-psi-okoraren 2019-12-10T00:06:00+09:00 2019-12-10T00:06:00+09:00

ウェブフォントによってフォントファイルごとWebページを配信することによって統一的な見た目のコンテンツをユーザに提供できる。これによって昔によくやりがちだったfont-familyによく見た目も知らんフォントを並べてその順番をどうしようかと考える必要がなくなる。見た目を知らずにこれを書いている人結構いるだろというあるある。Google Fontsはそのウェブフォントサービスの1つである。

<link ref="https://fonts.googleapis.com/css?family=Noto+Sans+JP&display=swap" rel="stylesheet"> 

<head></head>内で指定して自分のCSSのfont-familyなんかで'Noto Sans JP'を書いておくだけの超絶お手軽仕様。ここで上げたURLはGoogle Fontsの公式でポチポチやるだけで勝手に教えてくれる。

この方法でページをGoogle Fontsを読み込ませるとPage Speed Insights、以下PSIにフツーにちょっと怒られることをGoogle Fontsを使う限りPage Speedは上がらないでは述べた。やっているうちにgoogle webfonts helperを使ってSelf-Hostedなウェブフォントで重量級の日本語フォントをpreloadすることが最もよいやり方(ベストプラクティス)であると気づいた。方法とその理由を早くいいたい。どっちを先にするか迷ったがまずは方法から。あるあるを早くいいたい~。

Google Fontsをホスティングする方法

google webfonts helper

というサービスを使う。あくまでヘルパであってこれが無いとできないわけではないがこれが無いと限りなくめんどくさい。このサイトがやってくれることは

  • Google Fontsに指示されたCSSを読んでフォントファイルのURLを取得
  • ウェイト単位でunicode-rangeを統合、各ウェイトに対応するフォントファイルを生成
  • 指定したウェイト、コードセットのフォントファイルをzipにまとめてダウンロードさせる
  • Google Fontsによる@font-faceの記述をコピペさせる

といったところ。指示されたCSSを自分で開いてみるとこのへんの想像がつく。1つのフォントに対して複数のフォントファイルが存在するのは異なるfont-weightunicode-rangeごとにフォントファイルが用意されているから。指示されたCSSの内容は頻度はしらんが更新されているようなので生成したフォントファイルが毎回のリクエストで使いまわされている場合は最新版でない可能性がある。

googlewebfontshelperのスクリーンショット1

googlewebfontshelperのスクリーンショット1

  1. 左のサイドバーから使いたいフォントを選択。
  2. そのフォントで表示したい文字コードセットを選択。日本語フォントならばjapaneseにチェック、欧文フォントを別に指定する場合はlatinのチェックは外すほうがよい。指定しない場合チェックを外すと欧文はsans-serifになる。
  3. 使うウェイトを選択。regularと必要に応じてboldの700を選択する。
  4. @font-faceをコピーして自分のCSSのどっかに貼り付ける。Best Supportは最も多くのブラウザをケアできる書き方、Modern Browsersは簡素な書き方でほとんどのブラウザをケアできる書き方。Modern Browsersをオススメする。
  5. ダウンロードボタンを押してzipを落とす。サーバに配置。デフォルトでは../fonts/に配置することになっているが@font-faceをコピペするときにテキストボックスでディレクトリを指定できる。
  6. font-familyに最初に選んだフォント名を指定する。欧文フォントを別で用意する場合はLato, 'Noto Sans JP', sans-serif;を書いてそれぞれのフォントファイルを正しい文字コードセットで配置しておく。

これでSelf-HostなGoogle Fontsが完成する。

ページスピードのための最適化

ここまでは普通にhelperを使ってGoogle Fontsをホスティングしただけ。いわゆる普通のミートスパゲッティ。ここからがマグマなんです(なにがやねん)。ホスティングするメリットを最大限引き出せるので必ずやるべきといえる。

font-display:swap;を書く

書いてください。以上です。これがgoogle fonts helperのデフォルトで無いのはほとんどバグです(何かそうしていない事情があるんでしょうか?教えてほしい)。Google Fontsではデフォルトになっているので。

重量級ファイルのpreload

<link rel="preload" as="font" type="font/woff2" href="/fonts/noto-sans-jp-v24-japanese-700.woff2" crossorigin>

このようにして配置したフォントファイルはunicode-rangeが統合されているのでサイズが大きいです。このままではページのあるべきフォントでの表示時間に影響を与える可能性があるのとPSIをごまかすためにpreloadします。preloadすべきフォントファイルは@font-face内にかかれているものから選択することになりますが何をpreloadさせるかについては次を考慮します。

  • ローカルにない可能性が高いフォント:ユーザのローカルにフォントがある場合はWebからフォントファイルをダウンロードさせるよりもローカルのものを使った方が確実によいです。指定されたフォントファイルがどのローカルのフォントに対応するかは@font-faceを読むまで判断できないはずなので、preloadはブラウザの挙動にもよりますがおそらくローカルにあってもダウンロードされてしまいます。ローカルにありそうなフォントはpreloadさせないようにしましょう
  • コンテンツ内で確実に使用されるフォント:preloadしたのにページで使用しない場合にはブラウザのインスペクタやInsightsで警告が出る場合があります。例えばウェイト700がほとんど出現しないサイトならばそのウェイトのpreloadの必要性は薄くなる。
  • フォントファイルのサイズが大きいか:欧文フォントなどはそもそもフォントファイルのサイズが小さいのでdisplay:swap;だけで十分と言えます。preloadする前にディスプレイswapを書いた時点でファーストペイントの裏でフォントファイルがダウンロードされます。

一般的に書きましたが結論は日本語フォントpreloadの欧文フォントは何もしないのがよいでしょう。ウェブフォントを使っている時点で日本語はローカルにないフォントを使う場合が多いでしょうし。欧文はRobotoなどはAndroidにそのまま入ってるなんかありますしpreloadせんくていいでしょう。

フォントファイルはキャッシュさせる

これについては文句のでようもなくやったほうがいいです。やり方は別ページにゆずりますが静的ファイルのTTL(time-to-live)は長めに設定しておきましょう。個別に設定できるならフォントファイルだけ300日などにしておいても良いでしょう。バージョンアップするとv24のところの名前が変わるので事実上無限大でもいいくらいです。

Google Fontsをセルフホストしたほうがいい理由

したほうがいいといっていますがまずはしない方がいい理由から。つまりデメリットですね。

  • フォントファイルのロードが遅くなる:普通はGoogleのサーバの方が転送速度なんかの面でパワーが強いので予めGoogleサーバにpreconnectしておいた状態でロード時間を比較するとおそらく負けます。
  • フォントのバージョン更新が手動になる:先ほどのスクショにlast modifiedの項目があるようにGoogle Fontsは日々更新されています。セルフホストするとヘルパでダウンロードしたときのバージョンで止まったままになります。
  • 全ての文字が含まれたフォントファイルがダウンロードされる:unicode-rangeごとにダウンロードさせていたものを統合してダウンロードさせるのでページのコンテンツによっては最終的な転送量が大きくなります。

ぐらいでしょうか。つまり言いたいことはデメリットが多くありません。くわえて、これらは条件をつければ一概にデメリットとは言えません。得られるメリットを考えてみます。

  • CSSのサイズ削減:unicode-rangeなどが大量に書かれたCSSはサイズが大きく、それ自体がPSIに怒られていましたがヘルパによってフォントを統合し、使う分だけの@font-faceにしたことによりPSIに怒られません。
  • フラッシュの回数が減少:unicode-rangeに対して1つのフォントファイルをダウンロードさせると、ブラウザにもよりますがそれぞれのフォントファイルのダウンロード完了時に代替フォントからそのフォントへ表示か切り替わるフラッシュが発生します。統合することにより発生するフラッシュの回数は減少します。
  • 必ずキャッシュヒットする:これについても、フォントファイルを統合したことにより初回にページを開いたときの転送量は増えてしまいますが2回目以降は特にpreload指定されているフォントファイルは大抵キャッシュからとれるので長期的に見たときの転送量は大して増えません。必ずは言い過ぎなのでキャッシュヒット率が高くなるが正確でしょうか。
  • バージョンアップに対して安定:バージョンが固定されることはデメリットに上げましたがこれはメリットでもあります。現状のページの見た目に問題が無いならばGoogle Fontsがいくらアップデートされようがページの見た目はいつでもそのままで安定です。ありえないでしょうがGoogle Fonts側の改悪により突然文字化けしたりレイアウトが勝手に崩れるといったことがありません。
  • 複数フォントを使用する際に不要な文字コードセットの@font-faceを書かなくていい:ホスティングしない場合にはどんなfont-familyが来てもそのフォントの存在するタイプフェイスを表示しようと試みていました。ここではfont-familyがわかっていて最適な@font-faceと必要なフォントファイルを配信できるので健康です。

以上がまーあげられる理由でしょう。ページスピードの観点では「仕様していないCSS」「ページ表示に必要なリソース数」の面では確実に有利になります。preloadで大きなフォントファイルを非同期化しておけば大きなフォントファイルについてはPSIに怒られません。結果として実際はどうあれPSIのスコアはホスティングすることで確実に上がります。

さらっと書いたフラッシュの回数ですが、これは人間の感覚で有意にわかります。パパパッと一瞬過ぎてわからないくらいの違いしか無いと想像されそうですが、ホスティング前のフラッシュの回数が明らかに多いです。特にモバイルだとfirefoxの読み込みゲージが左から右に行くにつれて幾度となくフラッシュしたのちにページが表示され、これはエクスペリエンスとしては良くないです。ホスティングした場合は体感のフラッシュは1回だけで、別に代替フォント(そのシステムのsans-serif)の見た目も大抵の場合そんなに汚くないので確実にホスティングのウェブフォントのサイトの方が見やすいです。2回目以降はフラッシュすらしません。デフォルトではキャッシュにヒットしないフォントはダウンロードされるので2回目以降もフラッシュの可能性があります。

まとめ:Google FontsをSelf-Hostしよう

ここまで述べてきた内容はちゃんとデモンストレートするならブラウザのインスペクタ使って比較したいところですがそんなに大きくは外していないでしょう。総合的にみてGoogle Fontsを使用するならホスティングによる方法がベストプラクティスだといえそうな気がしないでもない(何が言いたいねん)。もちろん<head></head>しかいじれない媒体を使っているなどそれができない状況も考えられるのでGoogle Fontsの提供方法がそもそも悪いとは言えません。

]]>
Python正規表現だけでWebクローリング https://tm23forest.com/contents/python-simple-regex-web-crowling 2019-12-05T09:18:00+09:00 2019-12-05T09:18:00+09:00 そういう要求に出くわしたのでPythonの正規表現だけを使って特定ドメインの内部リンク先を巡回した。このやり方について記録しておく。もう少しリンクURLが複雑な場合、あるいは内部リンクに限定しない場合についても応用はある程度効くだろう。

ユニークな内部リンク

今回やった方法が使えた状況をまとめておく。

  • 対象はCMSによって生成されるものの実質的に静的ページのみ。
  • /ではじまる単純な内部リンクのみを探索する。
  • ユニークな内部リンクの総数はだいたいわかっている(多くない)。
  • サーバ負荷などは気にしない。UAもいじらなくていい。

また、importするのは以下の2点だけ。

  • re : Python標準の正規表現ライブラリ
  • requests : https通信用ライブラリ

あとはPythonが動けばok。やりたいことが明確であり迅速さを求めていたため、たいそうなWebスクレイピング・クローリング用のライブラリを持ち出していない。これらは初期の習熟に一定のリソースを割かれるのに対して上記2ライブラリは既にわかっていて他の場面にも応用が効く。

requestsによるコンテンツ取得

この部分は多用するので関数とした。Snippetかつ完全なコードをしめす。

import requests

def body_of(url):
  print(url)
  response = requests.get(url)
  content = response.content.decode('utf-8')
  match = re.search(r'^<body>.*^</body>', content, re.M | re.S)
  if match:
    return match.group(0)

Webにリクエストを送信するのはこの部分だけであるので最初にprintして通信を行ったことのプログレス表示を行う。名前のとおり<body></body>の中身だけ欲しいので正規表現によってこの部分を取り出している。

ここでも事前知識を用いていることに注意せよ。

  • <body></body>タグは必ず行頭にくる。

つかっている正規表現は難しくないが、オプションの2点はここでは重要である。

  • re.Sまたはre.DOTALL : ドットを改行を含む全ての文字列にマッチさせる
  • re.Mまたはre.MULTILINE : 複数行モード、各行の行頭が文字列の先頭と同一視される

短い方はコードは簡単になるがなんのオプションか後から見て全くわからないので考えものだな。

アンカータグのhrefを抽出

これもまずはSnippetをしめす。

matchiter = re.finditer(r'<a[^>]*? +?href="([^"]*?)"', body)
if matchiter:
  links = [a.group(1) for a in matchiter]
  for link in links:
    # do something to link here

コメントのところでbodyに含まれていたリンクに対して何かをする。正規表現は非常にナイーブに組んでおりこちらもHTMLで認識可能なaタグとはややことなる。

  • <aの間に空白がある場合は考慮しない
  • hrefが最初のattributeではない場合は考慮

反例あるかもしれないが適宜printするエンジニアリングでとりあえずターゲットドメインで動けばいいというスタンスでやっているので。

dictによる再帰

ここからは単純なプログラミングの話。Cなら再帰でもできる処理だがwhileで展開する。ホントはdo-whileが欲しかったが無念。URLをキーとしてbodyの内容を値とするC++のSTLでいうsetに相当するデータ構造が欲しいがPythonだとdictで瞬殺できる。プログラムの流れ含めてSnippetを示す。


root = 'https://kokoni_domain_name_wo_kaku/'
links_dict = { root : body_of(root) }

matchiter = re.finditer(r'<a[^>]*? +?href="([^"]*?)"', links_dict[root])
if matchiter:
  links = [a.group(1) for a in matchiter]
  for link in links:
    if link.startswith('/'):
      url = root[:-1] + link
      if not url in links_dict:
        links_dict[url] = body_of(url)

cont = True
while cont:
  cont = False
  bodys = list(links_dict.values())
  for body in bodys:
    if not body:
      continue
    matchiter = re.finditer(r'<a[^>]*? +?href="([^"]*?)"', body)
    if matchiter:
      links = [a.group(1) for a in matchiter]
      for link in links:
        if link.startswith('/'):
          url = root[:-1] + link
          if not url in links_dict:
            links_dict[url] = body_of(url)
            cont = True

あきらかにdo-whileが使える構造なんだがswiftさ重視で1つ無駄に同じようなコードチャンクを書き下した。やっていることは再帰を横方向に展開しているだけ。これ以上ユニークな内部リンクが増えなくなったらループから抜けるようになっている。同一URLで複数回リクエストが送出されることは無いようになってる。ただし前提条件に書いたようにここで想定している対象はユニークな内部リンクの数がそこまで多くない。この例は全てメモリ上でやっているがdictが限界迎えるくらいの内部リンクがあるなら改良は必要だろう。

ポイントというほどではないがstartswith('/')、つまりリンク文字列が/で始まるか否かによってそれが内部リンクかどうかを判断している。対象がCMSでそういうリンクしか出てこないことがわかっているためであるが、仮に純粋にApacheの静的ページであれば../などで親ディレクトリを参照したりする。この辺りは応用を効かせられるところだろう。あとハマったポイントはbodys = list(links_dict.values())でコピーを取っておかないとイテレーションの途中でdictが変化していると怒られる。

もうひとつ改良するとしたらrequestsでセッション管理もできるのでそれを利用する。といってもsessionはSet-Cookieによるもののことを指しているからコネクションの意味での同一セッションならrequestsの内部で勝手に行われているかもね。あらかじめ書いたようにサーバ負荷は気にしないので念のため注意。これぐらいでどうといったことになるサーバは無いだろうが。

参考URL

おそらく無限回参照するし今後も参照するだろうページだ。
re --- 正規表現操作 (Python 3.8.0 ドキュメント)

]]>
AndroidStudioで突然Cannot Resolve symbolで赤線が大量に出る時の対処法 https://tm23forest.com/contents/androidstudio-cannot-resolve-symbol 2019-11-29T20:20:23+09:00 2019-11-29T20:20:23+09:00 先日AndroidStudio3.1で普通に作業していると何かの拍子にandroid.support.v7.app.AppCompatActivityはじめとするSupport Library系のimport文がことごとく'Cannot Resolve Symbol'状態になり、それに伴ってコードのいたる所で警告が出現する事態が発生した。ただ、ビルド自体は通ってAPK生成したりはできる。これの直し方。

よくある対処法まとめ

  1. File -> 'Invalidate Caches / Restart'
    真っ先に出てくる対象法。ただ筆者の環境ではこれでは直らなかった。
  2. プロジェクトのフォルダにある.ideaを削除
    これもどこかで出てきた対処法。これは直ったが若干おかしな現象が起きた。左のProjectのツリーがおかしなことになった。しかしこれでも何度かプロジェクト開きなおしているともとに戻るので.ideaを消してもプロジェクトの構成が壊れるといったことは無いよう。

ベストプラクティス

まずは上記の'Invalidate Caches / Restart'を試す。これで直らない場合にはまずFile->Close Projectでプロジェクトを閉じてから開きなおす。ここで、開きなおすときに最近開いたプロジェクトのところから開くのではなく'Open an exsiting Android Studio project'から対象とするプロジェクトを指定して開くのがポイント。何やらプロジェクト構成系のファイルがリフレッシュされているような効果があるようですっきりCannot resolve symbol状態は直った。

同じような現象に悩まされている方はぜひお試しあれ。

]]>
Pythonでファイルの拡張子を取得 https://tm23forest.com/contents/python-get-extension 2019-11-29T20:20:05+09:00 2019-11-29T20:20:05+09:00 Pythonでファイル名を拡張子とそれ以外の部分に分ける方法。

import os
basename, extension = os.path.splitext(filename)

これでextensionにドットを含む拡張子部分、basenameにそれ以外の部分が入る。

]]>
GravのプラグインImageCaptionsでBootstrapのfigureを指定する https://tm23forest.com/contents/grav-plugin-imagecaptions-bootstrap-figure-style 2019-11-24T14:16:00+09:00 2019-11-24T14:16:00+09:00

GravのプラグインImage CaptionsでBootstrapのfigureクラスをあてる方法を紹介します。ImageCaptionsというプラグインはGravでは紫のチェックマークがついています。一応Gravの公式という意味でついているのですが、このプラグインの実装方法は少々疑問でした。これについてはページの最後に記載しています。

ImageCaptionsで出来ること

この説明はpluginのREADMEままです。Gravのマークダウンから次のように画像を埋め込みます。

![My Image Alt Text](myimage.jpg?classes=caption "My Image Caption")

ImageCaptionsプラグインがオフの場合は次のようにレンダリングされます。

<img src="/yourpage/mymage.jpg" alt="My Image Alt Text" title="My Image Caption" class="caption" />

ImageCaptionsプラグインをオンにするとこれが次のようになります。

<figure class="image-caption">
    <img src="/yourpage/mymage.jpg" alt="My Image Alt Text" title="My Image Caption" class="caption" />
    <figcaption>My Image Caption</figcaption>
</figure>

あとはクラス名等適当に指定してスタイルあててこっちでよろしくやってくださいよ、というプラグインです。

Bootstrapをあてる場合

enabled: true
built_in_css: false
scope: img.figure-img
entire_page: false
figure_class: figure
figcaption_class: figure-caption

figure_classfigcaption_classの設定はページごとにもfrontmatterで変更できます。build_in_cssはこれからbootstrapのスタイルをあてるのでオフにします。.figureをつかうときには必ず.figure-imgを指定するのでscopeはこのように設定しました。

![Image of forest](forest.jpg?classes=figure-img,img-fluid&width=50% "This is a beautiful forest")

レンダリングされた見た目はこのようになります。


Image of forest
This is a beautiful forest

このHTMLは次です(多少違いますが)。

<figure class="figure figure-img">
  <img width="50%" title="This is a beautiful forest" alt="Image of forest" class="img-fluid figure-img" src="forest.jpg">
  <figcaption class="figure-caption">This is a beautiful forest</figcaption>
</figure>

どうやらfigure-imgが意図していないfigureタグに追加されてしまうようです。これはよくわかりません。READMEとは違う動作なのでバグでしょうね。

画像ごとにマークダウンからclasseseは指定できるのでborderなどbootstrapで使えるクラスをうまいこと指定できます。figure-captionのクラスについてはfrontmatterからページごとに1パターンしか指定できないのでページごとに統一されます。

ImageCaptionsの実装方法の問題点

何をするプラグインなのかはわかりました。ところが実装を見てみたところ一見して効率は良くなさそうです。専門家ではないので違っていても許してね。簡単に説明すると吐き出された生HTMLをパースして一度DOMツリーを構成、その中からscopeオプションで指定されたタグ、クラス、idにより特定の要素をセレクトし、figureタグを挿入してDOMツリー再構成、そののちにHTMLに戻しています。DOMツリーは外部ライブラリを使っています。

ちなみにページ全体のHTMLをパースするかページのマークダウン単位でHTMLをパースするかをentire_pageオプションで指定できます。テンプレート内(例えばbase.html.twig)にimg.captionが無いことがわかっていればオンにする意味は無いし、あったところでテンプレートに直接<figure>を書いておけばいいのでこのオプションは全くもって不要ではないでしょうか。

HTMLをパースするなんてブラウザで日常的にやっているそんなに重い処理ではないといわれればそれまでなんですけどこれは気になります。なぜかというと、このやり方であると、例えばこれと同じようなプラグインがあったとすると、そのプラグインごとのonPageContentProcessedでほとんど同じDOMツリーを別々に構築することになってしまいます。この場合明らかに一度DOMツリーを構成してから全てのプラグインがやりたい処理を施し、再びHTMLに戻したほうが高効率です。scopeの要素を取ってくるんだからしっかりパースした方がregexしたりするより効率いい、という可能性とは関係なく。

コンテンツのimgタグを検索してそこをfigureで囲んで適切なアトリビュートを設定する、という機能はonMarkdownInitializedにてParseDownのブロックを上書きする方法の方が効率がよさそうに思います。が、onMarkdownInitializedで、新しいブロックやインラインを追加しているpluginはよくあるのですが上書きしている例を探しているのですがまだ見つけられていません。ということはそういったやり方が現状できないということなのでしょうか。出来てしかるべきだとは思うんですけどね。キーとなるのはelementToHtmlメソッドでしょうか。これについて要調査。

あと1点言いたいことはデフォルトでDOMツリーの構成まで処理が走ってしまうことですね。どういうことかというと

image-captions:
    enabled: false

でページのfrontmatterからプラグインの処理は中断させられるのですが、デフォルトではページごとの設定はオンであるのでImage Caption要素が無いページでは毎回このオプションをfrontmatterに書かなければなりません。オフのページの方が多いサイトではこれは問題です。プラグインそのものをオフにしてしまうと

image-captions:
    enabled: true

をしても機能は働きません。従ってfrontmatterの宣言が無い場合のデフォルトをプラグイン側のオプションとして設けなければこの問題は解決できません。ひとつ言えるのは、どのページでもキャプション画像無いならプラグインをオフにすべきということでしょうね。

英語で書いてない時点でアレなんですけど、これを見た誰か、修正してくれないかな~。以上。

]]>
GravのMarkdown NoticesプラグインでBootstrapのAlertを使う https://tm23forest.com/contents/grav-markdown-notices-bootstrap-using-alert 2019-11-23T23:26:00+09:00 2019-11-23T23:26:00+09:00 GravプラグインであるMarkdown Noticesの機能によりマークダウンから簡単にBootstrapのコンポーネントであるAlertを呼び出す方法を紹介します。やり方は普通にBootstrap CSSが読み込まれている環境でプラグインの設定を次のようにするだけです。YAMLで表示していますがAdminからも簡単に変更できます。

Markdown Noticesプラグインの設定

enabled: true
built_in_css: false
base_classes: 'alert'
level_classes: [alert-info, alert-warning, alert-danger, alert-success]

ビルトインCSSのクラスは使われないのでfalse、他はBootstrapのコンポーネントが呼び出せるように合わせます。

マークダウンの書き方

! A simple info alert—check it out!

!! A simple warning alert—check it out! 

!!! A simple danger alert—check it out! 

!!!! A simple success alert—check it out!

のようなマークダウンテキストを入力します。この結果は以下のようになります。

A simple info alert—check it out!

A simple warning alert—check it out!

A simple danger alert—check it out!

A simple success alert—check it out!

簡単です。もちろんprimarysecondaryなどのアラートを使うこともできまし、順番を変えることもできます。次のようにエクスクラメーションを続ければ複数行もいけますし、もともとのマークダウン記法も使えます。

! ### This is h3 headings.
! 
! In this block, markdown expression can be used in the same way.
! * I have a pen.
! * I have an apple.
!
! ---
!
! Aww yeah, you successfully read this important alert message.

これをレンダリングすると次のようになります。

This is h4 headings.

In this block, markdown expression can be used in the same way.

  • I have a pen.
  • I have an apple.

Aww yeah, you successfully read this important alert message.

ブロック内はあくまで普通のマークダウンになるということですね。この辺りはMarkdown Noticesの普通の使いかたですので簡単ですね。余りにもビックリマークが多いと使いにくいので実用上使えるアラートは4種類までといったところでしょうか。

alert-linkにマークダウンから対応

アラート内にリンクを貼るときにアンカータグのクラスでalert-linkを指定すると配色がいい感じになります。Markdown ExtraをGravの設定でオンにしてあれば次のようにしてalert-linkを指定することができます。

!! #### This is h4 headings
!!
!! This is [an example link](#){.alert-link}.
!! Using Markdown Extra functionality.

This is h4 headings

This is an example link Using Markdown Extra functionality.

Markdown Extraがオフの場合でも直接リンクを記入すれば同じことが出来ます。ただ、これを多用してしまうとマークダウンというよりただのHTMLファイルになるので多用する場合は諦めてExtraをオンにするほうが良いでしょう。

!!! #### This is h4 headings
!!!
!!! This is <a href="#" class="alert-link">an example link</a>
!!! Not using Markdown Extra functionality.

This is h4 headings

This is an example link Not using Markdown Extra functionality.

AlertとNoticeの違いとは

いや、同じでしょ。ほとんど。なのでGravのMarkdown Noticesプラグインを使っていてかつBootstrapにより構築されているテーマならばスタイルの重複なんかが発生するのでこのようにするのがよいでしょう。ちなみにビルトインCSS使ったデフォの見た目はこんな感じになります。

This is h3 headings.

In this block, markdown expression can be used in the same way.

  • I have a pen.
  • I have an apple.


Aww yeah, you successfully read this important alert message.

まあこれでもよいでしょう。但しコッチはGravのデフォルトなのでGrav感丸出し、一方でBootstrapの方もBootstrap感丸出しなのでまあ好み。BootstrapのAlertを採用してBootstrap全体をカスタマイズするのが良いかなと個人的に思います。以上。

]]>
VisualStudioCodeをWindowsにインストールしたときのログ https://tm23forest.com/contents/vscode-install-log-windows 2019-11-22T22:13:00+09:00 2019-11-22T22:13:00+09:00

単純にログを示す。世の中にはマナーのよいインストーラとマナーの悪いインストーラが存在するが、果たしてVisual Studio Codeの場合はどうだろうか。そもそもWindowsのインストールの方式がインストーラにやられたい放題なのが良くないのとレジストリとかいう意味不明方式がダメだというのは別として。Microsftなんだからマナー良くあってくれ。

それはそれとしてログをとることはインストール時に選択したオプションなどを記録できるので便利である。全ての設定は後から変更できるべきであるがマナーが悪いとそうはいかないのである。そういう時のために。

https://code.visualstudio.com/download

VSCodeのインストーラをダウンロードするページのスクリーンショット

からインストーラをダウンロードする。WindowsなのでWindowsボタンを押して何も考えずに実行。64bitのUser Installerが勝手に選択された。全てのユーザに対してインストールするならここでSystem Installerを選択しなければならなかった。いや、インストーラを起動してから選ばせてくれよそこは。UserとSystemの意味を後から知ったのでもうUserで構わん。起動して次々とダイアログを進めていく。

ここからはうるさいので一言ずつ一気にいく。

VSCodeのインストーラを起動したスクリーンショット1枚目
同意するでしょ(これ何の意味があるねん毎回)

VSCodeのインストーラを起動したスクリーンショット2枚目
Userインストーラを選択したのでココ、UACもでない。サイズもまあ妥当ですね。

VSCodeのインストーラを起動したスクリーンショット3枚目
ショートカットはつくる

VSCodeのインストーラを起動したスクリーンショット4枚目
えーとPATHはまあつける

VSCodeのインストーラを起動したスクリーンショット5枚目
他もお前に任せた

VSCodeのインストーラを起動したスクリーンショット6枚目
そうですね。そう指定しましたよと。

VSCodeのインストーラを起動したスクリーンショット7枚目
わりと早く終わった。

VSCodeをインストーラの最後から起動して最初に起動したときのスクリーンショット
「起動する」で完了するとコレ。

ここからカスタマイズ祭りになりますね。最初は。以上。

]]>