アンカー処理を制約に
GraphインスタンスがVector anchorListを持つようになりました。また、Anchorクラスを作成し、これがアンカー対象の頂点targetと、アンカーされる位置positionを持つようになりました。現状では頂点のアンカーは「優先度の高い『速度を0にせよ』命令による他の物理法則由来の速度の上書き」によって実現されているためpositionは使われていませんが、これは汎用性に乏しいので将来的に「『ターゲット頂点の位置が規定位置から一定誤差以内に収まる』という制約」によって実現することを目指してつけたものです。
これに伴いMassPoint.anchoredは削除されました。
Vector AnchorListはHashtable AnchorTableに変更されました。キーがVertexで値がdouble[] positionです。 ドラッグ中に頂点がマウスについて動くのを、従来は「マウスダウン時にanchored(他の力による速度が0で上書きされる)状態にして、マウスムーブ時にpositionをマウスカーソルの位置から計算して指定」という方法で実装していました。しかし、将来的に制約で実装することを考えるとこれは「マウスダウン時にアンカー(頂点を特定位置に縛り付ける制約)をセットし、マウスムーブ時にアンカーの位置を変更」となる方がよさそうです。そうすると、特定頂点につけられているアンカーの位置を頻繁に変更するわけですから Vectorは適切なデータ構造ではありません。VertexをキーにしたHashtableが適切でしょう。一つの頂点が複数の異なる位置にアンカーされることはありえないということも表現できて一石二鳥。
VecWithPrior(優先度付きベクトル)という癌を取り除くために、一時的にアンカーの優先度を他の力同様の0に変更。つまり「速度ベクトルを0で上書き」ではなく「頂点の位置を目的の位置へ移動するような速度がかかる」に変更したと言うことです。こうすると、頂点をドラッグして動かしたときに「頂点から生えている見えないバネを持って引っ張っている」ような状態になり、後ろから他のバネが引っ張るので頂点がマウスポインタから少し離れた位置に漂ってしまいます。
Constarin(制約)はPhysicalLaw(物理法則)の子クラスではなく、親クラスなのかも知れません。つまり「満たすべき条件を満たしているかどうかのチェック」が常にTrueを返すような特殊なConstrainがPhysicalLawである、という設計です。PhysicalLawの適用はPhysicalLaw#applyなのですが、チェックする関数の名前は何にしましょうかね。boolean validateですかね。
validateとapplyは構造的に非常に似ています。don't repeat yourself。applyがisSatisfied(制約が満足されたか)を返すようにすればいいですね。SpringEdgeやRepulsionは常にtrueを返し、Anchorは動かすべき頂点がなかった場合のみtrueを返す、と。
ViewportTransformerクラスにscaling, invScalingメソッドを追加しました。絶対ベクトルのビューポート変換だけではなく、相対ベクトルの変換もサポートする必要があると思ったので。これで、たとえば「アンカーの位置と頂点の位置のずれがスクリーン座標系で0.1ピクセル未満」なんて処理を行うのに「アンカーの位置をビューポート変換、頂点の位置もビューポート変換、その差を取って0.1と比較」とやらなくても「アンカーの位置と頂点の位置の差分ベクトルをビューポート(スケーリング)変換、その大きさを0.1と比較」とやれるようになります。
一時期「semi-structured dataの可視化」を考えていたせいで、グラフを保持するフィールドの名前がstructure。でもクラスはGraph。フィールドの名前をgraphに変更しました。
Aggregator.aggregateを修正して、制約をサポートしました。制約もPhysicalLawクラスの子クラスですが、applyTillSatisfyメソッドがtrueを返します。
VecWithPrior(優先度付きベクトル)を使用しているところを全部修正してをVecWithPrior.javaを削除しました。コミット完了。
これでVecWithPriorという癌も取り除けたので、他の制約も入れやすくなったはずです。たとえば天井に張り付くとか。NarVisualizerあたりを、このライブラリを使って再生してみようかな。