4
%% Copyright Ericsson AB 2009. All Rights Reserved.
4
%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
6
6
%% The contents of this file are subject to the Erlang Public License,
7
7
%% Version 1.1, (the "License"); you may not use this file except in
8
8
%% compliance with the License. You should have received a copy of the
9
9
%% Erlang Public License along with this software. If not, it can be
10
10
%% retrieved online at http://www.erlang.org/.
12
12
%% Software distributed under the License is distributed on an "AS IS"
13
13
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
14
14
%% the License for the specific language governing rights and limitations
15
15
%% under the License.
19
19
-module(reltool_fgraph_win).
111
111
Pid = spawn_link(fun() -> init([Parent, Me, Env, Options]) end),
112
112
receive {Pid, {?MODULE, Panel}} -> {Pid,Panel} end.
114
114
init([ParentWin, Pid, Env, Options]) ->
117
117
BReset = wxButton:new(ParentWin, ?reset, [{label,"Reset"}]),
118
118
BFreeze = wxButton:new(ParentWin, ?freeze, [{label,"Freeze"}]),
119
119
BLock = wxButton:new(ParentWin, ?lock, [{label,"Lock"}]),
120
120
BUnlock = wxButton:new(ParentWin, ?unlock, [{label,"Unlock"}]),
121
121
BDelete = wxButton:new(ParentWin, ?delete, [{label,"Delete"}]),
123
SQ = wxSlider:new(ParentWin, ?q_slider, ?default_q, 1, 500, [{style, ?wxVERTICAL}]),
124
SL = wxSlider:new(ParentWin, ?l_slider, ?default_l, 1, 500, [{style, ?wxVERTICAL}]),
125
SK = wxSlider:new(ParentWin, ?k_slider, ?default_k, 1, 500, [{style, ?wxVERTICAL}]),
123
SQ = wxSlider:new(ParentWin, ?q_slider, ?default_q, 1, 500,
124
[{style, ?wxVERTICAL}]),
125
SL = wxSlider:new(ParentWin, ?l_slider, ?default_l, 1, 500,
126
[{style, ?wxVERTICAL}]),
127
SK = wxSlider:new(ParentWin, ?k_slider, ?default_k, 1, 500,
128
[{style, ?wxVERTICAL}]),
126
129
Win = wxWindow:new(ParentWin, ?wxID_ANY, Options),
128
131
ButtonSizer = wxBoxSizer:new(?wxVERTICAL),
129
132
wxSizer:add(ButtonSizer, BReset),
130
133
wxSizer:add(ButtonSizer, BFreeze),
141
144
WindowSizer = wxBoxSizer:new(?wxHORIZONTAL),
142
145
wxSizer:add(WindowSizer, ButtonSizer, [{flag, ?wxEXPAND}, {proportion, 0}]),
143
146
wxSizer:add(WindowSizer, Win, [{flag, ?wxEXPAND}, {proportion, 1}]),
145
148
wxButton:setToolTip(BReset, "Remove selection and unlock all nodes."),
146
149
wxButton:setToolTip(BFreeze, "Start/stop redraw of screen."),
147
150
wxButton:setToolTip(BLock, "Lock all selected nodes."),
148
151
wxButton:setToolTip(BUnlock, "Unlock all selected nodes."),
149
152
wxButton:setToolTip(BDelete, "Delete all selected nodes."),
151
wxButton:setToolTip(SQ, "Control repulsive force. This can also be controlled with the mouse wheel on the canvas."),
154
wxButton:setToolTip(SQ, "Control repulsive force. This can also be"
155
" controlled with the mouse wheel on the canvas."),
152
156
wxButton:setToolTip(SL, "Control link length."),
153
157
wxButton:setToolTip(SK, "Control attractive force. Use with care."),
154
wxButton:setToolTip(Win,
155
"Drag mouse while left mouse button is pressed to perform various operations. "
156
"Combine with control key to select. Combine with shift key to lock single node."),
158
wxButton:setToolTip(Win,
159
"Drag mouse while left mouse button is pressed "
160
"to perform various operations. "
161
"Combine with control key to select. Combine "
162
"with shift key to lock single node."),
158
164
wxButton:connect(BReset, command_button_clicked),
159
165
wxButton:connect(BFreeze, command_button_clicked),
160
166
wxButton:connect(BLock, command_button_clicked),
161
167
wxButton:connect(BUnlock, command_button_clicked),
162
168
wxButton:connect(BDelete, command_button_clicked),
164
170
wxWindow:connect(SQ, command_slider_updated),
165
171
wxWindow:connect(SL, command_slider_updated),
166
172
wxWindow:connect(SK, command_slider_updated),
168
wxWindow:connect(Win, enter_window),
174
wxWindow:connect(Win, enter_window),
169
175
wxWindow:connect(Win, move),
170
176
wxWindow:connect(Win, motion),
171
177
wxWindow:connect(Win, mousewheel),
174
180
wxWindow:connect(Win, left_up),
175
181
wxWindow:connect(Win, right_down),
176
182
wxWindow:connect(Win, paint, [{skip, true}]),
178
184
Pen = wxPen:new({0,0,0}, [{width, 3}]),
179
185
Font = wxFont:new(12, ?wxSWISS, ?wxNORMAL, ?wxNORMAL,[]),
180
186
Brush = wxBrush:new({0,0,0}),
182
188
Pid ! {self(), {?MODULE, WindowSizer}},
184
190
wxWindow:setFocus(Win), %% Get keyboard focus
186
192
Vs = reltool_fgraph:new(),
187
193
Es = reltool_fgraph:new(),
190
196
Ticker = spawn_link(fun() -> ticker_init(Me) end),
192
198
loop( #state{ parent_pid = Pid,
216
222
P = {float(450 + random:uniform(100)),
217
223
float(450 + random:uniform(100))},
218
G#graph{ vs = reltool_fgraph:add(Key, #fg_v{ p = P, m = M, q = Q, color = Color}, Vs)}.
224
G#graph{ vs = reltool_fgraph:add(Key,
225
#fg_v{ p = P, m = M, q = Q, color = Color},
220
228
graph_change_node(Key, Color, G) ->
221
229
case reltool_fgraph:get(Key, G#graph.vs) of
225
G#graph{ vs = reltool_fgraph:set(Key, V#fg_v{ color = Color }, G#graph.vs)}
233
G#graph{ vs = reltool_fgraph:set(Key, V#fg_v{ color = Color },
228
237
graph_del_node(Key, G = #graph{ vs = Vs0, es = Es0}) ->
231
240
G#graph{ vs = Vs, es = Es }.
233
242
graph_add_link(Key0, Key1, G = #graph{ es = Es}) ->
234
K = 60.0, % attractive force
243
K = 60.0, % attractive force
235
244
L = 5.0, % spring length
236
245
G#graph{ es = reltool_fgraph:add({Key0, Key1}, #fg_e{ k = K, l = L}, Es) }.
249
258
D = timer:now_diff(T1, T0)/1000,
250
259
case round(40 - D) of
251
260
Ms when Ms < 0 ->
252
%io:format("ticker: wait is 0 ms [fg ~7s ms] [fps ~7s]~n", [s(D), s(1000/D)]),
261
%io:format("ticker: wait is 0 ms [fg ~7s ms] [fps ~7s]~n",
262
% [s(D), s(1000/D)]),
253
263
ticker_loop(Pid, 0);
255
%io:format("ticker: wait is ~3s ms [fg ~7s ms] [fps ~7s]~n", [s(Ms), s(D), s(1000/40)]),
265
%io:format("ticker: wait is ~3s ms [fg ~7s ms] [fps ~7s]~n",
266
% [s(Ms), s(D), s(1000/40)]),
256
267
ticker_loop(Pid, Ms)
260
delete_edges(Es, []) ->
271
delete_edges(Es, []) ->
262
273
delete_edges(Es, [Key|Keys]) ->
263
274
Edges = reltool_fgraph:foldl(fun
295
306
wxSlider:setValue(S#state.k_slider, K),
296
307
Es = set_length(L, G#graph.es),
297
308
Es2 = set_spring(K, Es),
299
Vs2 = reltool_fgraph:map(fun({Key, V}) ->
300
{Key, V#fg_v{selected = false, type = dynamic, q = Q}}
304
{Xs, Ys} = reltool_fgraph:foldl(fun({_Key, #fg_v{p = {X, Y}}}, {Xs, Ys}) ->
311
reltool_fgraph:map(fun({Key, V}) ->
312
{Key, V#fg_v{selected = false,
319
reltool_fgraph:foldl(fun({_Key,
320
#fg_v{p = {X, Y}}}, {Xs, Ys}) ->
309
325
%% io:format("Before: ~p\n", [G#graph.offset]),
311
327
case length(Xs) of
315
MeanX = (lists:sum(Xs) / N),
331
MeanX = (lists:sum(Xs) / N),
316
332
MeanY = (lists:sum(Ys) / N),
317
333
{SizeX, SizeY} = wxWindow:getSize(S#state.window),
318
%% io:format("Min: ~p\n", [{lists:min(Xs), lists:min(Ys)}]),
319
%% io:format("Mean: ~p\n", [{MeanX, MeanY}]),
320
%% io:format("Max: ~p\n", [{lists:max(Xs), lists:max(Ys)}]),
334
%% io:format("Min: ~p\n",
335
%% [{lists:min(Xs), lists:min(Ys)}]),
336
%% io:format("Mean: ~p\n",
337
%% [{MeanX, MeanY}]),
338
%% io:format("Max: ~p\n",
339
%% [{lists:max(Xs), lists:max(Ys)}]),
321
340
%% io:format("Size: ~p\n", [{SizeX, SizeY}]),
322
341
%% {XM - (XS / 2), YM - (YS / 2)}
323
342
%% {0 - lists:min(Xs) + 20, 0 - lists:min(Ys) + 20}
324
343
{0 - MeanX + (SizeX / 2), 0 - MeanY + (SizeY / 2)}
326
345
%% io:format("After: ~p\n", [Offset]),
327
loop(S, G#graph{vs = Vs2, es = Es2, offset = Offset, offset_state = false});
346
loop(S, G#graph{vs = Vs2,
349
offset_state = false});
328
350
#wx{id = ?freeze, event = #wxCommand{type=command_button_clicked}} ->
329
351
%% Start/stop redraw of screen
354
376
loop(S, G#graph{ vs = Vs });
355
377
#wx{id = ?delete, event = #wxCommand{type=command_button_clicked}} ->
356
378
%% Delete all selected nodes
357
{Vs1, Keys} = reltool_fgraph:foldl(fun
358
({Key, #fg_v{ selected = true}}, {Vs, Ks}) ->
359
{reltool_fgraph:del(Key,Vs), [Key|Ks]};
360
(_, {Vs, Ks}) -> {Vs, Ks}
380
reltool_fgraph:foldl(fun
382
#fg_v{ selected = true}},
384
{reltool_fgraph:del(Key,Vs),
361
388
end, {G#graph.vs,[]}, G#graph.vs),
362
389
Es = delete_edges(G#graph.es, Keys),
363
390
loop(S, G#graph{ vs = Vs1, es = Es});
368
395
#wx{id = ?move, event = #wxCommand{type=command_button_clicked}} ->
369
396
loop(S#state{ mouse_act = ?move }, G);
371
#wx{id = ?q_slider, event = #wxCommand{type=command_slider_updated, commandInt = Q}} ->
398
#wx{id = ?q_slider, event = #wxCommand{type=command_slider_updated,
372
400
loop(S, G#graph{ vs = set_charge(Q, G#graph.vs)});
373
#wx{id = ?l_slider, event = #wxCommand{type=command_slider_updated, commandInt = L}} ->
401
#wx{id = ?l_slider, event = #wxCommand{type=command_slider_updated,
374
403
loop(S, G#graph{ es = set_length(L, G#graph.es)});
375
#wx{id = ?k_slider, event = #wxCommand{type=command_slider_updated, commandInt = K}} ->
404
#wx{id = ?k_slider, event = #wxCommand{type=command_slider_updated,
376
406
loop(S, G#graph{ es = set_spring(K, G#graph.es)});
377
407
#wx{event=#wxKey{type=key_up, keyCode = 127}} -> % delete
379
reltool_fgraph:foldl(fun({Key, #fg_v{ selected = true}}, {Vs, Ks}) ->
380
{reltool_fgraph:del(Key,Vs), [Key|Ks]};
384
{G#graph.vs,[]}, G#graph.vs),
409
reltool_fgraph:foldl(fun({Key,
410
#fg_v{ selected = true}},
412
{reltool_fgraph:del(Key,Vs),
417
{G#graph.vs,[]}, G#graph.vs),
385
418
Es = delete_edges(G#graph.es, Keys),
386
419
loop(S, G#graph{ vs = Vs1, es = Es});
387
420
#wx{event=#wxKey{type=key_up}} ->
401
438
S#state.mouse_act =:= ?select ->
402
439
loop(S, mouse_left_down_select(G, {X,Y}))
404
#wx{event=#wxMouse{type=motion, shiftDown=Shift, controlDown=Ctrl, x=X, y=Y}} ->
441
#wx{event=#wxMouse{type=motion,
407
448
loop(S, mouse_motion_move(G, {X,Y}));
412
453
S#state.mouse_act =:= ?select ->
413
454
loop(S, mouse_motion_select(G, {X,Y}))
415
#wx{event=#wxMouse{type=left_up, shiftDown=Shift, controlDown=Ctrl, x=X, y=Y}} ->
456
#wx{event=#wxMouse{type=left_up,
458
controlDown=Ctrl, x=X, y=Y}} ->
418
461
loop(S, mouse_left_up_move(G, {X,Y}, Shift));
467
510
{SizeX, SizeY} = wxWindow:getSize(S#state.window),
468
Vs = reltool_fgraph:step(G#graph.vs, G#graph.es, {SizeX/2.0 - 20.0, SizeY/2.0}),
511
Vs = reltool_fgraph:step(G#graph.vs,
513
{SizeX/2.0 - 20.0, SizeY/2.0}),
469
514
case S#state.is_frozen of
471
516
Req ! {self(), ok};
495
540
G#graph{ offset_state = {X,Y}};
497
V = #fg_v{ type = Type} = reltool_fgraph:get(Key, Vs),
498
G#graph{ vs = reltool_fgraph:set(Key, V#fg_v{ type = moving}, Vs), select = {node, Key, Type, X, Y} }
542
V = #fg_v{ type = Type} = reltool_fgraph:get(Key, Vs),
543
G#graph{ vs = reltool_fgraph:set(Key,
544
V#fg_v{ type = moving}, Vs),
545
select = {node, Key, Type, X, Y} }
501
548
coord_to_key(#graph{vs = Vs, offset = {Xo, Yo}}, {X, Y}) ->
504
reltool_fgraph:foldl(fun({Key, #fg_v{ p = {Px, Py}}}, _) when abs(Px - Xr) < 10,
505
abs(Py - Yr) < 10 -> {true, Key};
551
reltool_fgraph:foldl(fun({Key, #fg_v{ p = {Px, Py}}}, _)
552
when abs(Px - Xr) < 10,
509
559
mouse_left_up_select(G, {_X,_Y}) ->
510
560
case G#graph.select of
557
607
G#graph{ vs = reltool_fgraph:set(Key, V2, Vs) };
558
608
mouse_motion_move(G, {X,Y}) ->
559
609
case G#graph.offset_state of
561
611
{X0, Y0} = G#graph.offset,
562
612
G#graph{ offset_state = {X,Y},
563
613
offset = {X0 - (X1 - X), Y0 - (Y1 - Y)} };
612
664
draw_line(DC, {X1,Y1}, {X0,Y1}, {0,0}),
613
665
draw_line(DC, {X0,Y0}, {X0,Y1}, {0,0}),
615
draw_select_box(_DC, _) ->
667
draw_select_box(_DC, _) ->
618
670
draw_es(DC, Vs, Es, Po, Pen, Brush) ->
619
671
reltool_fgraph:foreach(fun
620
672
({{K1, K2}, _}) ->
621
#fg_v{ p = P1} = reltool_fgraph:get(K1, Vs),
622
#fg_v{ p = P2} = reltool_fgraph:get(K2, Vs),
673
#fg_v{ p = P1} = reltool_fgraph:'get'(K1, Vs),
674
#fg_v{ p = P2} = reltool_fgraph:'get'(K2, Vs),
623
675
draw_arrow(DC, P1, P2, Po, Pen, Brush)
650
702
wxDC:drawPolygon(DC, Points, []).
652
704
draw_line(DC, {X0,Y0}, {X1, Y1}, {X, Y}) ->
653
wxDC:drawLine(DC, {round(X0 + X), round(Y0 + Y)}, {round(X1 + X), round(Y1 + Y)}).
706
{round(X0 + X), round(Y0 + Y)},
707
{round(X1 + X), round(Y1 + Y)}).
655
709
draw_vs(DC, Vs, {Xo, Yo}, Pen, Brush) ->
656
reltool_fgraph:foreach(fun({Key, #fg_v{ p ={X, Y}, color = Color, selected = Sel}}) ->
710
reltool_fgraph:foreach(fun({Key,
661
718
wxBrush:setColour(Brush, ?color_bg),
662
719
wxDC:setPen(DC,Pen),
663
720
wxDC:setBrush(DC, Brush),
664
SelProps = {round(X-12 + Xo), round(Y-12 + Yo), 24, 24},
665
wxDC:drawRoundedRectangle(DC, SelProps, float(?ARC_R)),
721
SelProps = {round(X-12 + Xo),
725
wxDC:drawRoundedRectangle(DC,
672
734
wxPen:setColour(Pen, ?color_default),
673
wxBrush:setColour(Brush, ?color_default_bg);
675
wxPen:setColour(Pen, ?color_alternate),
676
wxBrush:setColour(Brush, ?color_alternate_bg);
735
wxBrush:setColour(Brush,
740
wxBrush:setColour(Brush,
741
?color_alternate_bg);
677
742
{FgColor, BgColor} ->
678
743
wxPen:setColour(Pen, FgColor),
679
wxBrush:setColour(Brush, BgColor);
744
wxBrush:setColour(Brush, BgColor);
681
746
wxPen:setColour(Pen, Color),
682
747
wxBrush:setColour(Brush, Color)
684
749
wxDC:setPen(DC,Pen),
685
750
wxDC:setBrush(DC, Brush),
686
NodeProps = {round(X-8 + Xo),round(Y-8 + Yo),17,17},
687
wxDC:drawRoundedRectangle(DC, NodeProps, float(?ARC_R)),
688
wxDC:drawText(DC, String, {round(X + Xo), round(Y + Yo)}),
751
NodeProps = {round(X-8 + Xo),
752
round(Y-8 + Yo),17,17},
753
wxDC:drawRoundedRectangle(DC,
695
766
draw_text(DC, Nvs, Nes, _KE) ->