fisheye view とは、なんかインターフェイスの世界では常識っぽい、フォーカスとなる点を中心に座標をぐにょーんと引き延ばす方法です。日本語が不自由ですみません。要するにこういう変換です。



皇居あたりを中心に線路地図をぐにょーんと引き延ばしています。これを実装しようと思って計算式やサンプルプログラムを探したのですが、意外に情報が少なくて手間取りました。なので記録を残しておきます。

種類 参考文献 *1 を眺めたところ、cartesian fisheye と polar fisheye の二種類があるようです。左が cartesian で右が polar です。でもこの例だとほとんど区別が付かないですね。よく見ると端っこの方のつぶれ方が違います。

cartesian fisheye view フォーカスの座標を 、引き延ばしたい点の座標を 、壁の位置を とするとき ( になる) 、引き延ばし後の座標は以下になります。

は引き延ばし度のパラメータです。0 だと変化せず、大きいほどいっぱい引き延ばされます。x と y が独立に決まるあたりが cartesian (デカルト座標) です。フォーカスが原点以外であることを考慮してプログラムを書いたらこんな感じ。 $width , $height = [ 300 , 300 ] $x_focus , $y_focus = [ 100 , 100 ] $factor = 8 def cartesian_fisheye (x, y) x2 = cartesian_fisheye_aux(x, $x_focus , 0 , $width ) y2 = cartesian_fisheye_aux(y, $y_focus , 0 , $height ) [x2, y2] end def cartesian_fisheye_aux (x, x_focus, x_low, x_high) x_max = x > x_focus ? x_high - x_focus : x_low - x_focus x = (x - x_focus).abs ( $factor + 1 ) * x_max * x / ( $factor * x + x_max.abs) + x_focus end ローカル関数を定義できない Ruby が死ぬほど歯がゆい瞬間！ Proc 使うのはなぜか嫌。





polar fisheye view 今度は座標を全部極座標で扱います。フォーカスの座標を 、引き延ばしたい点の座標を 、その方向に突き進んで壁に当たる位置を とするとき ( になる) 、引き延ばし後の座標は以下になります。

が保存されるのが特徴です。以下、フォーカスが原点以外であることを考慮したプログラム。 def polar_fisheye (x, y) r, t = cartesian_to_polar(x, y) r_max = [ (x > $x_focus ? ( $width - $x_focus ) : $x_focus ) / Math .cos(t).abs, (y > $y_focus ? ( $height - $y_focus ) : $y_focus ) / Math .sin(t).abs ].min r2 = ( $factor + 1 ) * r_max * r / ( $factor * r + r_max) polar_to_cartesian(r2, t) end def cartesian_to_polar (x, y, x_polar = $x_focus , y_polar = $y_focus ) x -= x_polar; y -= y_polar [ Math .hypot(y, x), Math .atan2(y, x)] end def polar_to_cartesian (r, t, x_polar = $x_focus , y_polar = $y_focus ) [r * Math .cos(t) + x_polar, r * Math .sin(t) + y_polar] end

おまけ: 式のグラフ化 どちらの fisheye も以下の関数に基づいて変換してます。



これをグラフ化するとこんな感じです (d = 8 の場合) 。



これをグラフ化するとこんな感じです (d = 8 の場合) 。