1 // Copyright 2009 The Trustees of Indiana University.
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
7 // Authors: Jeremiah Willcock
10 #ifndef BOOST_GRAPH_TOPOLOGY_HPP
11 #define BOOST_GRAPH_TOPOLOGY_HPP
13 #include <boost/config/no_tr1/cmath.hpp>
15 #include <boost/random/uniform_01.hpp>
16 #include <boost/random/linear_congruential.hpp>
17 #include <boost/math/constants/constants.hpp> // For root_two
18 #include <boost/algorithm/minmax.hpp>
19 #include <boost/config.hpp> // For BOOST_STATIC_CONSTANT
20 #include <boost/math/special_functions/hypot.hpp>
22 // Classes and concepts to represent points in a space, with distance and move
23 // operations (used for Gurson-Atun layout), plus other things like bounding
24 // boxes used for other layout algorithms.
28 /***********************************************************
30 ***********************************************************/
31 template<std::size_t Dims
>
34 public: // For VisualAge C++
37 BOOST_STATIC_CONSTANT(std::size_t, dimensions
= Dims
);
39 double& operator[](std::size_t i
) {return values
[i
];}
40 const double& operator[](std::size_t i
) const {return values
[i
];}
46 public: // For VisualAge C++
47 struct point_difference
49 BOOST_STATIC_CONSTANT(std::size_t, dimensions
= Dims
);
51 for (std::size_t i
= 0; i
< Dims
; ++i
) values
[i
] = 0.;
53 double& operator[](std::size_t i
) {return values
[i
];}
54 const double& operator[](std::size_t i
) const {return values
[i
];}
56 friend point_difference
operator+(const point_difference
& a
, const point_difference
& b
) {
57 point_difference result
;
58 for (std::size_t i
= 0; i
< Dims
; ++i
)
59 result
[i
] = a
[i
] + b
[i
];
63 friend point_difference
& operator+=(point_difference
& a
, const point_difference
& b
) {
64 for (std::size_t i
= 0; i
< Dims
; ++i
)
69 friend point_difference
operator-(const point_difference
& a
) {
70 point_difference result
;
71 for (std::size_t i
= 0; i
< Dims
; ++i
)
76 friend point_difference
operator-(const point_difference
& a
, const point_difference
& b
) {
77 point_difference result
;
78 for (std::size_t i
= 0; i
< Dims
; ++i
)
79 result
[i
] = a
[i
] - b
[i
];
83 friend point_difference
& operator-=(point_difference
& a
, const point_difference
& b
) {
84 for (std::size_t i
= 0; i
< Dims
; ++i
)
89 friend point_difference
operator*(const point_difference
& a
, const point_difference
& b
) {
90 point_difference result
;
91 for (std::size_t i
= 0; i
< Dims
; ++i
)
92 result
[i
] = a
[i
] * b
[i
];
96 friend point_difference
operator*(const point_difference
& a
, double b
) {
97 point_difference result
;
98 for (std::size_t i
= 0; i
< Dims
; ++i
)
103 friend point_difference
operator*(double a
, const point_difference
& b
) {
104 point_difference result
;
105 for (std::size_t i
= 0; i
< Dims
; ++i
)
106 result
[i
] = a
* b
[i
];
110 friend point_difference
operator/(const point_difference
& a
, const point_difference
& b
) {
111 point_difference result
;
112 for (std::size_t i
= 0; i
< Dims
; ++i
)
113 result
[i
] = (b
[i
] == 0.) ? 0. : a
[i
] / b
[i
];
117 friend double dot(const point_difference
& a
, const point_difference
& b
) {
119 for (std::size_t i
= 0; i
< Dims
; ++i
)
120 result
+= a
[i
] * b
[i
];
129 typedef point point_type
;
130 typedef point_difference point_difference_type
;
132 double distance(point a
, point b
) const
135 for (std::size_t i
= 0; i
< Dims
; ++i
) {
136 double diff
= b
[i
] - a
[i
];
137 dist
= boost::math::hypot(dist
, diff
);
139 // Exact properties of the distance are not important, as long as
140 // < on what this returns matches real distances; l_2 is used because
141 // Fruchterman-Reingold also uses this code and it relies on l_2.
145 point
move_position_toward(point a
, double fraction
, point b
) const
148 for (std::size_t i
= 0; i
< Dims
; ++i
)
149 result
[i
] = a
[i
] + (b
[i
] - a
[i
]) * fraction
;
153 point_difference
difference(point a
, point b
) const {
154 point_difference result
;
155 for (std::size_t i
= 0; i
< Dims
; ++i
)
156 result
[i
] = a
[i
] - b
[i
];
160 point
adjust(point a
, point_difference delta
) const {
162 for (std::size_t i
= 0; i
< Dims
; ++i
)
163 result
[i
] = a
[i
] + delta
[i
];
167 point
pointwise_min(point a
, point b
) const {
168 BOOST_USING_STD_MIN();
170 for (std::size_t i
= 0; i
< Dims
; ++i
)
171 result
[i
] = min
BOOST_PREVENT_MACRO_SUBSTITUTION (a
[i
], b
[i
]);
175 point
pointwise_max(point a
, point b
) const {
176 BOOST_USING_STD_MAX();
178 for (std::size_t i
= 0; i
< Dims
; ++i
)
179 result
[i
] = max
BOOST_PREVENT_MACRO_SUBSTITUTION (a
[i
], b
[i
]);
183 double norm(point_difference delta
) const {
185 for (std::size_t i
= 0; i
< Dims
; ++i
)
186 n
= boost::math::hypot(n
, delta
[i
]);
190 double volume(point_difference delta
) const {
192 for (std::size_t i
= 0; i
< Dims
; ++i
)
199 template<std::size_t Dims
,
200 typename RandomNumberGenerator
= minstd_rand
>
201 class hypercube_topology
: public convex_topology
<Dims
>
203 typedef uniform_01
<RandomNumberGenerator
, double> rand_t
;
206 typedef typename convex_topology
<Dims
>::point_type point_type
;
207 typedef typename convex_topology
<Dims
>::point_difference_type point_difference_type
;
209 explicit hypercube_topology(double scaling
= 1.0)
210 : gen_ptr(new RandomNumberGenerator
), rand(new rand_t(*gen_ptr
)),
214 hypercube_topology(RandomNumberGenerator
& gen
, double scaling
= 1.0)
215 : gen_ptr(), rand(new rand_t(gen
)), scaling(scaling
) { }
217 point_type
random_point() const
220 for (std::size_t i
= 0; i
< Dims
; ++i
)
221 p
[i
] = (*rand
)() * scaling
;
225 point_type
bound(point_type a
) const
227 BOOST_USING_STD_MIN();
228 BOOST_USING_STD_MAX();
230 for (std::size_t i
= 0; i
< Dims
; ++i
)
231 p
[i
] = min
BOOST_PREVENT_MACRO_SUBSTITUTION (scaling
, max
BOOST_PREVENT_MACRO_SUBSTITUTION (-scaling
, a
[i
]));
235 double distance_from_boundary(point_type a
) const
237 BOOST_USING_STD_MIN();
238 BOOST_USING_STD_MAX();
239 #ifndef BOOST_NO_STDC_NAMESPACE
242 BOOST_STATIC_ASSERT (Dims
>= 1);
243 double dist
= abs(scaling
- a
[0]);
244 for (std::size_t i
= 1; i
< Dims
; ++i
)
245 dist
= min
BOOST_PREVENT_MACRO_SUBSTITUTION (dist
, abs(scaling
- a
[i
]));
249 point_type
center() const {
251 for (std::size_t i
= 0; i
< Dims
; ++i
)
257 shared_ptr
<RandomNumberGenerator
> gen_ptr
;
258 shared_ptr
<rand_t
> rand
;
262 template<typename RandomNumberGenerator
= minstd_rand
>
263 class square_topology
: public hypercube_topology
<2, RandomNumberGenerator
>
265 typedef hypercube_topology
<2, RandomNumberGenerator
> inherited
;
268 explicit square_topology(double scaling
= 1.0) : inherited(scaling
) { }
270 square_topology(RandomNumberGenerator
& gen
, double scaling
= 1.0)
271 : inherited(gen
, scaling
) { }
274 template<typename RandomNumberGenerator
= minstd_rand
>
275 class rectangle_topology
: public convex_topology
<2>
277 typedef uniform_01
<RandomNumberGenerator
, double> rand_t
;
280 rectangle_topology(double left
, double top
, double right
, double bottom
)
281 : gen_ptr(new RandomNumberGenerator
), rand(new rand_t(*gen_ptr
)),
282 left(std::min
BOOST_PREVENT_MACRO_SUBSTITUTION (left
, right
)),
283 top(std::min
BOOST_PREVENT_MACRO_SUBSTITUTION (top
, bottom
)),
284 right(std::max
BOOST_PREVENT_MACRO_SUBSTITUTION (left
, right
)),
285 bottom(std::max
BOOST_PREVENT_MACRO_SUBSTITUTION (top
, bottom
)) { }
287 rectangle_topology(RandomNumberGenerator
& gen
, double left
, double top
, double right
, double bottom
)
288 : gen_ptr(), rand(new rand_t(gen
)),
289 left(std::min
BOOST_PREVENT_MACRO_SUBSTITUTION (left
, right
)),
290 top(std::min
BOOST_PREVENT_MACRO_SUBSTITUTION (top
, bottom
)),
291 right(std::max
BOOST_PREVENT_MACRO_SUBSTITUTION (left
, right
)),
292 bottom(std::max
BOOST_PREVENT_MACRO_SUBSTITUTION (top
, bottom
)) { }
294 typedef typename convex_topology
<2>::point_type point_type
;
295 typedef typename convex_topology
<2>::point_difference_type point_difference_type
;
297 point_type
random_point() const
300 p
[0] = (*rand
)() * (right
- left
) + left
;
301 p
[1] = (*rand
)() * (bottom
- top
) + top
;
305 point_type
bound(point_type a
) const
307 BOOST_USING_STD_MIN();
308 BOOST_USING_STD_MAX();
310 p
[0] = min
BOOST_PREVENT_MACRO_SUBSTITUTION (right
, max
BOOST_PREVENT_MACRO_SUBSTITUTION (left
, a
[0]));
311 p
[1] = min
BOOST_PREVENT_MACRO_SUBSTITUTION (bottom
, max
BOOST_PREVENT_MACRO_SUBSTITUTION (top
, a
[1]));
315 double distance_from_boundary(point_type a
) const
317 BOOST_USING_STD_MIN();
318 BOOST_USING_STD_MAX();
319 #ifndef BOOST_NO_STDC_NAMESPACE
322 double dist
= abs(left
- a
[0]);
323 dist
= min
BOOST_PREVENT_MACRO_SUBSTITUTION (dist
, abs(right
- a
[0]));
324 dist
= min
BOOST_PREVENT_MACRO_SUBSTITUTION (dist
, abs(top
- a
[1]));
325 dist
= min
BOOST_PREVENT_MACRO_SUBSTITUTION (dist
, abs(bottom
- a
[1]));
329 point_type
center() const {
331 result
[0] = (left
+ right
) / 2.;
332 result
[1] = (top
+ bottom
) / 2.;
337 shared_ptr
<RandomNumberGenerator
> gen_ptr
;
338 shared_ptr
<rand_t
> rand
;
339 double left
, top
, right
, bottom
;
342 template<typename RandomNumberGenerator
= minstd_rand
>
343 class cube_topology
: public hypercube_topology
<3, RandomNumberGenerator
>
345 typedef hypercube_topology
<3, RandomNumberGenerator
> inherited
;
348 explicit cube_topology(double scaling
= 1.0) : inherited(scaling
) { }
350 cube_topology(RandomNumberGenerator
& gen
, double scaling
= 1.0)
351 : inherited(gen
, scaling
) { }
354 template<std::size_t Dims
,
355 typename RandomNumberGenerator
= minstd_rand
>
356 class ball_topology
: public convex_topology
<Dims
>
358 typedef uniform_01
<RandomNumberGenerator
, double> rand_t
;
361 typedef typename convex_topology
<Dims
>::point_type point_type
;
363 explicit ball_topology(double radius
= 1.0)
364 : gen_ptr(new RandomNumberGenerator
), rand(new rand_t(*gen_ptr
)),
368 ball_topology(RandomNumberGenerator
& gen
, double radius
= 1.0)
369 : gen_ptr(), rand(new rand_t(gen
)), radius(radius
) { }
371 point_type
random_point() const
377 for (std::size_t i
= 0; i
< Dims
; ++i
) {
378 double x
= (*rand
)() * 2*radius
- radius
;
382 } while (dist_sum
> radius
*radius
);
386 point_type
bound(point_type a
) const
388 BOOST_USING_STD_MIN();
389 BOOST_USING_STD_MAX();
391 for (std::size_t i
= 0; i
< Dims
; ++i
)
392 r
= boost::math::hypot(r
, a
[i
]);
393 if (r
<= radius
) return a
;
394 double scaling_factor
= radius
/ r
;
396 for (std::size_t i
= 0; i
< Dims
; ++i
)
397 p
[i
] = a
[i
] * scaling_factor
;
401 double distance_from_boundary(point_type a
) const
404 for (std::size_t i
= 0; i
< Dims
; ++i
)
405 r
= boost::math::hypot(r
, a
[i
]);
409 point_type
center() const {
411 for (std::size_t i
= 0; i
< Dims
; ++i
)
417 shared_ptr
<RandomNumberGenerator
> gen_ptr
;
418 shared_ptr
<rand_t
> rand
;
422 template<typename RandomNumberGenerator
= minstd_rand
>
423 class circle_topology
: public ball_topology
<2, RandomNumberGenerator
>
425 typedef ball_topology
<2, RandomNumberGenerator
> inherited
;
428 explicit circle_topology(double radius
= 1.0) : inherited(radius
) { }
430 circle_topology(RandomNumberGenerator
& gen
, double radius
= 1.0)
431 : inherited(gen
, radius
) { }
434 template<typename RandomNumberGenerator
= minstd_rand
>
435 class sphere_topology
: public ball_topology
<3, RandomNumberGenerator
>
437 typedef ball_topology
<3, RandomNumberGenerator
> inherited
;
440 explicit sphere_topology(double radius
= 1.0) : inherited(radius
) { }
442 sphere_topology(RandomNumberGenerator
& gen
, double radius
= 1.0)
443 : inherited(gen
, radius
) { }
446 template<typename RandomNumberGenerator
= minstd_rand
>
449 // Heart is defined as the union of three shapes:
450 // Square w/ corners (+-1000, -1000), (0, 0), (0, -2000)
451 // Circle centered at (-500, -500) radius 500*sqrt(2)
452 // Circle centered at (500, -500) radius 500*sqrt(2)
453 // Bounding box (-1000, -2000) - (1000, 500*(sqrt(2) - 1))
457 point() { values
[0] = 0.0; values
[1] = 0.0; }
458 point(double x
, double y
) { values
[0] = x
; values
[1] = y
; }
460 double& operator[](std::size_t i
) { return values
[i
]; }
461 double operator[](std::size_t i
) const { return values
[i
]; }
467 bool in_heart(point p
) const
469 #ifndef BOOST_NO_STDC_NAMESPACE
473 if (p
[1] < abs(p
[0]) - 2000) return false; // Bottom
474 if (p
[1] <= -1000) return true; // Diagonal of square
475 if (boost::math::hypot(p
[0] - -500, p
[1] - -500) <= 500. * boost::math::constants::root_two
<double>())
476 return true; // Left circle
477 if (boost::math::hypot(p
[0] - 500, p
[1] - -500) <= 500. * boost::math::constants::root_two
<double>())
478 return true; // Right circle
482 bool segment_within_heart(point p1
, point p2
) const
484 // Assumes that p1 and p2 are within the heart
485 if ((p1
[0] < 0) == (p2
[0] < 0)) return true; // Same side of symmetry line
486 if (p1
[0] == p2
[0]) return true; // Vertical
487 double slope
= (p2
[1] - p1
[1]) / (p2
[0] - p1
[0]);
488 double intercept
= p1
[1] - p1
[0] * slope
;
489 if (intercept
> 0) return false; // Crosses between circles
493 typedef uniform_01
<RandomNumberGenerator
, double> rand_t
;
496 typedef point point_type
;
499 : gen_ptr(new RandomNumberGenerator
), rand(new rand_t(*gen_ptr
)) { }
501 heart_topology(RandomNumberGenerator
& gen
)
502 : gen_ptr(), rand(new rand_t(gen
)) { }
504 point
random_point() const
508 result
[0] = (*rand
)() * (1000 + 1000 * boost::math::constants::root_two
<double>()) - (500 + 500 * boost::math::constants::root_two
<double>());
509 result
[1] = (*rand
)() * (2000 + 500 * (boost::math::constants::root_two
<double>() - 1)) - 2000;
510 } while (!in_heart(result
));
514 // Not going to provide clipping to bounding region or distance from boundary
516 double distance(point a
, point b
) const
518 if (segment_within_heart(a
, b
)) {
520 return boost::math::hypot(b
[0] - a
[0], b
[1] - a
[1]);
522 // Straight line bending around (0, 0)
523 return boost::math::hypot(a
[0], a
[1]) + boost::math::hypot(b
[0], b
[1]);
527 point
move_position_toward(point a
, double fraction
, point b
) const
529 if (segment_within_heart(a
, b
)) {
531 return point(a
[0] + (b
[0] - a
[0]) * fraction
,
532 a
[1] + (b
[1] - a
[1]) * fraction
);
534 double distance_to_point_a
= boost::math::hypot(a
[0], a
[1]);
535 double distance_to_point_b
= boost::math::hypot(b
[0], b
[1]);
536 double location_of_point
= distance_to_point_a
/
537 (distance_to_point_a
+ distance_to_point_b
);
538 if (fraction
< location_of_point
)
539 return point(a
[0] * (1 - fraction
/ location_of_point
),
540 a
[1] * (1 - fraction
/ location_of_point
));
543 b
[0] * ((fraction
- location_of_point
) / (1 - location_of_point
)),
544 b
[1] * ((fraction
- location_of_point
) / (1 - location_of_point
)));
549 shared_ptr
<RandomNumberGenerator
> gen_ptr
;
550 shared_ptr
<rand_t
> rand
;
555 #endif // BOOST_GRAPH_TOPOLOGY_HPP