%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
-module(reltool_fgraph).
-export([
step/2,
step/3
]).
-export([
new/0,
add/3,
set/3,
del/2,
is_defined/2,
get/2,
size/1,
foreach/2,
map/2,
foldl/3,
mapfoldl/3
]).
-compile(inline).
-compile({inline_size, 128}).
-include("reltool_fgraph.hrl").
%% KEY-VALUE STORAGE Process dictionary
new() -> [].
is_defined(Key, _Fg) ->
case get(Key) of
undefined -> false;
_ -> true
end.
get(K, _Fg) ->
case get(K) of
{_, V} -> V;
_ -> undefined
end.
add(Key, Value, Fg) ->
put(Key, {Key, Value}),
[Key|Fg].
set(Key, Value, Fg) ->
put(Key, {Key, Value}),
Fg.
size(Fg) -> length(Fg).
del(Key, Fg) ->
erase(Key),
lists:delete(Key, Fg).
foreach(Fun, Fg) ->
lists:foreach(fun
(Key) -> Fun(get(Key))
end, Fg),
Fg.
map(Fun, Fg) ->
lists:foreach(fun
(Key) -> put(Key,Fun(get(Key)))
end, Fg),
Fg.
foldl(Fun, I, Fg) ->
lists:foldl(fun
(Key, Out) ->
Fun(get(Key), Out)
end, I, Fg).
mapfoldl(Fun, I, Fg) ->
Acc = lists:foldl(fun
(Key, Out) ->
{Value, Acc} = Fun(get(Key), Out),
put(Key, Value),
Acc
end, I, Fg),
{Fg, Acc}.
step(Vs, Es) -> step(Vs, Es, {0,0}).
step(Vs, Es, Pa) ->
?MODULE:map(fun
(Node = {_, #fg_v{ type = static }}) -> Node;
({Key, Value = #fg_v{ p = {Px, Py}, v = {Vx, Vy}, type = dynamic}})
when is_float(Px), is_float(Py),
is_float(Vx), is_float(Vy) ->
F0 = {0.0,0.0},
F1 = coulomb_repulsion(Key, Value, Vs, F0),
F2 = hooke_attraction(Key, Value, Vs, Es, F1),
F3 = point_attraction(Key, Value, Pa, F2),
{Fx, Fy} = F3,
Vx1 = (Vx + ?fg_th*Fx)*?fg_damp,
Vy1 = (Vy + ?fg_th*Fy)*?fg_damp,
Px1 = Px + ?fg_th*Vx1,
Py1 = Py + ?fg_th*Vy1,
{Key, Value#fg_v{ p = {Px1, Py1}, v = {Vx1, Vy1}}};
(Node) -> Node
end, Vs).
point_attraction(_, #fg_v{ p = P0 }, Pa, {Fx, Fy})
when is_float(Fx), is_float(Fy) ->
K = 20,
L = 150,
{R, {Cx,Cy}} = composition(P0, Pa),
F = -K*?fg_stretch*(R - L),
{Fx + Cx*F, Fy + Cy*F}.
coulomb_repulsion(K0, #fg_v{ p = P0, q = Q0}, Vs, {Fx0, Fy0})
when is_float(Fx0), is_float(Fy0) ->
?MODULE:foldl(fun
({K1, _}, F) when K1 == K0 -> F;
({_, #fg_v{ p = P1, q = Q1}}, {Fx, Fy}) ->
{R, {Cx, Cy}} = composition(P0, P1),
F = ?fg_kc*(Q1*Q0)/(R*R+0.0001),
{Fx + Cx*F, Fy + Cy*F};
(_, F) -> F
end, {Fx0, Fy0}, Vs).
hooke_attraction(Key0, #fg_v{ p = P0 }, Vs, Es, {Fx0, Fy0})
when is_float(Fx0), is_float(Fy0) ->
?MODULE:foldl(fun
({{Key1,Key1}, _}, F) -> F;
({{Key1,Key2}, #fg_e{ l = L, k = K}}, {Fx, Fy}) when Key1 =:= Key0->
#fg_v{ p = P1} = ?MODULE:get(Key2, Vs),
{R, {Cx,Cy}} = composition(P0, P1),
F = -K*?fg_stretch*(R - L),
{Fx + Cx*F, Fy + Cy*F};
({{Key2,Key1}, #fg_e{ l = L, k = K}}, {Fx, Fy}) when Key1 =:= Key0->
#fg_v{ p = P1} = ?MODULE:get(Key2, Vs),
{R, {Cx,Cy}} = composition(P0, P1),
F = -K*?fg_stretch*(R - L),
{Fx + Cx*F, Fy + Cy*F};
(_, F) -> F
end, {Fx0, Fy0}, Es).
composition({Px1, Py1}, {Px0, Py0})
when is_float(Px1), is_float(Py1), is_float(Px0), is_float(Py0) ->
Dx = Px1 - Px0,
Dy = Py1 - Py0,
R = math:sqrt(Dx*Dx + Dy*Dy + 0.001),
{R, {Dx/R, Dy/R}}.