/[mcclim]/mcclim/graph-formatting.lisp
ViewVC logotype

Contents of /mcclim/graph-formatting.lisp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.21 - (hide annotations)
Sun Sep 16 22:39:22 2007 UTC (6 years, 7 months ago) by rgoldman
Branch: MAIN
Changes since 1.20: +57 -53 lines
Removed destructive modification of format-graph-from-roots &rest argument.
1 gilbert 1.1 ;;; -*- Mode: Lisp; Syntax: Common-Lisp; Package: CLIM-INTERNALS; -*-
2     ;;; ---------------------------------------------------------------------------
3     ;;; Title: Graph Formatting
4     ;;; Created: 2002-08-13
5     ;;; License: LGPL (See file COPYING for details).
6 rgoldman 1.21 ;;; $Id: graph-formatting.lisp,v 1.21 2007/09/16 22:39:22 rgoldman Exp $
7 gilbert 1.1 ;;; ---------------------------------------------------------------------------
8    
9     ;;; (c) copyright 2002 by Gilbert Baumann
10 rgoldman 1.16 ;;; (c) copyright 2005 by Robert P. Goldman
11 gilbert 1.1
12     ;;; This library is free software; you can redistribute it and/or
13     ;;; modify it under the terms of the GNU Library General Public
14     ;;; License as published by the Free Software Foundation; either
15     ;;; version 2 of the License, or (at your option) any later version.
16     ;;;
17     ;;; This library is distributed in the hope that it will be useful,
18     ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19     ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20     ;;; Library General Public License for more details.
21     ;;;
22     ;;; You should have received a copy of the GNU Library General Public
23     ;;; License along with this library; if not, write to the
24     ;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25     ;;; Boston, MA 02111-1307 USA.
26    
27 mikemac 1.7 (in-package :clim-internals)
28 gilbert 1.1
29     ;;;; Notes
30    
31     ;; - Now what exactly are layout-graph-nodes and layout-graph-edges
32     ;; supposed to do? If LAYOUT-GRAPH-NODES is only responsible for
33     ;; laying out the node output records, why does it get the
34     ;; arc-drawer? If it should also draw the edges why then is there
35     ;; the other function? --GB 2002-08-13
36    
37     ;; - There is this hash table initarg to graph-output-records? Should
38     ;; FORMAT-GRAPH-FROM-ROOTS pass a suitable hash table for the given
39     ;; 'duplicate-test', if so why it is passed down and why is it not
40     ;; restricted to the set of hash test functions? --GB 2002-08-13
41    
42 thenriksen 1.19 ;; - What is the purpose of (SETF GRAPH-NODE-CHILDREN) and
43     ;; (SETF GRAPH-NODE-PARENTS)? --GB 2002-08-14
44 gilbert 1.3
45     ;; - FORMAT-GRAPH-FROM-ROOTS passes the various options on to the
46     ;; instantiation of the graph-output-record class, so that the
47     ;; individual classes can choose appropriate defaults. --GB 2002-08-14
48    
49     ;; - In the same spirit, a non given ARC-DRAWER option is passed as it
50     ;; is, that is being NIL, to LAYOUT-GRAPH-EDGES so that the concrete
51     ;; graph-output-record can choose a default. --GB 2002-08-14
52 gilbert 1.1
53     ;;;; Declarations
54    
55     ;; format-graph-from-roots
56    
57     (defgeneric graph-root-nodes (graph-output-record))
58     (defgeneric (setf graph-root-nodes) (new-value graph-output-record))
59     (defgeneric generate-graph-nodes (graph-output-record stream root-objects
60     object-printer inferior-producer
61     &key duplicate-key duplicate-test))
62     (defgeneric layout-graph-nodes (graph-output-record stream arc-drawer arc-drawing-options))
63     (defgeneric layout-graph-edges (graph-output-record stream arc-drawer arc-drawing-options))
64     ;;; NOTE: Which calls which? --GB 2002-08-13
65    
66     (defgeneric graph-node-parents (graph-node-record))
67     (defgeneric (setf graph-node-parents) (new-value graph-node-record))
68     (defgeneric graph-node-children (graph-node-record))
69     (defgeneric (setf graph-node-children) (new-value graph-node-record))
70     (defgeneric graph-node-object (graph-node-record))
71    
72     ;;;; Machinery for graph types
73    
74     (defconstant +built-in-graph-types+
75     '(:tree :directed-graph :digraph :directed-acyclic-graph :dag)
76     "List of graph types builtin by CLIM.")
77    
78     (defvar *graph-types-hash*
79     (make-hash-table :test #'eq)
80     "A hash table which maps from symbols that name graph-types to class names; Filled by CLIM:DEFINE-GRAPH-TYPE")
81    
82     (defun register-graph-type (graph-type class)
83     "Registers a new graph-type."
84     (setf (gethash graph-type *graph-types-hash*) class))
85    
86     (defun find-graph-type (graph-type)
87     "Find the a graph type; when it does not exist barks at the user."
88     (or (gethash graph-type *graph-types-hash*)
89     (progn
90     (cerror "Specify another graph type to use instead."
91     "There is no such graph type defined: ~S.~%The defined ones are: ~{~S~^, ~@_~}."
92     graph-type
93     (loop for key being each hash-key of *graph-types-hash*
94     collect key))
95     ;; accept anyone?
96     (princ "Graph Type? ")
97     (find-graph-type (read)))))
98    
99     (defmacro define-graph-type (graph-type class)
100     (check-type graph-type symbol)
101     (check-type class symbol)
102     (unless (eq *package* (find-package :climi))
103     (when (member graph-type +built-in-graph-types+)
104     (cerror "Do it anyway" "You are about to redefine the builtin graph type ~S."
105     graph-type)))
106     ;; Note: I would really like this to obey to package locks and stuff.
107     `(progn
108     (register-graph-type ',graph-type ',class)
109     ',graph-type))
110    
111     (define-graph-type :tree tree-graph-output-record)
112     (define-graph-type :directed-acyclic-graph dag-graph-output-record)
113     (define-graph-type :dag dag-graph-output-record)
114     (define-graph-type :directed-graph digraph-graph-output-record)
115     (define-graph-type :digraph digraph-graph-output-record)
116    
117     ;;;; Entry
118 rgoldman 1.21 (defun format-graph-from-root (root-object &rest other-args)
119     (apply #'format-graph-from-roots (list root-object) other-args))
120 gilbert 1.1
121     (defun format-graph-from-roots (root-objects object-printer inferior-producer
122 rgoldman 1.21 &rest rest-args
123 gilbert 1.1 &key stream orientation cutoff-depth
124     merge-duplicates duplicate-key duplicate-test
125     generation-separation
126     within-generation-separation
127 ahefner 1.12 center-nodes
128     (arc-drawer #'clim-internals::standard-arc-drawer)
129     arc-drawing-options
130 gilbert 1.3 graph-type (move-cursor t)
131     &allow-other-keys)
132 gilbert 1.4 (declare (ignore orientation generation-separation within-generation-separation center-nodes))
133 rgoldman 1.21 ;; don't destructively modify the &rest arg
134     (let ((graph-options (copy-list rest-args)))
135     ;; Munge some arguments
136     (check-type cutoff-depth (or null integer))
137     (check-type root-objects sequence)
138     (setf stream (or stream *standard-output*)
139     graph-type (or graph-type (if merge-duplicates :digraph :tree))
140     duplicate-key (or duplicate-key #'identity)
141     duplicate-test (or duplicate-test #'eql) )
142    
143     ;; I'm not sure what to do here. Saying you want a tree, but want
144     ;; duplicates merged seems wrong. OTOH, if you go out of your way
145     ;; to do it, at your own risk, is it our place to say "no"?
146 rgoldman 1.16 ;; [2005/08/11:rpg]
147     ;;; (when (and (eq graph-type :tree) merge-duplicates)
148     ;;; (cerror "Substitute NIL for merge-duplicates"
149     ;;; "Merge duplicates specified to be true when using :tree layout.")
150     ;;; (setf merge-duplicates nil))
151 gilbert 1.3
152 rgoldman 1.21 ;; clean the options
153     (remf graph-options :stream)
154     (remf graph-options :duplicate-key)
155     (remf graph-options :duplicate-test)
156     (remf graph-options :arc-drawer)
157     (remf graph-options :arc-drawing-options)
158     (remf graph-options :graph-type)
159     (remf graph-options :move-cursor)
160 gilbert 1.3
161 rgoldman 1.21 (multiple-value-bind (cursor-old-x cursor-old-y)
162     (stream-cursor-position stream)
163     (let ((graph-output-record
164     (labels ((cont (stream graph-output-record)
165     (with-output-recording-options (stream :draw nil :record t)
166     (generate-graph-nodes graph-output-record stream root-objects
167     object-printer inferior-producer
168     :duplicate-key duplicate-key
169     :duplicate-test duplicate-test)
170     (layout-graph-nodes graph-output-record stream arc-drawer arc-drawing-options)
171     (layout-graph-edges graph-output-record stream arc-drawer arc-drawing-options)) ))
172     (apply #'invoke-with-new-output-record stream
173     #'cont
174     (find-graph-type graph-type)
175     nil
176     ;; moved to local variable... [2005/07/25:rpg]
177     ;; :hash-table (make-hash-table :test duplicate-test)
178     graph-options
179     ))))
180     (setf (output-record-position graph-output-record)
181     (values cursor-old-x cursor-old-y))
182     (when (and (stream-drawing-p stream)
183     (output-record-ancestor-p (stream-output-history stream)
184     graph-output-record))
185     (with-output-recording-options (stream :draw t :record nil)
186     (replay graph-output-record stream)))
187     (when move-cursor
188     (setf (stream-cursor-position stream)
189     (values (bounding-rectangle-max-x graph-output-record)
190     (bounding-rectangle-max-y graph-output-record))))
191     graph-output-record))))
192 mikemac 1.6
193     (defun format-graph-from-root (root &rest rest)
194     (apply #'format-graph-from-roots (list root) rest))
195 gilbert 1.1
196     ;;;; Graph Output Records
197    
198     (defclass standard-graph-output-record (graph-output-record
199     standard-sequence-output-record)
200 rgoldman 1.16 ((orientation
201     :initarg :orientation
202     :initform :horizontal)
203     (center-nodes
204     :initarg :center-nodes
205     :initform nil)
206     (cutoff-depth
207     :initarg :cutoff-depth
208     :initform nil)
209     (merge-duplicates
210     :initarg :merge-duplicates
211     :initform nil)
212     (generation-separation
213     :initarg :generation-separation
214     :initform '(4 :character))
215     (within-generation-separation
216     :initarg :within-generation-separation
217     :initform '(1/2 :line))
218     ;; removed HASH-TABLE slot and stuffed it into
219     ;; GENERATE-GRAPH-NODES method definition [2005/07/25:rpg]
220     (root-nodes
221     :accessor graph-root-nodes)
222     ))
223 gilbert 1.1
224 adejneka 1.2 (defclass tree-graph-output-record (standard-graph-output-record)
225 rgoldman 1.16 ())
226    
227     ;;;(defmethod initialize-instance :after ((obj tree-graph-output-record) &key merge-duplicates)
228     ;;; (when merge-duplicates
229     ;;; (warn "Cannot use a TREE layout for graphs while merging duplicates.")))
230 gilbert 1.3
231 adejneka 1.2 (defclass dag-graph-output-record (standard-graph-output-record)
232 rgoldman 1.16 (
233     ))
234 gilbert 1.3
235 adejneka 1.2 (defclass digraph-graph-output-record (standard-graph-output-record)
236     ())
237 gilbert 1.1
238     ;;;; Nodes
239    
240     (defclass standard-graph-node-output-record (graph-node-output-record
241     standard-sequence-output-record)
242     ((graph-parents
243     :initarg :graph-parents
244     :initform nil
245     :accessor graph-node-parents)
246     (graph-children
247     :initarg :graph-children
248     :initform nil
249     :accessor graph-node-children)
250 crhodes 1.18 (edges-from :initform (make-hash-table))
251     (edges-to :initform (make-hash-table))
252 gilbert 1.1 (object
253     :initarg :object
254 gilbert 1.4 :reader graph-node-object)
255 rgoldman 1.21 ;; internal slots for the graph layout algorithm
256 gilbert 1.4 (minor-size
257     :initform nil
258     :accessor graph-node-minor-size
259     :documentation "Space requirement for this node and its children. Also used as a mark.") ))
260 gilbert 1.1
261     ;;;;
262    
263 rgoldman 1.16 ;;; Modified to make this obey the spec better by using a hash-table
264     ;;; for detecting previous nodes only when the duplicate-test argument
265     ;;; permits it. [2005/08/10:rpg]
266 gilbert 1.1 (defmethod generate-graph-nodes ((graph-output-record standard-graph-output-record)
267     stream root-objects
268     object-printer inferior-producer
269     &key duplicate-key duplicate-test)
270 rgoldman 1.16 (with-slots (cutoff-depth merge-duplicates) graph-output-record
271     (let* ((hash-table (when (and merge-duplicates (member duplicate-test (list #'eq #'eql #'equal #'equalp)))
272     (make-hash-table :test duplicate-test)))
273     node-list
274     (hashed hash-table))
275     (labels
276     ((previous-node (obj)
277     ;; is there a previous node for obj? if so, return it.
278     (when merge-duplicates
279     (if hashed
280     (locally (declare (type hash-table hash-table))
281     (gethash obj hash-table))
282     (cdr (assoc obj node-list :test duplicate-test)))))
283     ((setf previous-node) (val obj)
284     (if hashed
285     (locally (declare (type hash-table hash-table))
286     (setf (gethash obj hash-table) val))
287     (setf node-list (push (cons obj val) node-list))))
288     (traverse-objects (node objects depth)
289     (unless (and cutoff-depth (>= depth cutoff-depth))
290     (remove nil
291     (map 'list
292     (lambda (child)
293     (let* ((key (funcall duplicate-key child))
294     (child-node (previous-node key)))
295     (cond (child-node
296     (when node
297     (push node (graph-node-parents child-node)))
298     child-node)
299     (t
300     (let ((child-node
301     (with-output-to-output-record
302     (stream 'standard-graph-node-output-record new-node
303     :object child)
304     (funcall object-printer child stream))))
305     (when merge-duplicates
306     (setf (previous-node key) child-node)
307     ;; (setf (gethash key hash-table) child-node)
308     )
309     (when node
310     (push node (graph-node-parents child-node)))
311     (setf (graph-node-children child-node)
312     (traverse-objects child-node
313     (funcall inferior-producer child)
314     (+ depth 1)))
315     child-node)))))
316     objects)))))
317     ;;
318     (setf (graph-root-nodes graph-output-record)
319     (traverse-objects nil root-objects 0))
320     (values)))))
321 gilbert 1.1
322 gilbert 1.4 (defun traverse-graph-nodes (graph continuation)
323 hefner1 1.11 ;; continuation: node x children x cont -> some value
324 gilbert 1.4 (let ((hash (make-hash-table :test #'eq)))
325     (labels ((walk (node)
326     (unless (gethash node hash)
327     (setf (gethash node hash) t)
328     (funcall continuation node (graph-node-children node) #'walk))))
329     (funcall continuation graph (graph-root-nodes graph) #'walk))))
330    
331 gilbert 1.1 (defmethod layout-graph-nodes ((graph-output-record tree-graph-output-record)
332     stream arc-drawer arc-drawing-options)
333 gilbert 1.3 ;; work in progress! --GB 2002-08-14
334     (declare (ignore arc-drawer arc-drawing-options))
335 gilbert 1.1 (with-slots (orientation center-nodes generation-separation within-generation-separation root-nodes) graph-output-record
336     (check-type orientation (member :horizontal :vertical)) ;xxx move to init.-inst.
337     ;; here major dimension is the dimension in which we grow the
338     ;; tree.
339 gilbert 1.3 (let ((within-generation-separation (parse-space stream within-generation-separation
340     (case orientation
341     (:horizontal :vertical)
342     (:vertical :horizontal))))
343     (generation-separation (parse-space stream generation-separation orientation)))
344 rgoldman 1.16 ;; generation sizes is an adjustable array that tracks the major
345     ;; dimension of each of the generations [2005/07/18:rpg]
346 gilbert 1.4 (let ((generation-sizes (make-array 10 :adjustable t :initial-element 0)))
347     (labels ((node-major-dimension (node)
348     (if (eq orientation :vertical)
349     (bounding-rectangle-height node)
350     (bounding-rectangle-width node)))
351     (node-minor-dimension (node)
352     (if (eq orientation :vertical)
353     (bounding-rectangle-width node)
354     (bounding-rectangle-height node)))
355 rgoldman 1.16 ;; WALK returns a node minor dimension for the node,
356     ;; AFAICT, allowing space for that node's children
357     ;; along the minor dimension. [2005/07/18:rpg]
358 gilbert 1.4 (walk (node depth)
359     (unless (graph-node-minor-size node)
360     (when (>= depth (length generation-sizes))
361 ahefner 1.15 (setf generation-sizes (adjust-array generation-sizes (ceiling (* depth 1.2)) :initial-element 0)))
362 gilbert 1.4 (setf (aref generation-sizes depth)
363     (max (aref generation-sizes depth) (node-major-dimension node)))
364     (setf (graph-node-minor-size node) 0)
365     (max (node-minor-dimension node)
366     (setf (graph-node-minor-size node)
367     (let ((sum 0) (n 0))
368     (map nil (lambda (child)
369     (let ((x (walk child (+ depth 1))))
370     (when x
371     (incf sum x)
372     (incf n))))
373     (graph-node-children node))
374     (+ sum
375     (* (max 0 (- n 1)) within-generation-separation))))))))
376     (map nil #'(lambda (x) (walk x 0)) root-nodes)
377     (let ((hash (make-hash-table :test #'eq)))
378     (labels ((foo (node majors u0 v0)
379     (cond ((gethash node hash)
380     v0)
381 gilbert 1.3 (t
382 gilbert 1.4 (setf (gethash node hash) t)
383     (let ((d (- (node-minor-dimension node)
384     (graph-node-minor-size node))))
385     (let ((v (+ v0 (/ (min 0 d) -2))))
386     (setf (output-record-position node)
387     (if (eq orientation :vertical)
388 ahefner 1.13 (transform-position (medium-transformation stream) v u0)
389     (transform-position (medium-transformation stream) u0 v)))
390 gilbert 1.4 (add-output-record node graph-output-record))
391     ;;
392     (let ((u (+ u0 (car majors)))
393     (v (+ v0 (max 0 (/ d 2))))
394     (firstp t))
395     (map nil (lambda (q)
396     (unless (gethash q hash)
397     (if firstp
398     (setf firstp nil)
399     (incf v within-generation-separation))
400     (setf v (foo q (cdr majors)
401     u v))))
402     (graph-node-children node)))
403     ;;
404     (+ v0 (max (node-minor-dimension node)
405     (graph-node-minor-size node))))))))
406     ;;
407     (let ((majors (mapcar (lambda (x) (+ x generation-separation))
408     (coerce generation-sizes 'list))))
409     (let ((u (+ 0 (car majors)))
410     (v 0))
411     (maplist (lambda (rest)
412     (setf v (foo (car rest) majors u v))
413     (unless (null rest)
414     (incf v within-generation-separation)))
415     (graph-root-nodes graph-output-record)))))))))))
416 rgoldman 1.16
417 crhodes 1.18 ;;;; Edges
418    
419     (defclass standard-edge-output-record (standard-sequence-output-record)
420     ((stream)
421     (arc-drawer)
422     (arc-drawing-options)
423     (from-node :initarg :from-node)
424     (to-node :initarg :to-node)))
425    
426 rgoldman 1.16
427     (defmethod layout-graph-nodes ((graph-output-record dag-graph-output-record)
428     stream arc-drawer arc-drawing-options)
429     "This is a first shot at a DAG layout. First does a TOPO sort that associates
430     each node with a depth, then lays out by depth. Tries to reuse a maximum of the
431     tree graph layout code.
432     PRECONDITION: This code assumes that we have generated only nodes up to the
433     cutoff-depth. GENERATE-GRAPH-NODES seems to obey this precondition."
434     (declare (ignore arc-drawer arc-drawing-options))
435     (with-slots (orientation center-nodes generation-separation within-generation-separation root-nodes
436     merge-duplicates) graph-output-record
437     ;; this code is snarly enough, handling merge-duplicates. If
438     ;; you're not merging duplicates, you're out of luck, at least for
439     ;; now... [2005/07/18:rpg]
440     (unless merge-duplicates
441     (cerror "Set to T and continue?" "DAG graph-layout type only supports merge-duplicates to be T")
442     (setf merge-duplicates t))
443    
444     (check-type orientation (member :horizontal :vertical)) ;xxx move to init.-inst.
445    
446     ;; here major dimension is the dimension in which we grow the
447     ;; tree.
448     (let ((within-generation-separation (parse-space stream within-generation-separation
449     (case orientation
450     (:horizontal :vertical)
451     (:vertical :horizontal))))
452     (generation-separation (parse-space stream generation-separation orientation)))
453     ;; generation sizes is an adjustable array that tracks the major
454     ;; dimension of each of the generations [2005/07/18:rpg]
455     (let ((generation-sizes (make-array 10 :adjustable t :initial-element 0))
456     (visited (make-hash-table :test #'eq))
457     (parent-hash (make-hash-table :test #'eq)))
458     (labels ((node-major-dimension (node)
459     (if (eq orientation :vertical)
460     (bounding-rectangle-height node)
461     (bounding-rectangle-width node)))
462     (node-minor-dimension (node)
463     (if (eq orientation :vertical)
464     (bounding-rectangle-width node)
465     (bounding-rectangle-height node)))
466     ;; WALK returns a node minor dimension for the node,
467     ;; AFAICT, allowing space for that node's children
468     ;; along the minor dimension. [2005/07/18:rpg]
469     (walk (node depth &optional parent)
470     (unless (gethash node visited)
471     (setf (gethash node visited) depth)
472     (when parent
473     (setf (gethash node parent-hash) parent))
474     (unless (graph-node-minor-size node)
475     (when (>= depth (length generation-sizes))
476     (setf generation-sizes (adjust-array generation-sizes (ceiling (* depth 1.2))
477     :initial-element 0)))
478     (setf (aref generation-sizes depth)
479     (max (aref generation-sizes depth) (node-major-dimension node)))
480     (setf (graph-node-minor-size node) 0)
481     (max (node-minor-dimension node)
482     (setf (graph-node-minor-size node)
483     (let ((sum 0) (n 0))
484     (map nil (lambda (child)
485     (let ((x (walk child (+ depth 1) node)))
486     (when x
487     (incf sum x)
488     (incf n))))
489     (graph-node-children node))
490     (+ sum
491     (* (max 0 (- n 1)) within-generation-separation)))))))))
492     (map nil #'(lambda (x) (walk x 0)) root-nodes)
493     (let ((hash (make-hash-table :test #'eq)))
494     (labels ((foo (node majors u0 v0)
495     (cond ((gethash node hash)
496     v0)
497     (t
498     (setf (gethash node hash) t)
499     (let ((d (- (node-minor-dimension node)
500     (graph-node-minor-size node))))
501     (let ((v (+ v0 (/ (min 0 d) -2))))
502     (setf (output-record-position node)
503     (if (eq orientation :vertical)
504     (transform-position (medium-transformation stream) v u0)
505     (transform-position (medium-transformation stream) u0 v)))
506     (add-output-record node graph-output-record))
507     ;;
508     (let ((u (+ u0 (car majors)))
509     (v (+ v0 (max 0 (/ d 2))))
510     (firstp t))
511     (map nil (lambda (q)
512     (unless (gethash q hash)
513     (if firstp
514     (setf firstp nil)
515     (incf v within-generation-separation))
516     (setf v (foo q (cdr majors)
517     u v))))
518     ;; when computing the sizes, to
519     ;; make the tree-style layout
520     ;; work, we have to have each
521     ;; node have a unique
522     ;; parent. [2005/07/18:rpg]
523     (remove-if-not #'(lambda (x) (eq (gethash x parent-hash) node))
524     (graph-node-children node))))
525     ;;
526     (+ v0 (max (node-minor-dimension node)
527     (graph-node-minor-size node))))))))
528     ;;
529     (let ((majors (mapcar (lambda (x) (+ x generation-separation))
530     (coerce generation-sizes 'list))))
531     (let ((u (+ 0 (car majors)))
532     (v 0))
533     (maplist (lambda (rest)
534     (setf v (foo (car rest) majors u v))
535     (unless (null rest)
536     (incf v within-generation-separation)))
537     (graph-root-nodes graph-output-record)))))))))))
538    
539    
540 gilbert 1.1
541 mikemac 1.5 #+ignore
542 gilbert 1.4 (defmethod layout-graph-edges ((graph-output-record standard-graph-output-record)
543 gilbert 1.1 stream arc-drawer arc-drawing-options)
544     (with-slots (root-nodes orientation) graph-output-record
545     (let ((hash (make-hash-table)))
546     (labels ((walk (node)
547 crhodes 1.18 (unless (gethash node hash)
548 gilbert 1.1 (setf (gethash node hash) t)
549     (dolist (k (graph-node-children node))
550     (with-bounding-rectangle* (x1 y1 x2 y2) node
551     (with-bounding-rectangle* (u1 v1 u2 v2) k
552 gilbert 1.3 (ecase orientation
553     ((:horizontal)
554     (multiple-value-bind (from to) (if (< x1 u1)
555     (values x2 u1)
556     (values x1 u2))
557     (apply arc-drawer stream node k
558     from (/ (+ y1 y2) 2)
559     to (/ (+ v1 v2) 2)
560     arc-drawing-options)))
561     ((:vertical)
562     (multiple-value-bind (from to) (if (< y1 v1)
563     (values y2 v1)
564     (values y1 v2))
565     (apply arc-drawer stream node k
566     (/ (+ x1 x2) 2) from
567     (/ (+ u1 u2) 2) to
568     arc-drawing-options)) ))))
569 gilbert 1.1 (walk k)))))
570     (map nil #'walk root-nodes)))))
571    
572 crhodes 1.18 (defun layout-edges (graph node stream arc-drawer arc-drawing-options)
573     (dolist (k (graph-node-children node))
574     (layout-edge graph node k stream arc-drawer arc-drawing-options)))
575    
576     (defun ensure-edge-record (graph major-node minor-node)
577     (let ((edges-from (slot-value major-node 'edges-from))
578     (edges-to (slot-value minor-node 'edges-to)))
579     (assert (eq (gethash minor-node edges-from)
580     (gethash major-node edges-to)))
581     (or (gethash minor-node edges-from)
582     (let ((record (make-instance 'standard-edge-output-record
583     :from-node major-node :to-node minor-node)))
584     (setf (gethash minor-node edges-from) record
585     (gethash major-node edges-to) record)
586     (add-output-record record graph)
587     record))))
588    
589     (defun layout-edge-1 (graph major-node minor-node)
590     (let ((edge-record (ensure-edge-record graph major-node minor-node)))
591     (with-slots (stream arc-drawer arc-drawing-options) edge-record
592     (with-bounding-rectangle* (x1 y1 x2 y2) major-node
593     (with-bounding-rectangle* (u1 v1 u2 v2) minor-node
594     (clear-output-record edge-record) ;;; FIXME: repaint?
595     (letf (((stream-current-output-record stream) edge-record))
596     (ecase (slot-value graph 'orientation)
597     ((:horizontal)
598     (multiple-value-bind (from to) (if (< x1 u1)
599     (values x2 u1)
600     (values x1 u2))
601     (apply arc-drawer stream major-node minor-node
602     from (/ (+ y1 y2) 2)
603     to (/ (+ v1 v2) 2)
604     arc-drawing-options)))
605     ((:vertical)
606     (multiple-value-bind (from to) (if (< y1 v1)
607     (values y2 v1)
608     (values y1 v2))
609     (apply arc-drawer stream major-node minor-node
610     (/ (+ x1 x2) 2) from
611     (/ (+ u1 u2) 2) to
612     arc-drawing-options))))))))))
613    
614     (defun layout-edge (graph major-node minor-node stream arc-drawer arc-drawing-options)
615     (let ((edge-record (ensure-edge-record graph major-node minor-node)))
616     (setf (slot-value edge-record 'stream) stream
617     (slot-value edge-record 'arc-drawer) arc-drawer
618     (slot-value edge-record 'arc-drawing-options) arc-drawing-options)
619     (layout-edge-1 graph major-node minor-node)))
620    
621 gilbert 1.4 (defmethod layout-graph-edges ((graph standard-graph-output-record)
622     stream arc-drawer arc-drawing-options)
623     (with-slots (orientation) graph
624 ahefner 1.13 ;; We tranformed the position of the nodes when we inserted them into
625     ;; output history, so the bounding rectangles queried below will be
626     ;; transformed. Therefore, disable the transformation now, otherwise
627     ;; the transformation is effectively applied twice to the edges.
628     (with-identity-transformation (stream)
629 gilbert 1.4 (traverse-graph-nodes graph
630     (lambda (node children continuation)
631     (unless (eq node graph)
632 crhodes 1.18 (layout-edges graph node stream arc-drawer arc-drawing-options))
633 ahefner 1.13 (map nil continuation children))))))
634 gilbert 1.4
635     (defmethod layout-graph-edges :around ((graph-output-record tree-graph-output-record)
636     stream arc-drawer arc-drawing-options)
637     (setf arc-drawer (or arc-drawer #'standard-arc-drawer))
638     (call-next-method graph-output-record stream arc-drawer arc-drawing-options))
639    
640     (defmethod layout-graph-edges :around ((graph-output-record digraph-graph-output-record)
641     stream arc-drawer arc-drawing-options)
642     (setf arc-drawer (or arc-drawer #'arrow-arc-drawer))
643     (call-next-method graph-output-record stream arc-drawer arc-drawing-options))
644    
645     (defmethod layout-graph-edges :around ((graph-output-record dag-graph-output-record)
646     stream arc-drawer arc-drawing-options)
647     (setf arc-drawer (or arc-drawer #'standard-arc-drawer))
648     (call-next-method graph-output-record stream arc-drawer arc-drawing-options))
649    
650 gilbert 1.3 (defun standard-arc-drawer (stream from-node to-node x1 y1 x2 y2
651     &rest drawing-options
652     &key &allow-other-keys)
653     (declare (ignore from-node to-node))
654     (apply #'draw-line* stream x1 y1 x2 y2 drawing-options))
655    
656     (defun arrow-arc-drawer (stream from-node to-node x1 y1 x2 y2
657     &rest drawing-options
658     &key &allow-other-keys)
659     (declare (ignore from-node to-node))
660     (apply #'draw-arrow* stream x1 y1 x2 y2 drawing-options))
661    
662 gilbert 1.1 #||
663 gilbert 1.3
664 gilbert 1.1 ;; Experimental version for rectangular graphs
665 gilbert 1.3
666 gilbert 1.1 (defmethod layout-graph-edges ((graph-output-record tree-graph-output-record)
667     stream arc-drawer arc-drawing-options)
668     (with-slots (root-nodes orientation) graph-output-record
669     (let ((hash (make-hash-table)))
670     (labels ((walk (node &aux (vlast nil) uu)
671     (unless (gethash node hash)
672     (setf (gethash node hash) t)
673     (with-bounding-rectangle* (x1 y1 x2 y2) node
674     (dolist (k (graph-node-children node))
675     (with-bounding-rectangle* (u1 v1 u2 v2) k
676     (case orientation
677     (:horizontal
678     (draw-line* stream (/ (+ x2 u1) 2) (/ (+ v1 v2) 2)
679     (- u1 2) (/ (+ v1 v2) 2))
680     (setf uu u1)
681     (setf vlast (max (or vlast 0) (/ (+ v1 v2) 2))))
682     (:vertical
683     (draw-line* stream (/ (+ x1 x2) 2) y2
684     (/ (+ u1 u2) 2) v1))))
685     (walk k))
686     (when vlast
687     (draw-line* stream (+ x2 2) (/ (+ y1 y2) 2) (/ (+ x2 uu) 2) (/ (+ y1 y2) 2))
688     (draw-line* stream (/ (+ x2 uu) 2) (/ (+ y1 y2) 2)
689     (/ (+ x2 uu) 2) vlast))))))
690     (map nil #'walk root-nodes)))))
691     ||#
692    
693     #||
694 gilbert 1.3
695     ;;; Testing --GB 2002-08-14
696    
697 gilbert 1.1 (define-application-frame graph-test ()
698     ()
699     (:panes
700     (interactor :interactor :width 800 :height 400 :max-width +fill+ :max-height +fill+))
701     (:layouts
702     (default
703     interactor)))
704    
705     (define-graph-test-command foo ()
706     (with-text-style (*query-io* (make-text-style :sans-serif nil 12))
707     (let ((*print-case* :downcase))
708     (format-graph-from-roots
709     (list `(define-graph-test-command test ()
710     (let ((stream *query-io*)
711     (orientation :horizontal))
712     (fresh-line stream)
713     (macrolet ((make-node (&key name children)
714     `(list* ,name ,children)))
715     (flet ((node-name (node)
716     (car node))
717     (node-children (node)
718     (cdr node)))
719     (let* ((2a (make-node :name "2A"))
720     (2b (make-node :name "2B"))
721     (2c (make-node :name "2C"))
722     (1a (make-node :name "1A" :children (list 2a 2b)))
723     (1b (make-node :name "1B" :children (list 2b 2c)))
724     (root (make-node :name "0" :children (list 1a 1b))))
725     (format-graph-from-roots
726     (list root)
727     #'(lambda (node s)
728     (write-string (node-name node) s))
729     #'node-children
730     :orientation orientation
731     :stream stream)))))))
732     #'(lambda (x s) (with-output-as-presentation (s x 'command)
733     (let ((*print-level* 1))
734     (princ (if (consp x) (car x) x) s))))
735     #'(lambda (x) (and (consp x) (cdr x)))
736     :stream *query-io*
737     :orientation :horizontal))))
738    
739     (defun external-symbol-p (sym)
740     ;; *cough* *cough*
741     (< (count #\: (let ((*package* (find-package :keyword)))
742     (prin1-to-string sym)))
743     2))
744    
745     (define-graph-test-command bar ()
746 gilbert 1.3 (with-text-style (*query-io* (make-text-style :sans-serif nil 10))
747 gilbert 1.1 (let ((*print-case* :downcase))
748     (format-graph-from-roots
749     (list (clim-mop:find-class 'climi::basic-output-record))
750     #'(lambda (x s)
751 gilbert 1.4 (progn ;;surrounding-output-with-border (s :shape :oval)
752 gilbert 1.3 (with-text-style (s (make-text-style nil
753     (if (external-symbol-p (class-name x))
754     :bold
755     nil)
756     nil))
757     (prin1 (class-name x) s))))
758 gilbert 1.1 #'(lambda (x)
759     (clim-mop:class-direct-subclasses x))
760 gilbert 1.4 :generation-separation '(4 :line)
761     :within-generation-separation '(2 :character)
762 gilbert 1.1 :stream *query-io*
763 gilbert 1.4 :orientation :vertical))))
764    
765     (define-graph-test-command bar ()
766     (with-text-style (*query-io* (make-text-style :sans-serif nil 10))
767     (format-graph-from-roots
768 mikemac 1.8 (list '(:foo
769     (:bar)
770     (:baaaaaaaaaaaaaaz
771     (:a)
772     (:b))
773     (:q
774     (:x) (:y)))
775 gilbert 1.4 )
776     #'(lambda (x s)
777     (prin1 (first x) s))
778     #'(lambda (x)
779     (cdr x))
780     :generation-separation '(4 :line)
781     :within-generation-separation '(2 :character)
782     :stream *query-io*
783     :orientation :vertical)))
784 gilbert 1.1
785     (define-graph-test-command baz ()
786 gilbert 1.4 (with-text-style (*query-io* (make-text-style :sans-serif nil 10))
787 gilbert 1.1 (let ((*print-case* :downcase))
788     (format-graph-from-roots
789 gilbert 1.4 (list (clim-mop:find-class 'standard-graph-output-record)
790     ;;(clim-mop:find-class 'climi::basic-output-record)
791     ;;(clim-mop:find-class 'climi::graph-output-record)
792    
793 gilbert 1.1 )
794     #'(lambda (x s)
795     (with-text-style (s (make-text-style nil
796     (if (external-symbol-p (class-name x))
797     :bold
798     nil)
799     nil))
800     (prin1 (class-name x) s)))
801     #'(lambda (x)
802 gilbert 1.4 (reverse(clim-mop:class-direct-superclasses x)))
803 gilbert 1.1 ;; :duplicate-key #'(lambda (x) 't)
804     :merge-duplicates t
805     :graph-type :tree
806 gilbert 1.4 :arc-drawer #'arrow-arc-drawer
807 gilbert 1.1 :stream *query-io*
808 gilbert 1.4 :orientation :vertical))))
809 gilbert 1.1
810     (define-graph-test-command test ()
811     (let ((stream *query-io*)
812     (orientation :vertical))
813     (fresh-line stream)
814     (macrolet ((make-node (&key name children)
815     `(list* ,name ,children)))
816     (flet ((node-name (node)
817     (car node))
818     (node-children (node)
819     (cdr node)))
820     (let* ((2a (make-node :name "2A"))
821     (2b (make-node :name "2B"))
822     (2c (make-node :name "2C"))
823     (1a (make-node :name "1A" :children (list 2a 2b)))
824     (1b (make-node :name "1B" :children (list 2b 2c)))
825     (root (make-node :name "0" :children (list 1a 1b))))
826     (format-graph-from-roots
827     (list root)
828     #'(lambda (node s)
829     (write-string (node-name node) s))
830     #'node-children
831 gilbert 1.3 :arc-drawer #'arrow-arc-drawer
832     :arc-drawing-options (list :ink +red+ :line-thickness 1)
833 gilbert 1.1 :orientation orientation
834     :stream stream))))))
835 gilbert 1.3
836     (defun make-circ-list (list)
837     (nconc list list))
838    
839     (define-graph-test-command test2 ()
840     (let ((stream *query-io*)
841     (orientation :vertical))
842     (fresh-line stream)
843     (format-graph-from-roots
844 gilbert 1.4 (list '(defun dcons (x) (cons x x))
845     (make-circ-list (list 1 '(2 . 4) 3)))
846 gilbert 1.3 #'(lambda (node s)
847     (if (consp node)
848     (progn
849     (draw-circle* s 5 5 5 :filled nil))
850     (princ node s)))
851     #'(lambda (x) (if (consp x) (list (car x) (cdr x))))
852     :cutoff-depth nil
853     :graph-type :tree
854     :merge-duplicates t
855     :arc-drawer #'arrow-arc-drawer
856     :arc-drawing-options (list :ink +red+ :line-thickness 1)
857     :orientation orientation
858     :stream stream)))
859 gilbert 1.1 ||#

  ViewVC Help
Powered by ViewVC 1.1.5