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