2 ;;; GRAPHS - graph theory package for Maxima
4 ;;; Copyright (C) 2007-2011 Andrej Vodopivec <andrej.vodopivec@gmail.com>
6 ;;; This program is free software; you can redistribute it and/or modify
7 ;;; it under the terms of the GNU General Public License as published by
8 ;;; the Free Software Foundation; either version 2 of the License, or
9 ;;; (at your option) any later version.
11 ;;; This program is distributed in the hope that it will be useful,
12 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;;; GNU General Public License for more details.
16 ;;; You should have received a copy of the GNU General Public License
17 ;;; along with this program; if not, write to the Free Software
18 ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
24 ;;; force-based graph embedding algorithm
27 ;;; T.M.J. Fruchterman, E.M. Reingold, Graph drawing by force-directed
28 ;;; placement, Software practice and experience 21 (1991), 11, 1129--1164.
30 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
34 (defvar *vertex-position
*)
35 (defvar *optimal-distance
*)
37 (defvar *epsilon-distance
* 0.5)
38 (defvar *frame-width
* 10.0)
40 (defvar *fixed-vertices
* nil
)
42 (defun attractive-force (d)
43 (/ (* d d
) *optimal-distance
*))
45 (defun repulsive-force (d)
46 (let ((d (max d
*epsilon-distance
*)))
47 (/ (* *optimal-distance
* *optimal-distance
*) d
100)))
49 (defun distance (p1 p2
)
50 (let ((d (mapcar #'- p1 p2
)))
51 (sqrt (apply #'+ (mapcar #'* d d
)))))
53 (defun random-positions (v-list dimension
)
54 (when *fixed-vertices
*
55 (let ((n (length *fixed-vertices
*)))
56 (dotimes (i (length *fixed-vertices
*))
57 (let ((v (nth i
*fixed-vertices
*))
58 (x (* *frame-width
* ($sin
(/ (* 2 i pi
) n
))))
59 (y (* *frame-width
* ($cos
(/ (* 2 i pi
) n
)))))
60 (setf (gethash v
*vertex-position
*)
63 (unless (member v
*fixed-vertices
*)
64 (let* ((x (- *frame-width
* (random (* 2 *frame-width
*))))
65 (y (- *frame-width
* (random (* 2 *frame-width
*))))
66 (z (- *frame-width
* (random (* 2 *frame-width
*)))))
67 (setf (gethash v
*vertex-position
*)
72 (defmfun $spring_embedding
(g depth fixed-vertices dimension continue
)
73 (let ((*vertex-position
* (make-hash-table))
74 (vertex-displacement (make-hash-table))
75 (*fixed-vertices
* (cdr fixed-vertices
))
76 (*optimal-distance
* (/ (* 2 *frame-width
*)
77 (sqrt ($graph_order g
)))))
79 ;; Start with current positions if we already have some.
81 (> (length ($get_positions g
)) 1)
82 (= ($length
($first
($get_positions g
))) dimension
))
83 (dolist (v (cdr ($get_positions g
)))
84 (setf (gethash (cadr v
) *vertex-position
*) (cdaddr v
)))
85 (random-positions (vertices g
) dimension
))
87 (let* ((step (/ *frame-width
* 5))
88 (d-step (/ step
(1+ depth
))))
90 (setq step
(- step d-step
))
92 (dolist (v (vertices g
))
93 (setf (gethash v vertex-displacement
) (if (= dimension
2) (list 0 0) (list 0 0 0))))
95 ;; calculate repulsive forces
96 (when (null *fixed-vertices
*)
97 (let ((v-vrt (vertices g
)))
99 (let* ((v (car v-vrt
))
101 (v-pos (gethash v
*vertex-position
*)))
103 (let* ((u (car u-vrt
))
104 (u-pos (gethash u
*vertex-position
*))
105 (delta (mapcar #'- v-pos u-pos
))
106 (delta-abs (distance v-pos u-pos
))
107 (force (repulsive-force delta-abs
))
109 #'(lambda (u) (* (/ u
(max delta-abs
*epsilon-distance
*)) force
))
111 (v-disp (gethash v vertex-displacement
))
112 (u-disp (gethash u vertex-displacement
)))
113 (setf (gethash v vertex-displacement
)
114 (mapcar #'+ v-disp vu-disp
)
115 (gethash u vertex-displacement
)
116 (mapcar #'- u-disp vu-disp
))
117 (setq u-vrt
(cdr u-vrt
)))))
118 (setq v-vrt
(cdr v-vrt
)))))
120 ;; calculate attractive forces
121 (dolist (e (edges g
))
124 (v-pos (gethash v
*vertex-position
*))
125 (u-pos (gethash u
*vertex-position
*))
126 (delta (mapcar #'- v-pos u-pos
))
127 (delta-abs (distance v-pos u-pos
))
128 (v-disp (gethash v vertex-displacement
))
129 (u-disp (gethash u vertex-displacement
))
130 (force (attractive-force delta-abs
))
133 (* (/ u
(max delta-abs
*epsilon-distance
*)) force
))
135 (setf (gethash v vertex-displacement
)
136 (mapcar #'- v-disp vu-disp
)
137 (gethash u vertex-displacement
)
138 (mapcar #'+ u-disp vu-disp
))))
140 ;; Limit the displacement
141 (dolist (v (vertices g
))
142 (unless (member v
*fixed-vertices
*)
143 (let* ((v-disp (gethash v vertex-displacement
))
144 (v-disp (mapcar #'(lambda (u) (/ u
2)) v-disp
))
145 (v-disp-abs (sqrt (apply #'+ (mapcar #'* v-disp v-disp
))))
146 (v-pos (gethash v
*vertex-position
*)))
147 (if (> v-disp-abs step
)
148 (setq v-pos
(mapcar #'(lambda (u v
)
149 (+ u
(* (/ v v-disp-abs
) step
)))
151 (setq v-pos
(mapcar #'+ v-pos v-disp
)))
152 (setq v-pos
(mapcar #'(lambda (u) (min *frame-width
* (max u
(- *frame-width
*))))
154 (setf (gethash v
*vertex-position
*) v-pos
))))
158 (maphash #'(lambda (vrt pos
)
160 (cons `((mlist simp
) ,vrt
((mlist simp
) ,@pos
))
163 (cons '(mlist simp
) result
)) ))