3
This file is part of the extensible drawing editor Ipe.
4
Copyright (C) 1993-2005 Otfried Cheong
6
Ipe is free software; you can redistribute it and/or modify it
7
under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2 of the License, or
9
(at your option) any later version.
11
As a special exception, you have permission to link Ipe with the
12
CGAL library and distribute executables, as long as you follow the
13
requirements of the Gnu General Public License in regard to all of
14
the software in the executable aside from CGAL.
16
Ipe is distributed in the hope that it will be useful, but WITHOUT
17
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
19
License for more details.
21
You should have received a copy of the GNU General Public License
22
along with Ipe; if not, you can find it at
23
"http://www.gnu.org/copyleft/gpl.html", or write to the Free
24
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28
visibility-polygon.cpp: ipelet for finding the visibility polygon of
29
a point inside a polygon
31
Author: Chris Gray <cgray@win.tue.nl>
32
License: Same as ipe (GPL with CGAL exception)
33
Date: Thu Aug 11 14:51:03 CEST 2005
35
Commentary: This ipelet computes the visibility polygon of a point
36
inside a polygon. The algorithm is adapted from the book _Art
37
Gallery Theorems and Algorithms_ by Joseph O'Rourke (which was in
38
turn based on the algorithm by Lee from 1985).
45
#include "ipevisitor.h"
51
static const char *aboutText =
52
"Written by Chris Gray (www.win.tue.nl/~cgray/ipelets.html)";
54
// --------------------------------------------------------------------
56
int TurnType (IpeVector *a, IpeVector *b, IpeVector *c);
57
class VisibilityStack;
59
class VisibilityPolygonIpelet : public Ipelet {
61
virtual int IpelibVersion() const { return IPELIB_VERSION; }
62
virtual const char *Label() const { return "Visibility Polygon"; }
63
virtual const char *About() const { return aboutText; }
64
virtual void Run(int, IpePage *page, IpeletHelper *helper);
66
virtual VisibilityStack *FindVisibilityPolygon(IpeVector **poly,
69
virtual IpeVector **RenumberPoly(IpeVector **poly, IpeVector *point,
74
class VisibilityStack {
81
list<IpeVector *> myStack;
82
list<double> alphaStack;
83
IpeVector *top() { return myStack.front(); }
84
double alphaTop() { return alphaStack.front(); }
85
void push (IpeVector *pt, double alpha)
86
{ myStack.push_front(pt); alphaStack.push_front(alpha); stackSize++; }
88
{ IpeVector *pt = top(); myStack.pop_front(); delete pt; stackSize--;
89
alphaStack.pop_front(); }
90
int stopPop (int i, IpeVector *prevPoint, double prevAlpha,
94
void SetPoly (IpeVector **p, int num) { poly = p; npoints = num; }
95
void SetPoint (IpeVector *p) { point = p; }
97
IpeVector **ToPolygon ();
98
VisibilityStack() { poly = NULL; point = NULL; stackSize = 0; npoints = 0; }
99
int Length() { return stackSize; }
103
VisibilityStack::~VisibilityStack ()
106
for (i = 0; i < npoints; i++) {
110
delete [] polyAlphas;
113
// Returns 0, 1, or 2.
114
// 0 means "don't stop"
115
// 1 means "stop because case a happened"
116
// 2 means "stop because case b happened"
117
// If 1 or 2 is returned, outPoint is filled with the appropriate point.
118
int VisibilityStack::stopPop(int i, IpeVector *prevPoint,
119
double prevAlpha, IpeVector &outPoint)
121
double currentAlpha = alphaTop();
122
IpeVector *currentPoint = top();
124
IpeSegment seg(*(point), *currentPoint);
125
IpeSegment seg2(*(poly[i]), *(poly[(i + 1) % npoints]));
126
IpeSegment seg3(*(poly[(i + 1) % npoints]), *(poly[(i + 2) % npoints]));
127
IpeSegment seg4(*currentPoint, *prevPoint);
128
if ((fabs(currentAlpha - prevAlpha) <= 1e-10
129
|| fabs(fabs(currentAlpha - prevAlpha) - 2 * M_PI) <= 1e-10)
130
&& (seg4.Intersects(seg3, outPoint)
131
|| seg4.Intersects(seg2, outPoint))) {
132
IpeVector v1 = outPoint - *point;
133
IpeVector v2 = *currentPoint - *point;
134
if (v2.SqLen() < v1.SqLen()) {
138
if (prevAlpha >= polyAlphas[(i + 1) % npoints]
139
&& polyAlphas[(i + 1) % npoints] > currentAlpha) {
140
IpeSegment seg(*(point), *(poly[(i + 1) % npoints]));
141
IpeSegment seg2(*prevPoint, *currentPoint);
142
seg2.Intersects(seg.Line(), outPoint);
149
void VisibilityStack::Run ()
152
IpeSegment *W = new IpeSegment();
156
if (npoints == 0) return;
158
polyAlphas = new double[npoints];
160
// fill in the polyAlphas array
162
for (i = 1; i < npoints; i++) {
163
IpeVector v1 = *(poly[i]) - *point;
164
IpeVector v2 = *(poly[i - 1]) - *point;
165
polyAlphas[i] = polyAlphas[i - 1]
166
+ TurnType(point, poly[i - 1], poly[i])
167
* acos((v1.iX * v2.iX + v1.iY * v2.iY) / (v1.Len() * v2.Len()));
170
push(new IpeVector(*poly[0]), 0.0);
171
enum state { PUSH, POP, WAIT } state = PUSH;
172
for (i = 0; i < npoints;) {
173
int iplus1modn = (i + 1) % npoints;
176
if (polyAlphas[iplus1modn] < 2 * M_PI
177
|| (fabs(polyAlphas[iplus1modn] - 2 * M_PI) <= 1e-10)) {
178
push(new IpeVector(*poly[iplus1modn]), polyAlphas[iplus1modn]);
180
iplus1modn = (i + 1) % npoints;
181
if (i == npoints) goto out;
182
if (polyAlphas[iplus1modn] >= polyAlphas[i] || i == npoints - 1) {
185
if (TurnType(poly[i - 1], poly[i], poly[iplus1modn]) >= 0) {
188
// Set W to s_t \infty in the direction from x to s_t
191
IpeVector v = W->iP - *point;
197
// push the intersection of the ray x v_0 with v_i v_{i + 1}
198
// onto the stack and then call wait with W = v_0 s_t
200
// calculate the intersection between the horizontal line from
201
// v_0 and v_i v_{i + 1}
202
double pointY = point->iY;
203
double edgeY1 = poly[i]->iY;
204
double edgeY2 = poly[iplus1modn]->iY;
205
if ((pointY < edgeY1 && pointY > edgeY2)
206
|| (pointY > edgeY1 && pointY < edgeY2)) {
207
double a = (pointY - edgeY2) / (edgeY1 - edgeY2);
208
double edgeX1 = poly[i]->iX;
209
double edgeX2 = poly[iplus1modn]->iX;
210
double xprime = (a * edgeX1) + ((1 - a) * edgeX2);
211
// push the intersection with alpha 2\pi
212
push(new IpeVector(xprime, pointY), 2 * M_PI);
213
// set W to be v_0 s_t
217
// something bad happened.
223
// pop until one of two things happens:
224
// a) prevAlpha >= polyAlphas[i + 1] > alphaTop
225
// b) prevAlpha = alphaTop >= polyAlphas[i + 1] and the
226
// intersection of the ray from point through prevAlpha to the
227
// next segment happens between prevAlpha and alphaTop
229
IpeVector *prevPoint;
231
prevAlpha = alphaTop();
234
} while (!(stopReason = stopPop(i, prevPoint, prevAlpha, y)));
236
// what you do depends on whether a or b happened
237
if (stopReason == 1) {
238
IpeVector v = y - *point;
239
push(new IpeVector(y), v.Angle().Normalize(0.0));
244
if (polyAlphas[i] > polyAlphas[(i + 1) % npoints]) {
247
if (TurnType(poly[i - 1], poly[i], poly[(i + 1) % npoints]) < 0) {
265
// i is incremented until v_i v_{i + 1} intersects W at point y
266
// from the correct direction. When that occurs, y is pushed on
267
// the stack, and either push or pop is called depending on
268
// whether polyAlphas[i + 1] >= polyAlphas[i] or vice versa,
272
IpeSegment currentSeg;
275
currentSeg.iP = *(poly[i]);
276
currentSeg.iQ = *(poly[(i + 1) % npoints]);
277
} while (!(currentSeg.Intersects(*W, pt) &&
278
TurnType(¤tSeg.iP, &(W->iQ), ¤tSeg.iQ) > 0));
280
// push pt on the stack, after computing alpha
282
IpeVector diff = pt - *point;
283
push (new IpeVector(pt), diff.Angle().Normalize(0.0));
285
// now figure out what to set the state to.
286
if (polyAlphas[i] < polyAlphas[(i + 1) % npoints]) {
299
IpeVector **VisibilityStack::ToPolygon()
301
IpeVector **output = new IpeVector*[stackSize];
305
output[i] = new IpeVector(*top());
312
IpeVector **VisibilityPolygonIpelet::RenumberPoly(IpeVector **poly,
317
int nearestPointBetween = -1;
319
double nearest = 1e42; /* FIXME: bounding circle */
320
IpeVector *nearestPoint = new IpeVector();
321
IpeVector **output = new IpeVector*[npoints + 2];
323
IpeVector farPoint(1e42, point->iY); /* FIXME: bounding circle */
324
IpeSegment horizSegment(*point, farPoint);
326
nearestPoint->iY = point->iY;
328
for (i = 0; i < npoints; i++) {
329
int j = (i + 1) % npoints;
331
IpeSegment seg(*(poly[i]), *(poly[j]));
333
if (horizSegment.Intersects(seg, pt)) {
334
if (pt.iX > point->iX && pt.iX < nearest) {
336
nearestPointBetween = j;
337
nearestPoint->iX = pt.iX;
342
output[0] = nearestPoint;
343
if (poly[(nearestPointBetween - 1 + npoints) % npoints]->iY
344
< poly[nearestPointBetween]->iY) {
348
nearestPointBetween = (nearestPointBetween - 1 + npoints) % npoints;
350
for (i = 0; i < npoints; i++) {
351
output[i + 1] = new IpeVector(*(poly[(reverse * i + nearestPointBetween
352
+ npoints) % npoints]));
354
output[npoints + 1] = new IpeVector(*nearestPoint);
359
int TurnType (IpeVector *a, IpeVector *b, IpeVector *c)
361
double area2 = (b->iX - a->iX) *
362
(c->iY - a->iY) - (c->iX - a->iX) * (b->iY - a->iY);
366
} else if (area2 < 0) {
373
VisibilityStack *VisibilityPolygonIpelet::FindVisibilityPolygon
374
(IpeVector **poly, IpeVector *point, int npoints)
377
IpeVector **renumbered = RenumberPoly (poly, point, npoints);
379
VisibilityStack *st = new VisibilityStack;
381
for (i = 0; i < npoints - 1; i++) {
386
st->SetPoly (renumbered, npoints + 1);
387
st->SetPoint (point);
393
void VisibilityPolygonIpelet::Run(int, IpePage *page, IpeletHelper *helper)
395
IpePage::iterator it;
400
for (it = page->begin(); it != page->end(); it++) {
401
if (it->Select() && it->Object() && it->Object()->AsMark()) {
403
} else if (it->Select() && it->Object() && it->Object()->AsPath()) {
404
IpePath *path = it->Object()->AsPath();
405
for (j = 0; j < path->NumSubPaths(); j++) {
406
if (path->SubPath(j)->Type() == IpeSubPath::ESegments) {
407
const IpeSubPath *sp = it->Object()->AsPath()->SubPath(j);
409
length = sp->AsSegs()->NumSegments() + 1;
415
if (length < 2 || have_point == 0) {
416
helper->Message("Too little selected");
419
IpeVector **points = new IpeVector*[length];
420
IpeVector *point = NULL;
422
for (it = page->begin(); it != page->end(); it++) {
423
if (it->Select() && it->Object()) {
424
IpeMatrix m = it->Object()->Matrix();
425
if (it->Object()->AsMark()) {
426
point = new IpeVector(m * it->Object()->AsMark()->Position());
427
} else if (it->Object()->AsPath()) {
428
IpePath *path = it->Object()->AsPath();
429
for (j = 0; j < path->NumSubPaths(); j++) {
430
if (path->SubPath(j)->Type() == IpeSubPath::ESegments) {
431
const IpeSegmentSubPath *sp = path->SubPath(j)->AsSegs();
432
for (k = 0; k < sp->NumSegments(); k++) {
433
points[i] = new IpeVector(m * sp->Segment(k).CP(0));
436
points[i] = new IpeVector(m * sp->Segment(k - 1).CP(1));
444
VisibilityStack *output = FindVisibilityPolygon (points, point, length);
447
length = output->Length();
449
IpePath *path = new IpePath(helper->Attributes());
450
IpeSegmentSubPath *sp = new IpeSegmentSubPath;
452
IpeVector **newpoints = output->ToPolygon();
454
for (i = 1; i < length; i++) {
455
sp->AppendSegment (*(newpoints[i - 1]), *(newpoints[i]));
458
path->AddSubPath(sp);
459
page->push_back(IpePgObject(IpePgObject::EPrimary, helper->CurrentLayer(),
462
for (i = 0; i < length; i++) {
470
IPELET_DECLARE Ipelet *NewIpelet()
472
return new VisibilityPolygonIpelet;