1 /** \file
2 * \brief Implementation of Geometry classes like ogdf::DPoint, ogdf::DRect,
3 * ogdf::DIntersectableRect, ogdf::DPolygon.
4 *
5 * \author Joachim Kupke
6 *
7 * \par License:
8 * This file is part of the Open Graph Drawing Framework (OGDF).
9 *
10 * \par
11 * Copyright (C)<br>
12 * See README.md in the OGDF root directory for details.
13 *
14 * \par
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * Version 2 or 3 as published by the Free Software Foundation;
18 * see the file LICENSE.txt included in the packaging of this file
19 * for details.
20 *
21 * \par
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * \par
28 * You should have received a copy of the GNU General Public
29 * License along with this program; if not, see
30 * http://www.gnu.org/copyleft/gpl.html
31 */
32
33
34 #include <ogdf/basic/geometry.h>
35
36
37 namespace ogdf {
38
39 const EpsilonTest OGDF_GEOM_ET(1.0e-6);
40
41 // output the rect
operator <<(std::ostream & os,const DRect & dr)42 std::ostream &operator<<(std::ostream &os, const DRect &dr) {
43 os << "\nLower left corner: " << dr.m_p1;
44 os << "\nUpper right corner: " << dr.m_p2;
45 os << "\nWidth: " << dr.width();
46 os << "\nHeight: " << dr.height();
47 return os;
48 }
49
parallelDist(const DSegment & d1,const DSegment & d2) const50 double DRect::parallelDist(const DSegment &d1, const DSegment &d2) const {
51 OGDF_ASSERT((d1.isHorizontal() && d2.isHorizontal()) ||
52 (d1.isVertical() && d2.isVertical()));
53 double d1min, d1max, d2min, d2max, paraDist, dist;
54 if(d1.isVertical()) {
55 d1min = d1.start().m_y;
56 d1max = d1.end().m_y;
57 d2min = d2.start().m_y;
58 d2max = d2.end().m_y;
59 paraDist = fabs(d1.start().m_x - d2.start().m_x);
60 }
61 else {
62 d1min = d1.start().m_x;
63 d1max = d1.end().m_x;
64 d2min = d2.start().m_x;
65 d2max = d2.end().m_x;
66 paraDist = fabs(d1.start().m_y - d2.start().m_y);
67 }
68 if(d1min > d1max) std::swap(d1min, d1max);
69 if(d2min > d2max) std::swap(d2min, d2max);
70 if(d1min > d2max || d2min > d1max) { // no overlap
71 dist = pointDist(d1.start(), d2.start());
72 Math::updateMin(dist, pointDist(d1.start(), d2.end()));
73 Math::updateMin(dist, pointDist(d1.end(), d2.start()));
74 Math::updateMin(dist, pointDist(d1.end(), d2.end()));
75 }
76 else
77 dist = paraDist; // segments overlap
78 return dist;
79 }
80
81 // output the rect
operator <<(std::ostream & os,const DIntersectableRect & dr)82 std::ostream &operator<<(std::ostream &os, const DIntersectableRect &dr) {
83 os << static_cast<DRect>(dr);
84 os << "\nCenter: " << dr.center();
85 os << "\nArea: " << dr.area();
86 return os;
87 }
88
initAreaAndCenter()89 void DIntersectableRect::initAreaAndCenter() {
90 m_area = (m_p2.m_x-m_p1.m_x)*(m_p2.m_y-m_p1.m_y);
91 m_center.m_x = m_p1.m_x + 0.5*(m_p2.m_x-m_p1.m_x);
92 m_center.m_y = m_p1.m_y + 0.5*(m_p2.m_y-m_p1.m_y);
93 }
94
move(const DPoint & point)95 void DIntersectableRect::move(const DPoint &point) {
96 double dX = point.m_x - m_center.m_x;
97 double dY = point.m_y - m_center.m_y;
98 m_center = point;
99 m_p1.m_x += dX;
100 m_p1.m_y += dY;
101 m_p2.m_x += dX;
102 m_p2.m_y += dY;
103 }
104
distance(const DIntersectableRect & other) const105 double DIntersectableRect::distance(const DIntersectableRect &other) const {
106 double dist = 0.0;
107 if(!intersects(other)) {
108 dist = parallelDist(top(), other.bottom());
109 Math::updateMin(dist, parallelDist(left(), other.right()));
110 Math::updateMin(dist, parallelDist(right(), other.left()));
111 Math::updateMin(dist, parallelDist(bottom(), other.top()));
112 }
113 return dist;
114 }
115
intersects(const DIntersectableRect & rectangle) const116 bool DIntersectableRect::intersects(const DIntersectableRect &rectangle) const {
117 bool intersect = false;
118 if(contains(rectangle.m_center) || rectangle.contains(m_center)) intersect = true;
119 else {
120 DPoint p1(rectangle.m_p1.m_x, rectangle.m_p2.m_y);
121 DPoint p2(rectangle.m_p2.m_x, rectangle.m_p1.m_y);
122 intersect = contains(p1) || contains(p2) || contains(rectangle.m_p1) || contains(rectangle.m_p2);
123 }
124 return intersect;
125 }
126
intersection(const DIntersectableRect & other) const127 DIntersectableRect DIntersectableRect::intersection(const DIntersectableRect &other) const {
128 double top1 = m_p2.m_y;
129 double bottom1 = m_p1.m_y;
130 double left1 = m_p1.m_x;
131 double right1 = m_p2.m_x;
132
133 double top2 = other.m_p2.m_y;
134 double bottom2 = other.m_p1.m_y;
135 double left2 = other.m_p1.m_x;
136 double right2 = other.m_p2.m_x;
137
138 OGDF_ASSERT(top1 >= bottom1);
139 OGDF_ASSERT(left1 <= right1);
140 OGDF_ASSERT(top2 >= bottom2);
141 OGDF_ASSERT(left2 <= right2);
142
143 double bottomInter = max(bottom1,bottom2);
144 double topInter = min(top1,top2);
145 double leftInter = max(left1,left2);
146 double rightInter = min(right1,right2);
147
148 if(bottomInter > topInter) return DIntersectableRect();
149 if(leftInter > rightInter) return DIntersectableRect();
150
151 return DIntersectableRect(DPoint(leftInter,bottomInter),DPoint(rightInter,topInter));
152 }
153
154 // gives the segment starting at point 'it'
segment(ListConstIterator<DPoint> it) const155 DSegment DPolygon::segment(ListConstIterator<DPoint> it) const
156 {
157 OGDF_ASSERT(!empty());
158 OGDF_ASSERT(size() != 1);
159 return DSegment(*it, *cyclicSucc(it));
160 }
161
162
163
164 // Assignment operator (for assigning from a rectangle).
operator =(const DRect & rect)165 DPolygon &DPolygon::operator=(const DRect &rect)
166 {
167 clear();
168 DRect r1(rect);
169 DRect r2(rect);
170 if (m_counterclock)
171 r2.xInvert();
172 else
173 r2.yInvert();
174
175 pushBack(r1.p1());
176 pushBack(r2.p1());
177 pushBack(r1.p2());
178 pushBack(r2.p2());
179
180 unify();
181 return *this;
182 }
183
184
185 // inserts the point p, which must ly on the boarder of the polygon, between the two points p1 and p2
186 // returns the index to that point, which is inserted only once
insertPoint(const DPoint & p,ListIterator<DPoint> p1,ListIterator<DPoint> p2)187 ListIterator<DPoint> DPolygon::insertPoint(
188 const DPoint &p,
189 ListIterator<DPoint> p1,
190 ListIterator<DPoint> p2)
191 {
192 ListIterator<DPoint> i = p1;
193
194 do {
195 DSegment seg = segment(i);
196 if (seg.contains(p)) {
197 if (seg.start() == p)
198 return i;
199 else if (seg.end() == p) {
200 i = cyclicSucc(i);
201 return i;
202 }
203 else
204 return insertAfter(p, i);
205 }
206
207 i = cyclicSucc(i);
208 } while (i != p2);
209
210 OGDF_ASSERT(false); // Point not in polygon, should not be reached!
211 return i;
212 }
213
214
215 // inserts 'p' on every segment (a,b) with p in the open range ]a, b[
insertCrossPoint(const DPoint & p)216 void DPolygon::insertCrossPoint(const DPoint &p)
217 {
218 ListIterator<DPoint> i = begin();
219
220 do {
221 DSegment seg = segment(i);
222 if (seg.contains(p)
223 && seg.start() != p
224 && seg.end() != p) {
225 i = insertAfter(p, i);
226 }
227
228 i = cyclicSucc(i);
229 } while (i != begin());
230 }
231
232
233 //
getCrossPoints(const DPolygon & p,List<DPoint> & crossPoints) const234 int DPolygon::getCrossPoints(const DPolygon &p, List<DPoint> &crossPoints) const
235 {
236 crossPoints.clear();
237
238 for (auto i = begin(); i.valid(); ++i) {
239 DSegment s1 = segment(i);
240 for (auto j = p.begin(); j.valid(); ++j) {
241 DSegment s2 = p.segment(j);
242 DPoint intersec;
243
244 // TODO: What to do when IntersectionType::Overlapping is returned?
245 if (s1.intersection(s2, intersec) == IntersectionType::SinglePoint)
246 crossPoints.pushBack(intersec);
247 }
248 }
249 // unify the list
250 for (auto i = crossPoints.begin(); i.valid(); ++i) {
251 for (auto j = i.succ(); j.valid(); ++j) {
252 if (*i == *j) {
253 --j;
254 crossPoints.del(crossPoints.cyclicSucc(j));
255 }
256 }
257 }
258
259 return crossPoints.size();
260 }
261
262
263
264 // delete all consecutive double-points
unify()265 void DPolygon::unify()
266 {
267 ListIterator<DPoint> iter, next;
268 for (iter = begin(); iter.valid(); ++iter) {
269 next = cyclicSucc(iter);
270 while (*iter == *next) {
271 del(next);
272 next = cyclicSucc(iter);
273 if (iter == next)
274 break;
275 }
276 }
277 }
278
279
280 // deletes all points, which are not facets
normalize()281 void DPolygon::normalize()
282 {
283 unify();
284
285 ListIterator<DPoint> iter, next;
286 for (iter = begin(); iter.valid(); ++iter) {
287 for( ; ; ) {
288 next = cyclicSucc(iter);
289 DSegment s1 = segment(iter);
290 DSegment s2 = segment(next);
291 DRect r (*iter, *cyclicSucc(next));
292 if (s1.slope() == s2.slope() && r.contains(*next))
293 del(next);
294 else
295 break; // while
296 }
297 }
298 }
299
300
301
302 // Checks wether a Point /a p is inside the Poylgon or not.
containsPoint(DPoint & p) const303 bool DPolygon::containsPoint(DPoint &p) const
304 {
305 if (size() < 3) {
306 return false;
307 }
308
309 double angle = 0.0;
310 DPolygon::const_iterator i = cyclicPred(begin());
311 double lastangle = atan2((*i).m_y - p.m_y, (*i).m_x - p.m_x);
312 for (const DPoint &q : *this)
313 {
314 double tempangle = atan2(q.m_y - p.m_y, q.m_x - p.m_x);
315 double step = lastangle - tempangle;
316 while (step > Math::pi) step -= 2.0*Math::pi;
317 while (step < -Math::pi) step += 2.0*Math::pi;
318 angle += step;
319 lastangle = tempangle;
320 }
321
322 double d = angle / (2.0 * Math::pi);
323 int rounds = static_cast<int>(d<0?d-.5:d+.5);
324
325 return (rounds % 2) != 0;
326 }
327
328
329 // outputs the polygon
operator <<(std::ostream & os,const DPolygon & dop)330 std::ostream &operator<<(std::ostream &os, const DPolygon &dop)
331 {
332 print(os, dop, ' ');
333 return os;
334 }
335
orientation(const DPoint & p,const DPoint & q,const DPoint & r)336 int orientation(const DPoint &p, const DPoint &q, const DPoint &r)
337 {
338 double d1 = (p.m_x - q.m_x) * (p.m_y - r.m_y);
339 double d2 = (p.m_y - q.m_y) * (p.m_x - r.m_x);
340
341 if(d1 == d2)
342 return 0;
343 else
344 return (d1 > d2) ? +1 : -1;
345 }
346
347 }
348