Chris@16
|
1 //=======================================================================
|
Chris@16
|
2 // Copyright 2007 Aaron Windsor
|
Chris@16
|
3 //
|
Chris@16
|
4 // Distributed under the Boost Software License, Version 1.0. (See
|
Chris@16
|
5 // accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
6 // http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
7 //=======================================================================
|
Chris@16
|
8 #ifndef __IS_STRAIGHT_LINE_DRAWING_HPP__
|
Chris@16
|
9 #define __IS_STRAIGHT_LINE_DRAWING_HPP__
|
Chris@16
|
10
|
Chris@16
|
11 #include <boost/config.hpp>
|
Chris@16
|
12 #include <boost/next_prior.hpp>
|
Chris@16
|
13 #include <boost/tuple/tuple.hpp>
|
Chris@16
|
14 #include <boost/tuple/tuple_comparison.hpp>
|
Chris@16
|
15 #include <boost/property_map/property_map.hpp>
|
Chris@16
|
16 #include <boost/graph/properties.hpp>
|
Chris@16
|
17 #include <boost/graph/planar_detail/bucket_sort.hpp>
|
Chris@16
|
18
|
Chris@16
|
19 #include <algorithm>
|
Chris@16
|
20 #include <vector>
|
Chris@16
|
21 #include <set>
|
Chris@16
|
22 #include <map>
|
Chris@16
|
23
|
Chris@16
|
24
|
Chris@16
|
25
|
Chris@16
|
26 namespace boost
|
Chris@16
|
27 {
|
Chris@16
|
28
|
Chris@16
|
29 // Return true exactly when the line segments s1 = ((x1,y1), (x2,y2)) and
|
Chris@16
|
30 // s2 = ((a1,b1), (a2,b2)) intersect in a point other than the endpoints of
|
Chris@16
|
31 // the line segments. The one exception to this rule is when s1 = s2, in
|
Chris@16
|
32 // which case false is returned - this is to accomodate multiple edges
|
Chris@16
|
33 // between the same pair of vertices, which shouldn't invalidate the straight
|
Chris@16
|
34 // line embedding. A tolerance variable epsilon can also be used, which
|
Chris@16
|
35 // defines how far away from the endpoints of s1 and s2 we want to consider
|
Chris@16
|
36 // an intersection.
|
Chris@16
|
37
|
Chris@16
|
38 inline bool intersects(double x1, double y1,
|
Chris@16
|
39 double x2, double y2,
|
Chris@16
|
40 double a1, double b1,
|
Chris@16
|
41 double a2, double b2,
|
Chris@16
|
42 double epsilon = 0.000001
|
Chris@16
|
43 )
|
Chris@16
|
44 {
|
Chris@16
|
45
|
Chris@16
|
46 if (x1 - x2 == 0)
|
Chris@16
|
47 {
|
Chris@16
|
48 std::swap(x1,a1);
|
Chris@16
|
49 std::swap(y1,b1);
|
Chris@16
|
50 std::swap(x2,a2);
|
Chris@16
|
51 std::swap(y2,b2);
|
Chris@16
|
52 }
|
Chris@16
|
53
|
Chris@16
|
54 if (x1 - x2 == 0)
|
Chris@16
|
55 {
|
Chris@16
|
56 BOOST_USING_STD_MAX();
|
Chris@16
|
57 BOOST_USING_STD_MIN();
|
Chris@16
|
58
|
Chris@16
|
59 //two vertical line segments
|
Chris@16
|
60 double min_y = min BOOST_PREVENT_MACRO_SUBSTITUTION(y1,y2);
|
Chris@16
|
61 double max_y = max BOOST_PREVENT_MACRO_SUBSTITUTION(y1,y2);
|
Chris@16
|
62 double min_b = min BOOST_PREVENT_MACRO_SUBSTITUTION(b1,b2);
|
Chris@16
|
63 double max_b = max BOOST_PREVENT_MACRO_SUBSTITUTION(b1,b2);
|
Chris@16
|
64 if ((max_y > max_b && max_b > min_y) ||
|
Chris@16
|
65 (max_b > max_y && max_y > min_b)
|
Chris@16
|
66 )
|
Chris@16
|
67 return true;
|
Chris@16
|
68 else
|
Chris@16
|
69 return false;
|
Chris@16
|
70 }
|
Chris@16
|
71
|
Chris@16
|
72 double x_diff = x1 - x2;
|
Chris@16
|
73 double y_diff = y1 - y2;
|
Chris@16
|
74 double a_diff = a2 - a1;
|
Chris@16
|
75 double b_diff = b2 - b1;
|
Chris@16
|
76
|
Chris@16
|
77 double beta_denominator = b_diff - (y_diff/((double)x_diff)) * a_diff;
|
Chris@16
|
78
|
Chris@16
|
79 if (beta_denominator == 0)
|
Chris@16
|
80 {
|
Chris@16
|
81 //parallel lines
|
Chris@16
|
82 return false;
|
Chris@16
|
83 }
|
Chris@16
|
84
|
Chris@16
|
85 double beta = (b2 - y2 - (y_diff/((double)x_diff)) * (a2 - x2)) /
|
Chris@16
|
86 beta_denominator;
|
Chris@16
|
87 double alpha = (a2 - x2 - beta*(a_diff))/x_diff;
|
Chris@16
|
88
|
Chris@16
|
89 double upper_bound = 1 - epsilon;
|
Chris@16
|
90 double lower_bound = 0 + epsilon;
|
Chris@16
|
91
|
Chris@16
|
92 return (beta < upper_bound && beta > lower_bound &&
|
Chris@16
|
93 alpha < upper_bound && alpha > lower_bound);
|
Chris@16
|
94
|
Chris@16
|
95 }
|
Chris@16
|
96
|
Chris@16
|
97
|
Chris@16
|
98 template <typename Graph,
|
Chris@16
|
99 typename GridPositionMap,
|
Chris@16
|
100 typename VertexIndexMap
|
Chris@16
|
101 >
|
Chris@16
|
102 bool is_straight_line_drawing(const Graph& g,
|
Chris@16
|
103 GridPositionMap drawing,
|
Chris@16
|
104 VertexIndexMap
|
Chris@16
|
105 )
|
Chris@16
|
106 {
|
Chris@16
|
107
|
Chris@16
|
108 typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
|
Chris@16
|
109 typedef typename graph_traits<Graph>::edge_descriptor edge_t;
|
Chris@16
|
110 typedef typename graph_traits<Graph>::edge_iterator edge_iterator_t;
|
Chris@16
|
111
|
Chris@16
|
112 typedef std::size_t x_coord_t;
|
Chris@16
|
113 typedef std::size_t y_coord_t;
|
Chris@16
|
114 typedef boost::tuple<edge_t, x_coord_t, y_coord_t> edge_event_t;
|
Chris@16
|
115 typedef typename std::vector< edge_event_t > edge_event_queue_t;
|
Chris@16
|
116
|
Chris@16
|
117 typedef tuple<y_coord_t, y_coord_t, x_coord_t, x_coord_t> active_map_key_t;
|
Chris@16
|
118 typedef edge_t active_map_value_t;
|
Chris@16
|
119 typedef std::map< active_map_key_t, active_map_value_t > active_map_t;
|
Chris@16
|
120 typedef typename active_map_t::iterator active_map_iterator_t;
|
Chris@16
|
121
|
Chris@16
|
122
|
Chris@16
|
123 edge_event_queue_t edge_event_queue;
|
Chris@16
|
124 active_map_t active_edges;
|
Chris@16
|
125
|
Chris@16
|
126 edge_iterator_t ei, ei_end;
|
Chris@16
|
127 for(boost::tie(ei,ei_end) = edges(g); ei != ei_end; ++ei)
|
Chris@16
|
128 {
|
Chris@16
|
129 edge_t e(*ei);
|
Chris@16
|
130 vertex_t s(source(e,g));
|
Chris@16
|
131 vertex_t t(target(e,g));
|
Chris@16
|
132 edge_event_queue.push_back
|
Chris@16
|
133 (make_tuple(e,
|
Chris@16
|
134 static_cast<std::size_t>(drawing[s].x),
|
Chris@16
|
135 static_cast<std::size_t>(drawing[s].y)
|
Chris@16
|
136 )
|
Chris@16
|
137 );
|
Chris@16
|
138 edge_event_queue.push_back
|
Chris@16
|
139 (make_tuple(e,
|
Chris@16
|
140 static_cast<std::size_t>(drawing[t].x),
|
Chris@16
|
141 static_cast<std::size_t>(drawing[t].y)
|
Chris@16
|
142 )
|
Chris@16
|
143 );
|
Chris@16
|
144 }
|
Chris@16
|
145
|
Chris@16
|
146 // Order by edge_event_queue by first, then second coordinate
|
Chris@16
|
147 // (bucket_sort is a stable sort.)
|
Chris@16
|
148 bucket_sort(edge_event_queue.begin(), edge_event_queue.end(),
|
Chris@16
|
149 property_map_tuple_adaptor<edge_event_t, 2>()
|
Chris@16
|
150 );
|
Chris@16
|
151
|
Chris@16
|
152 bucket_sort(edge_event_queue.begin(), edge_event_queue.end(),
|
Chris@16
|
153 property_map_tuple_adaptor<edge_event_t, 1>()
|
Chris@16
|
154 );
|
Chris@16
|
155
|
Chris@16
|
156 typedef typename edge_event_queue_t::iterator event_queue_iterator_t;
|
Chris@16
|
157 event_queue_iterator_t itr_end = edge_event_queue.end();
|
Chris@16
|
158 for(event_queue_iterator_t itr = edge_event_queue.begin();
|
Chris@16
|
159 itr != itr_end; ++itr
|
Chris@16
|
160 )
|
Chris@16
|
161 {
|
Chris@16
|
162 edge_t e(get<0>(*itr));
|
Chris@16
|
163 vertex_t source_v(source(e,g));
|
Chris@16
|
164 vertex_t target_v(target(e,g));
|
Chris@16
|
165 if (drawing[source_v].y > drawing[target_v].y)
|
Chris@16
|
166 std::swap(source_v, target_v);
|
Chris@16
|
167
|
Chris@16
|
168 active_map_key_t key(get(drawing, source_v).y,
|
Chris@16
|
169 get(drawing, target_v).y,
|
Chris@16
|
170 get(drawing, source_v).x,
|
Chris@16
|
171 get(drawing, target_v).x
|
Chris@16
|
172 );
|
Chris@16
|
173
|
Chris@16
|
174 active_map_iterator_t a_itr = active_edges.find(key);
|
Chris@16
|
175 if (a_itr == active_edges.end())
|
Chris@16
|
176 {
|
Chris@16
|
177 active_edges[key] = e;
|
Chris@16
|
178 }
|
Chris@16
|
179 else
|
Chris@16
|
180 {
|
Chris@16
|
181 active_map_iterator_t before, after;
|
Chris@16
|
182 if (a_itr == active_edges.begin())
|
Chris@16
|
183 before = active_edges.end();
|
Chris@16
|
184 else
|
Chris@16
|
185 before = prior(a_itr);
|
Chris@16
|
186 after = boost::next(a_itr);
|
Chris@16
|
187
|
Chris@16
|
188 if (before != active_edges.end())
|
Chris@16
|
189 {
|
Chris@16
|
190
|
Chris@16
|
191 edge_t f = before->second;
|
Chris@16
|
192 vertex_t e_source(source(e,g));
|
Chris@16
|
193 vertex_t e_target(target(e,g));
|
Chris@16
|
194 vertex_t f_source(source(f,g));
|
Chris@16
|
195 vertex_t f_target(target(f,g));
|
Chris@16
|
196
|
Chris@16
|
197 if (intersects(drawing[e_source].x,
|
Chris@16
|
198 drawing[e_source].y,
|
Chris@16
|
199 drawing[e_target].x,
|
Chris@16
|
200 drawing[e_target].y,
|
Chris@16
|
201 drawing[f_source].x,
|
Chris@16
|
202 drawing[f_source].y,
|
Chris@16
|
203 drawing[f_target].x,
|
Chris@16
|
204 drawing[f_target].y
|
Chris@16
|
205 )
|
Chris@16
|
206 )
|
Chris@16
|
207 return false;
|
Chris@16
|
208 }
|
Chris@16
|
209
|
Chris@16
|
210 if (after != active_edges.end())
|
Chris@16
|
211 {
|
Chris@16
|
212
|
Chris@16
|
213 edge_t f = after->second;
|
Chris@16
|
214 vertex_t e_source(source(e,g));
|
Chris@16
|
215 vertex_t e_target(target(e,g));
|
Chris@16
|
216 vertex_t f_source(source(f,g));
|
Chris@16
|
217 vertex_t f_target(target(f,g));
|
Chris@16
|
218
|
Chris@16
|
219 if (intersects(drawing[e_source].x,
|
Chris@16
|
220 drawing[e_source].y,
|
Chris@16
|
221 drawing[e_target].x,
|
Chris@16
|
222 drawing[e_target].y,
|
Chris@16
|
223 drawing[f_source].x,
|
Chris@16
|
224 drawing[f_source].y,
|
Chris@16
|
225 drawing[f_target].x,
|
Chris@16
|
226 drawing[f_target].y
|
Chris@16
|
227 )
|
Chris@16
|
228 )
|
Chris@16
|
229 return false;
|
Chris@16
|
230 }
|
Chris@16
|
231
|
Chris@16
|
232 active_edges.erase(a_itr);
|
Chris@16
|
233
|
Chris@16
|
234 }
|
Chris@16
|
235 }
|
Chris@16
|
236
|
Chris@16
|
237 return true;
|
Chris@16
|
238
|
Chris@16
|
239 }
|
Chris@16
|
240
|
Chris@16
|
241
|
Chris@16
|
242 template <typename Graph, typename GridPositionMap>
|
Chris@16
|
243 bool is_straight_line_drawing(const Graph& g, GridPositionMap drawing)
|
Chris@16
|
244 {
|
Chris@16
|
245 return is_straight_line_drawing(g, drawing, get(vertex_index,g));
|
Chris@16
|
246 }
|
Chris@16
|
247
|
Chris@16
|
248 }
|
Chris@16
|
249
|
Chris@16
|
250 #endif // __IS_STRAIGHT_LINE_DRAWING_HPP__
|