File: dGraphView.ml

package info (click to toggle)
ocamlgraph 1.8.5-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 1,888 kB
  • ctags: 2,576
  • sloc: ml: 15,777; makefile: 513; xml: 151
file content (316 lines) | stat: -rw-r--r-- 11,035 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
(**************************************************************************)
(*                                                                        *)
(*  This file is part of OcamlGraph.                                      *)
(*                                                                        *)
(*  Copyright (C) 2009-2010                                               *)
(*    CEA (Commissariat  l'nergie Atomique)                             *)
(*                                                                        *)
(*  you can redistribute it and/or modify it under the terms of the GNU   *)
(*  Lesser General Public License as published by the Free Software       *)
(*  Foundation, version 2.1, with a linking exception.                    *)
(*                                                                        *)
(*  It is distributed in the hope that it will be useful,                 *)
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
(*  GNU Lesser General Public License for more details.                   *)
(*                                                                        *)
(*  See the file ../LICENSE for more details.                             *)
(*                                                                        *)
(*  Authors:                                                              *)
(*    - Julien Signoles  (Julien.Signoles@cea.fr)                         *)
(*    - Jean-Denis Koeck (jdkoeck@gmail.com)                              *)
(*    - Benoit Bataille  (benoit.bataille@gmail.com)                      *)
(*                                                                        *)
(**************************************************************************)

open DGraphViewItem

let ($) f x = f x

let distance x y = if x > y then x - y else y - x

class type ['vertex, 'edge, 'cluster] view = object
  inherit GnoCanvas.canvas
  method model : ('vertex, 'edge, 'cluster) DGraphModel.abstract_model
  method get_node : 'vertex -> 'vertex view_item
  method get_edge : 'edge -> 'edge view_item
  method get_cluster : 'cluster -> 'cluster view_item
  method iter_nodes:  ('vertex view_item -> unit) -> unit
  method iter_edges: ('vertex view_item -> 'vertex view_item -> unit) -> unit
  method iter_edges_e:  ('edge view_item -> unit) -> unit
  method iter_clusters: ('cluster view_item -> unit) -> unit
  method iter_succ: ('vertex view_item -> unit) -> 'vertex view_item -> unit
  method iter_pred: ('vertex view_item -> unit) -> 'vertex view_item -> unit
  method iter_succ_e: ('edge view_item -> unit) -> 'vertex view_item -> unit
  method iter_pred_e: ('edge view_item -> unit) -> 'vertex view_item -> unit
  method iter_associated_vertex:
    ('vertex view_item -> unit) -> 'vertex view_item -> unit
  method mem_edge: 'vertex view_item -> 'vertex view_item -> bool
  method find_edge: 'vertex view_item -> 'vertex view_item -> 'edge view_item
  method src: 'edge view_item -> 'vertex view_item
  method dst: 'edge view_item -> 'vertex view_item
  method zoom_factor : float
  method zoom_to : float -> unit
  method zoom_in : unit -> unit
  method zoom_out : unit -> unit
  method adapt_zoom : unit -> unit
  method center_node: 'vertex view_item -> unit
  method set_zoom_padding: float -> unit
  method connect_highlighting_event: unit -> unit
  method highlight: ?color: int32 * int32 -> 'vertex view_item -> unit
  method dehighlight: 'vertex view_item -> unit
end

module type S = sig

  type vertex
  type edge
  type cluster

  val view:
    ?aa:bool (** Anti-aliasing *) ->
    ?delay_node:(vertex -> bool) ->
    ?delay_edge:(edge -> bool) ->
    ?delay_cluster:(cluster -> bool) ->
    ?border_width:int ->
    ?width:int ->
    ?height:int ->
    ?packing:(GObj.widget -> unit) ->
    ?show:bool ->
    (vertex, edge, cluster) DGraphModel.abstract_model ->
    (vertex, edge, cluster) view
(** View as a Gnome Canvas.
    Support zooming and scrolling. *)

end

(* ************************************************************************* *)
(** View from a model *)
(* ************************************************************************* *)

module Make(V: Sig.HASHABLE)(E: Sig.HASHABLE)(C: Sig.HASHABLE) = struct

  type vertex = V.t
  type edge = E.t
  type cluster = C.t

  module HV = Hashtbl.Make(V)
  module HE = Hashtbl.Make(E)
  module HC = Hashtbl.Make(C)

  (* Widget derived from Gnome Canvas.
     Supports zooming and scrolling *)
  class view
    ?delay_node ?delay_edge ?delay_cluster
    obj
    (model : (V.t, E.t, C.t) DGraphModel.abstract_model)
    =
    let delay f v = match f with None -> false | Some f -> f v in
    let (x1, y1), (x2, y2) = model#bounding_box in
  object(self)

    inherit GnoCanvas.canvas obj

    method model = model

    (* Hash tables from the model to the view items*)
    val node_hash : V.t view_item HV.t = HV.create 17
    val edge_hash : E.t view_item HE.t = HE.create 17
    val cluster_hash : C.t view_item HC.t = HC.create 7

    (* Canvas items creation *)

    method private add_vertex vertex =
      try
	let layout = model#get_vertex_layout vertex in
	let item =
	  view_node
	    ~delay:(delay delay_node vertex)
	    ~view:(self :> common_view) ~vertex ~layout ()
	in
	HV.add node_hash vertex item
      with Not_found ->
	assert false

    method private add_edge edge =
      try
	let layout = model#get_edge_layout edge in
	let item =
	  view_edge
	    ~delay:(delay delay_edge edge)
	    ~view:(self:>common_view) ~edge ~layout ()
	in
	HE.add edge_hash edge item
      with Not_found ->
	assert false

    method private add_cluster cluster =
      let layout = model#get_cluster_layout cluster in
      let item =
	view_cluster
	  ~delay:(delay delay_cluster cluster)
	  ~view:(self :> common_view) ~cluster ~layout ()
      in
      HC.add cluster_hash cluster item

    (* From model to view items *)

    method get_node n =
      try HV.find node_hash n with Not_found -> assert false

    method get_edge e =
      try HE.find edge_hash e with Not_found -> assert false

    method get_cluster c =
      try HC.find cluster_hash c with Not_found -> assert false

    (* Iterate on nodes and edges *)
    method iter_nodes f = HV.iter (fun _ v -> f v) node_hash
    method iter_edges_e f = HE.iter (fun _ e -> f e) edge_hash
    method iter_clusters f = HC.iter (fun _ c -> f c) cluster_hash

    method iter_edges f =
      model#iter_edges (fun v1 v2 -> f (self#get_node v1) (self#get_node v2))

    (* Iterate on successors of a node *)
    method iter_succ f (node: 'v view_item) =
      let f' v = f (self#get_node v) in
      model#iter_succ f' node#item

    (* Iterate on predecessors of a node *)
    method iter_pred f (node: 'v view_item) =
      let f' v = f (self#get_node v) in
      model#iter_pred f' node#item

    method iter_succ_e f (node: 'v view_item) =
      let f' e = f (self#get_edge e) in
      model#iter_succ_e f' node#item

    method iter_pred_e f (node: 'v view_item) =
      let f' e = f (self#get_edge e) in
      model#iter_pred_e f' node#item

    (* Iterate on associated nodes *)
    method iter_associated_vertex f (node: 'v view_item) =
      let f' v = f (self#get_node v) in
      model#iter_associated_vertex f' node#item

    (* Membership functions *)

    method mem_edge (n1:'v view_item) (n2:'v view_item) =
      model#mem_edge n1#item n2#item

    method find_edge (n1:'v view_item) (n2:'v view_item) =
      self#get_edge (model#find_edge n1#item n2#item)

    method src (e: 'e view_item) = self#get_node (model#src e#item)
    method dst (e: 'e view_item) = self#get_node (model#dst e#item)

    (* Zoom factor *)
    val mutable zoom_f = 1.
    method zoom_factor = zoom_f

    val mutable zoom_padding = 0.1
    method set_zoom_padding n = zoom_padding <- n

    method private set_zoom_f x = if x > 1e-10 then zoom_f <- x

    (* Zoom to a particular factor *)
    method zoom_to x =
      self#set_zoom_f x;
      self#set_pixels_per_unit zoom_f;
      self#iter_clusters (fun c -> c#zoom_text zoom_f);
      self#iter_nodes (fun n -> n#zoom_text zoom_f);
      self#iter_edges_e (fun e -> e#zoom_text zoom_f)

    method zoom_in () = self#zoom_to (zoom_f +. zoom_padding *. zoom_f)
    method zoom_out () = self#zoom_to (zoom_f -. zoom_padding *. zoom_f)
    method center_node (node:V.t view_item) = 
      node#center ()
(*      self#zoom_in ();*)


    method adapt_zoom () =
      let width = self#hadjustment#page_size in
      let height = self#vadjustment#page_size in
      let w_zoom = width /. abs_float (x1-.x2) in
      let h_zoom = height /. abs_float (y1-.y2) in
      self#zoom_to (min 1. (min w_zoom h_zoom))

    (* EVENTS *)

    (* Zoom with the keys *)
    method private zoom_keys_ev ev =
      match GdkEvent.Key.keyval ev with
      | k when k = GdkKeysyms._KP_Subtract -> self#zoom_out (); true
      | k when k = GdkKeysyms._KP_Add -> self#zoom_in (); true
      | _ -> false

    (* Zoom with the mouse *)
    method private zoom_mouse_ev ev =
      match GdkEvent.Scroll.direction ev with
      | `UP -> self#zoom_in (); true
      | `DOWN -> self#zoom_out (); true
      | _ -> false

    method highlight ?color node =
      let h e = e#highlight ?color () in
      h node;
      self#iter_associated_vertex (fun v ->
	h v;
	self#iter_succ_e h v;
	self#iter_pred_e h v)
	node

    method dehighlight node =
      let h e = e#dehighlight () in
      h node;
      self#iter_associated_vertex (fun v ->
	h v;
	self#iter_succ_e h v;
	self#iter_pred_e h v)
	node

    method connect_highlighting_event () =
      let connect node =
	let callback = function
	  | `MOTION_NOTIFY _ -> self#highlight node; false
	  | `LEAVE_NOTIFY _ -> self#dehighlight node; false
	  | _ -> false
	in
	node#connect_event ~callback
      in
      self#iter_nodes connect

    initializer
      (* Create and add items from the model vertices, edges and clusters *)
      model#iter_clusters self#add_cluster;
      model#iter_vertex self#add_vertex;
      model#iter_edges_e self#add_edge;
      (* Scroll region management *)
      ignore $ self#set_center_scroll_region true;
      ignore $ self#set_scroll_region ~x1 ~y1 ~x2 ~y2 ;
      (* Attach zoom events *)
      ignore $ self#event#connect#key_press self#zoom_keys_ev;
      ignore $ self#event#connect#scroll self#zoom_mouse_ev;

  end

  let view
      ?(aa=false) ?delay_node ?delay_edge ?delay_cluster
      ?border_width ?width ?height ?packing ?show
      (model:(vertex, edge, cluster) DGraphModel.abstract_model) =
    let canvas = 
      GnoCanvas.canvas ~aa ?border_width ?width ?height ?show ?packing () 
    in
    (* Grab focus to process keyboard input *)
    ignore $ canvas#event#connect#enter_notify 
      (fun _ -> canvas#misc#grab_focus () ; false); 
    let view = 
      new view ?delay_node ?delay_edge ?delay_cluster
        (Gobject.unsafe_cast canvas#as_widget) 
        model 
    in 
    view 

end