Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
geomtypes.cpp
Go to the documentation of this file.
1/*
2 * vim: ts=4 sw=4 et tw=0 wm=0
3 *
4 * libavoid - Fast, Incremental, Object-avoiding Line Router
5 *
6 * Copyright (C) 2004-2014 Monash University
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 * See the file LICENSE.LGPL distributed with the library.
13 *
14 * Licensees holding a valid commercial license may use this file in
15 * accordance with the commercial license agreement provided with the
16 * library.
17 *
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
21 *
22 * Author(s): Michael Wybrow
23*/
24
25
26#include <cmath>
27#include <cfloat>
28#include <cstdlib>
29#include <algorithm>
30
31#include "libavoid/geomtypes.h"
32#include "libavoid/shape.h"
33#include "libavoid/router.h"
34#include "libavoid/assertions.h"
35
36
37namespace Avoid
38{
39
40
41Point::Point() :
42 id(0),
44{
45}
46
47
48Point::Point(const double xv, const double yv) :
49 x(xv),
50 y(yv),
51 id(0),
53{
54}
55
56
57bool Point::operator==(const Point& rhs) const
58{
59 if ((x == rhs.x) && (y == rhs.y))
60 {
61 return true;
62 }
63 return false;
64}
65
66
67bool Point::operator!=(const Point& rhs) const
68{
69 if ((x != rhs.x) || (y != rhs.y))
70 {
71 return true;
72 }
73 return false;
74}
75
76
77bool Point::equals(const Point& rhs, double epsilon) const
78{
79 if ( (fabs(x - rhs.x) < epsilon) && (fabs(y - rhs.y) < epsilon) )
80 {
81 return true;
82 }
83 return false;
84}
85
86
87// Just defined to allow std::set<Point>. Not particularly meaningful!
88bool Point::operator<(const Point& rhs) const
89{
90 if (x == rhs.x)
91 {
92 return (y < rhs.y);
93 }
94 return (x < rhs.x);
95}
96
97
98double& Point::operator[](const size_t dimension)
99{
100 COLA_ASSERT((dimension == 0) || (dimension == 1));
101 return ((dimension == 0) ? x : y);
102}
103
104
105const double& Point::operator[](const size_t dimension) const
106{
107 COLA_ASSERT((dimension == 0) || (dimension == 1));
108 return ((dimension == 0) ? x : y);
109}
110
111Point Point::operator+(const Point& rhs) const
112{
113 return Point(x + rhs.x, y + rhs.y);
114}
115
116
117Point Point::operator-(const Point& rhs) const
118{
119 return Point(x - rhs.x, y - rhs.y);
120}
121
122
125 _id(poly._id),
126 psRef(poly.size()),
127 psPoints(poly.size())
128{
129 COLA_ASSERT(router != nullptr);
130 for (size_t i = 0; i < poly.size(); ++i)
131 {
132 if (poly.ps[i].id == 0)
133 {
134 // Can't be referenced, so just make a copy of the point.
135 psRef[i] = std::make_pair((Polygon *) nullptr,
137 psPoints[i] = poly.ps[i];
138 }
139 else
140 {
141 const Polygon *polyPtr = nullptr;
142 for (ObstacleList::const_iterator sh = router->m_obstacles.begin();
143 sh != router->m_obstacles.end(); ++sh)
144 {
145 if ((*sh)->id() == poly.ps[i].id)
146 {
147 const Polygon& poly = (*sh)->polygon();
148 polyPtr = &poly;
149 break;
150 }
151 }
152 COLA_ASSERT(polyPtr != nullptr);
153 psRef[i] = std::make_pair(polyPtr, poly.ps[i].vn);
154 }
155 }
156}
157
158
164
165
167{
168 psRef.clear();
169 psPoints.clear();
170}
171
172
174{
175 return psRef.empty();
176}
177
178
179size_t ReferencingPolygon::size(void) const
180{
181 return psRef.size();
182}
183
184
186{
187 return _id;
188}
189
190
191const Point& ReferencingPolygon::at(size_t index) const
192{
193 COLA_ASSERT(index < size());
194
195 if (psRef[index].first != nullptr)
196 {
197 const Polygon& poly = *(psRef[index].first);
198 unsigned short poly_index = psRef[index].second;
199 COLA_ASSERT(poly_index < poly.size());
200
201 return poly.ps[poly_index];
202 }
203 else
204 {
205 return psPoints[index];
206 }
207}
208
209
211{
212 Box bBox;
213 bBox.min.x = DBL_MAX;
214 bBox.min.y = DBL_MAX;
215 bBox.max.x = -DBL_MAX;
216 bBox.max.y = -DBL_MAX;
217
218 for (size_t i = 0; i < size(); ++i)
219 {
220 bBox.min.x = std::min(bBox.min.x, at(i).x);
221 bBox.min.y = std::min(bBox.min.y, at(i).y);
222 bBox.max.x = std::max(bBox.max.x, at(i).x);
223 bBox.max.y = std::max(bBox.max.y, at(i).y);
224 }
225
226 // Add buffer space.
227 bBox.min.x -= offset;
228 bBox.min.y -= offset;
229 bBox.max.x += offset;
230 bBox.max.y += offset;
231
232 return bBox;
233}
234
235double Box::length(size_t dimension) const
236{
237 if (dimension == 0)
238 {
239 return (max.x - min.x);
240 }
241 return (max.y - min.y);
242}
243
244double Box::width(void) const
245{
246 return (max.x - min.x);
247}
248
249double Box::height(void) const
250{
251 return (max.y - min.y);
252}
253
256 _id(0)
257{
258 clear();
259}
260
261
262Polygon::Polygon(const int pn)
264 _id(0),
265 ps(pn)
266{
267}
268
269
272 _id(poly.id()),
273 ps(poly.size())
274{
275 for (size_t i = 0; i < poly.size(); ++i)
276 {
277 ps[i] = poly.at(i);
278 }
279}
280
281
283{
284 Box boundingBox = offsetBoundingBox(0.0);
285
286 return Rectangle(boundingBox.min, boundingBox.max);
287}
288
289static Point unitNormalForEdge(const Point &pt1, const Point &pt2)
290{
291 if (pt2 == pt1)
292 {
293 return Point(0, 0);
294 }
295 double dx = pt2.x - pt1.x;
296 double dy = pt2.y - pt1.y;
297 double f = 1.0 / std::sqrt((dx * dx) + (dy * dy));
298 dx *= f;
299 dy *= f;
300 return Point(dy, -dx);
301}
302
304{
305 Polygon newPoly;
306 newPoly._id = id();
307 if (offset == 0)
308 {
309 for (size_t i = 0; i < size(); ++i)
310 {
311 newPoly.ps.push_back(at(i));
312 }
313 return newPoly;
314 }
315
316 size_t numOfEdges = size();
317 std::vector<Vector> normals(numOfEdges);
318 for (size_t i = 0; i < numOfEdges; ++i)
319 {
320 normals[i] = unitNormalForEdge(at(i), at((i + 1) % numOfEdges));
321 }
322
323 size_t j = numOfEdges - 1;
324 for (size_t i = 0; i < numOfEdges; ++i)
325 {
326 double R = 1 + ((normals[i].x * normals[j].x) +
327 (normals[i].y * normals[j].y));
328 if (((normals[j].x * normals[i].y) - (normals[i].x * normals[j].y)) *
329 offset >= 0)
330 {
331 double q = offset / R;
332 Point pt = Point(at(i).x + (normals[j].x + normals[i].x) * q,
333 at(i).y + (normals[j].y + normals[i].y) * q);
334
335 pt.id = id();
336 pt.vn = newPoly.size();
337 newPoly.ps.push_back(pt);
338 }
339 else
340 {
341 Point pt1 = Point(at(i).x + normals[j].x * offset,
342 at(i).y + normals[j].y * offset);
343 Point pt2 = at(i);
344 Point pt3 = Point(at(i).x + normals[i].x * offset,
345 at(i).y + normals[i].y * offset);
346
347 pt1.id = id();
348 pt1.vn = newPoly.size();
349 newPoly.ps.push_back(pt1);
350
351 pt2.id = id();
352 pt2.vn = newPoly.size();
353 newPoly.ps.push_back(pt2);
354
355 pt3.id = id();
356 pt3.vn = newPoly.size();
357 newPoly.ps.push_back(pt3);
358 }
359 j = i;
360 }
361
362 return newPoly;
363}
364
366{
367 ps.clear();
368 ts.clear();
369}
370
371
372bool Polygon::empty(void) const
373{
374 return ps.empty();
375}
376
377
378size_t Polygon::size(void) const
379{
380 return ps.size();
381}
382
383
384int Polygon::id(void) const
385{
386 return _id;
387}
388
389
390const Point& Polygon::at(size_t index) const
391{
392 COLA_ASSERT(index < size());
393
394 return ps[index];
395}
396
397void Polygon::setPoint(size_t index, const Point& point)
398{
399 COLA_ASSERT(index < size());
400
401 ps[index] = point;
402}
403
404
405static const unsigned int SHORTEN_NONE = 0;
406static const unsigned int SHORTEN_START = 1;
407static const unsigned int SHORTEN_END = 2;
408static const unsigned int SHORTEN_BOTH = SHORTEN_START | SHORTEN_END;
409
410// shorten_line():
411// Given the two endpoints of a line segment, this function adjusts the
412// endpoints of the line to shorten the line by shorten_length at either
413// or both ends.
414//
415static void shorten_line(double& x1, double& y1, double& x2, double& y2,
416 const unsigned int mode, const double shorten_length)
417{
418 if (mode == SHORTEN_NONE)
419 {
420 return;
421 }
422
423 double rise = y1 - y2;
424 double run = x1 - x2;
425 double disty = fabs(rise);
426 double distx = fabs(run);
427
428 // Handle case where shorten length is greater than the length of the
429 // line segment.
430 if ((mode == SHORTEN_BOTH) &&
431 (((distx > disty) && ((shorten_length * 2) > distx)) ||
432 ((disty >= distx) && ((shorten_length * 2) > disty))))
433 {
434 x1 = x2 = x1 - (run / 2);
435 y1 = y2 = y1 - (rise / 2);
436 return;
437 }
438 else if ((mode == SHORTEN_START) &&
439 (((distx > disty) && (shorten_length > distx)) ||
440 ((disty >= distx) && (shorten_length > disty))))
441 {
442 x1 = x2;
443 y1 = y2;
444 return;
445 }
446 else if ((mode == SHORTEN_END) &&
447 (((distx > disty) && (shorten_length > distx)) ||
448 ((disty >= distx) && (shorten_length > disty))))
449 {
450 x2 = x1;
451 y2 = y1;
452 return;
453 }
454
455 // Handle orthogonal line segments.
456 if (x1 == x2)
457 {
458 // Vertical
459 int sign = (y1 < y2) ? 1: -1;
460
461 if (mode & SHORTEN_START)
462 {
463 y1 += (sign * shorten_length);
464 }
465 if (mode & SHORTEN_END)
466 {
467 y2 -= (sign * shorten_length);
468 }
469 return;
470 }
471 else if (y1 == y2)
472 {
473 // Horizontal
474 int sign = (x1 < x2) ? 1: -1;
475
476 if (mode & SHORTEN_START)
477 {
478 x1 += (sign * shorten_length);
479 }
480 if (mode & SHORTEN_END)
481 {
482 x2 -= (sign * shorten_length);
483 }
484 return;
485 }
486
487 int xpos = (x1 < x2) ? -1 : 1;
488 int ypos = (y1 < y2) ? -1 : 1;
489
490 double tangent = rise / run;
491
492 if (mode & SHORTEN_END)
493 {
494 if (disty > distx)
495 {
496 y2 += shorten_length * ypos;
497 x2 += shorten_length * ypos * (1 / tangent);
498 }
499 else if (disty < distx)
500 {
501 y2 += shorten_length * xpos * tangent;
502 x2 += shorten_length * xpos;
503 }
504 }
505
506 if (mode & SHORTEN_START)
507 {
508 if (disty > distx)
509 {
510 y1 -= shorten_length * ypos;
511 x1 -= shorten_length * ypos * (1 / tangent);
512 }
513 else if (disty < distx)
514 {
515 y1 -= shorten_length * xpos * tangent;
516 x1 -= shorten_length * xpos;
517 }
518 }
519}
520
521
522void Polygon::translate(const double xDist, const double yDist)
523{
524 for (size_t i = 0; i < size(); ++i)
525 {
526 ps[i].x += xDist;
527 ps[i].y += yDist;
528 }
529}
530
531
533{
534 // Copy the PolyLine.
535 Polygon simplified = *this;
536
537 std::vector<std::pair<size_t, Point> >& checkpoints =
538 simplified.checkpointsOnRoute;
539 bool hasCheckpointInfo = !(checkpoints.empty());
540
541 std::vector<Point>::iterator it = simplified.ps.begin();
542 if (it != simplified.ps.end()) ++it;
543
544 // Combine collinear line segments into single segments:
545 for (size_t j = 2; j < simplified.size(); )
546 {
547 if (vecDir(simplified.ps[j - 2], simplified.ps[j - 1],
548 simplified.ps[j]) == 0)
549 {
550 // These three points make up two collinear segments, so just
551 // combine them into a single segment.
552 it = simplified.ps.erase(it);
553
554 if (hasCheckpointInfo)
555 {
556 // 0 1 2 3 4 <- vertices on path
557 // +-----+-----+-----+-----+
558 // 0 1 2 3 4 5 6 7 8 <- checkpoints on points & edges
559 // |
560 // \_ deletedPointValue = 4
561 //
562 // If 1-2-3 is collinear then we want to end up with
563 //
564 // 0 1 2 3
565 // +-----+-----------+-----+
566 // 0 1 2 3 3 3 4 5 6
567 //
568 //
569 //
570 size_t deletedPointValue = (j - 1) - 1;
571 for (size_t i = 0; i < checkpoints.size(); ++i)
572 {
573 if (checkpoints[i].first == deletedPointValue)
574 {
575 checkpoints[i].first -= 1;
576 }
577 else if (checkpoints[i].first > deletedPointValue)
578 {
579 checkpoints[i].first -= 2;
580 }
581 }
582 }
583 }
584 else
585 {
586 ++j;
587 ++it;
588 }
589 }
590
591 return simplified;
592}
593
594std::vector<Point> Polygon::checkpointsOnSegment(size_t segmentLowerIndex,
595 int indexModifier) const
596{
597 std::vector<Point> checkpoints;
598 // 0 1 2 3 4 <- vertices on path
599 // +-----+-----+-----+-----+
600 // 0 1 2 3 4 5 6 7 8 <- checkpoints on points & edges
601
602 size_t checkpointLowerValue = 2 * segmentLowerIndex;
603 size_t checkpointUpperValue = checkpointLowerValue + 2;
604 size_t index = 0;
605
606 if (indexModifier > 0)
607 {
608 checkpointLowerValue++;
609 }
610 else if (indexModifier < 0)
611 {
612 checkpointUpperValue--;
613 }
614
615 while (index < checkpointsOnRoute.size())
616 {
617 if ((checkpointsOnRoute[index].first >= checkpointLowerValue) &&
618 (checkpointsOnRoute[index].first <= checkpointUpperValue))
619 {
620 checkpoints.push_back(checkpointsOnRoute[index].second);
621 }
622 ++index;
623 }
624 return checkpoints;
625}
626
627
628#define mid(a, b) ((a < b) ? a + ((b - a) / 2) : b + ((a - b) / 2))
629
630
631// curvedPolyline():
632// Returns a curved approximation of this multi-segment PolyLine, with
633// the corners replaced by smooth Bezier curves. curve_amount describes
634// how large to make the curves.
635// The ts value for each point in the returned Polygon describes the
636// drawing operation: 'M' (move) marks the first point, a line segment
637// is marked with an 'L' and three 'C's (along with the previous point)
638// describe the control points of a Bezier curve.
639//
640Polygon Polygon::curvedPolyline(const double curve_amount,
641 const bool closed) const
642{
643 Polygon simplified = this->simplify();
644
645 Polygon curved;
646 size_t num_of_points = size();
647 if (num_of_points <= 2)
648 {
649 // There is only a single segment, do nothing.
650 curved = *this;
651 curved.ts.push_back('M');
652 curved.ts.push_back('L');
653 return curved;
654 }
655
656 // Build the curved polyline:
657 curved._id = _id;
658 double last_x = 0;
659 double last_y = 0;
660 if (closed)
661 {
662 double x1 = simplified.ps[0].x;
663 double y1 = simplified.ps[0].y;
664 double x2 = simplified.ps[1].x;
665 double y2 = simplified.ps[1].y;
666 shorten_line(x1, y1, x2, y2, SHORTEN_START, curve_amount);
667 curved.ps.push_back(Point(x1, y1));
668 curved.ts.push_back('M');
669 }
670 else
671 {
672 curved.ps.push_back(ps[0]);
673 curved.ts.push_back('M');
674 }
675
676 size_t simpSize = simplified.size();
677 size_t finish = (closed) ? simpSize + 2 : simpSize;
678 for (size_t j = 1; j < finish; ++j)
679 {
680 double x1 = simplified.ps[(simpSize + j - 1) % simpSize].x;
681 double y1 = simplified.ps[(simpSize + j - 1) % simpSize].y;
682 double x2 = simplified.ps[j % simpSize].x;
683 double y2 = simplified.ps[j % simpSize].y;
684
685 double old_x = x1;
686 double old_y = y1;
687
688 unsigned int mode = SHORTEN_BOTH;
689 if (!closed)
690 {
691 if (j == 1)
692 {
694 }
695 else if (j == (size() - 1))
696 {
698 }
699 }
700 shorten_line(x1, y1, x2, y2, mode, curve_amount);
701
702 if (j > 1)
703 {
704 curved.ts.insert(curved.ts.end(), 3, 'C');
705 curved.ps.push_back(Point(mid(last_x, old_x), mid(last_y, old_y)));
706 curved.ps.push_back(Point(mid(x1, old_x), mid(y1, old_y)));
707 curved.ps.push_back(Point(x1, y1));
708 }
709 if (closed && (j == (finish - 1)))
710 {
711 // Close the path.
712 curved.ts.push_back('Z');
713 curved.ps.push_back(Point(x1, y1));
714 break;
715 }
716 curved.ts.push_back('L');
717 curved.ps.push_back(Point(x2, y2));
718
719 last_x = x2;
720 last_y = y2;
721 }
722
723 return curved;
724}
725
726
727Rectangle::Rectangle(const Point& topLeft, const Point& bottomRight)
728 : Polygon(4)
729{
730 double xMin = std::min(topLeft.x, bottomRight.x);
731 double xMax = std::max(topLeft.x, bottomRight.x);
732 double yMin = std::min(topLeft.y, bottomRight.y);
733 double yMax = std::max(topLeft.y, bottomRight.y);
734
735 ps[0] = Point(xMax, yMin);
736 ps[1] = Point(xMax, yMax);
737 ps[2] = Point(xMin, yMax);
738 ps[3] = Point(xMin, yMin);
739}
740
741
742Rectangle::Rectangle(const Point& centre, const double width,
743 const double height)
744 : Polygon(4)
745{
746 double halfWidth = width / 2.0;
747 double halfHeight = height / 2.0;
748 double xMin = centre.x - halfWidth;
749 double xMax = centre.x + halfWidth;
750 double yMin = centre.y - halfHeight;
751 double yMax = centre.y + halfHeight;
752
753 ps[0] = Point(xMax, yMin);
754 ps[1] = Point(xMax, yMax);
755 ps[2] = Point(xMin, yMax);
756 ps[3] = Point(xMin, yMin);
757}
758
759
760}
761
A bounding box, represented by the top-left and bottom-right corners.
Definition geomtypes.h:134
Point min
The top-left point.
Definition geomtypes.h:137
Point max
The bottom-right point.
Definition geomtypes.h:139
double height(void) const
double length(size_t dimension) const
double width(void) const
The Point class defines a point in the plane.
Definition geomtypes.h:53
unsigned short vn
The vertex number associated with this point.
Definition geomtypes.h:113
double y
The y position.
Definition geomtypes.h:109
double x
The x position.
Definition geomtypes.h:107
Point()
Default constructor.
Definition geomtypes.cpp:41
unsigned int id
The ID associated with this point.
Definition geomtypes.h:111
A common interface used by the Polygon classes.
Definition geomtypes.h:151
virtual const Point & at(size_t index) const =0
Returns a specific point in the polygon.
Polygon boundingRectPolygon(void) const
Returns the bounding rectangle for this polygon.
virtual size_t size(void) const =0
Returns the number of points in this polygon.
Polygon offsetPolygon(double offset) const
virtual int id(void) const =0
Returns the ID value associated with this polygon.
Box offsetBoundingBox(double offset) const
Returns the bounding rectangle that contains this polygon with optionally some buffer space around it...
A dynamic Polygon, to which points can be easily added and removed.
Definition geomtypes.h:208
void setPoint(size_t index, const Point &point)
Sets a position for a particular point in the polygon.
Polygon simplify(void) const
Returns a simplified Polyline, where all collinear line segments have been collapsed down into single...
size_t size(void) const
Returns the number of points in this polygon.
int id(void) const
Returns the ID value associated with this polygon.
Polygon curvedPolyline(const double curve_amount, const bool closed=false) const
Returns a curved approximation of this multi-segment PolyLine, with the corners replaced by smooth Be...
std::vector< char > ts
If used, denotes whether the corresponding point in ps is a move-to operation or a Bezier curve-to.
Definition geomtypes.h:301
bool empty(void) const
Returns true if this polygon is empty.
void translate(const double xDist, const double yDist)
Translates the polygon position by a relative amount.
int _id
An ID for the polygon.
Definition geomtypes.h:283
std::vector< Point > ps
A vector of the points that make up the Polygon.
Definition geomtypes.h:285
void clear(void)
Resets this to the empty polygon.
const Point & at(size_t index) const
Returns a specific point in the polygon.
std::vector< std::pair< size_t, Point > > checkpointsOnRoute
Definition geomtypes.h:314
Polygon()
Constructs an empty polygon (with zero points).
std::vector< Point > checkpointsOnSegment(size_t segmentLowerIndex, int indexModifier=0) const
A Rectangle, a simpler way to define the polygon for square or rectangular shapes.
Definition geomtypes.h:357
Rectangle(const Point &topLeft, const Point &bottomRight)
Constructs a rectangular polygon given two opposing corner points.
const Point & at(size_t index) const
Returns a specific point in the polygon.
size_t size(void) const
Returns the number of points in this polygon.
std::vector< std::pair< const Polygon *, unsigned short > > psRef
Definition geomtypes.h:348
std::vector< Point > psPoints
Definition geomtypes.h:349
int id(void) const
Returns the ID value associated with this polygon.
void clear(void)
Resets this to the empty polygon.
bool empty(void) const
Returns true if this polygon is empty.
The Router class represents a libavoid router instance.
Definition router.h:386
ObstacleList m_obstacles
Definition router.h:401
Contains the interface for various geometry types and classes.
double offset
libavoid: Object-avoiding orthogonal and polyline connector routing library.
static const unsigned int SHORTEN_END
static const unsigned short kUnassignedVertexNumber
Constant value representing an unassigned vertex number.
Definition geomtypes.h:120
static void shorten_line(double &x1, double &y1, double &x2, double &y2, const unsigned int mode, const double shorten_length)
static int vecDir(const Point &a, const Point &b, const Point &c, const double maybeZero=0.0)
Definition geometry.h:79
static const unsigned int SHORTEN_START
static const unsigned int SHORTEN_NONE
static const unsigned int SHORTEN_BOTH
static Point unitNormalForEdge(const Point &pt1, const Point &pt2)
int mode
int size
Contains the interface for the Router class.
static double sign(double const x)
Returns -1 or 1 according to the sign of x.
Contains the interface for the ShapeRef class.
int index
double height
double width