2
* libpal - Automated Placement of Labels Library
4
* Copyright (C) 2008 Maxence Laurent, MIS-TIC, HEIG-VD
5
* University of Applied Sciences, Western Switzerland
9
* maxence.laurent <at> heig-vd <dot> ch
11
* eric.taillard <at> heig-vd <dot> ch
13
* This file is part of libpal.
15
* libpal is free software: you can redistribute it and/or modify
16
* it under the terms of the GNU General Public License as published by
17
* the Free Software Foundation, either version 3 of the License, or
18
* (at your option) any later version.
20
* libpal is distributed in the hope that it will be useful,
21
* but WITHOUT ANY WARRANTY; without even the implied warranty of
22
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
* GNU General Public License for more details.
25
* You should have received a copy of the GNU General Public License
26
* along with libpal. If not, see <http://www.gnu.org/licenses/>.
33
#if defined(_VERBOSE_) || (_DEBUG_) || (_DEBUG_FULL_)
47
#include "geomfunction.h"
55
nbPoints = cHullSize = 0;
62
PointSet::PointSet( int nbPoints, double *x, double *y )
64
this->nbPoints = nbPoints;
65
this->x = new double[nbPoints];
66
this->y = new double[nbPoints];
69
for ( i = 0; i < nbPoints; i++ )
78
PointSet::PointSet( double x, double y )
80
nbPoints = cHullSize = 1;
81
this->x = new double[1];
82
this->y = new double[1];
93
PointSet::PointSet( PointSet &ps )
97
nbPoints = ps.nbPoints;
98
x = new double[nbPoints];
99
y = new double[nbPoints];
102
for ( i = 0; i < nbPoints; i++ )
110
cHullSize = ps.cHullSize;
111
for ( i = 0; i < cHullSize; i++ )
113
cHull[i] = ps.cHull[i];
127
PointSet::~PointSet()
135
void PointSet::deleteCoords()
148
PointSet* PointSet::extractShape( int nbPtSh, int imin, int imax, int fps, int fpe, double fptx, double fpty )
153
PointSet *newShape = new PointSet();
155
newShape->type = GEOS_POLYGON;
157
newShape->nbPoints = nbPtSh;
159
newShape->x = new double[newShape->nbPoints];
160
newShape->y = new double[newShape->nbPoints];
162
std::cout << "New shape: ";
164
// new shape # 1 from imin to imax
165
for ( j = 0, i = imin; i != ( imax + 1 ) % nbPoints; i = ( i + 1 ) % nbPoints, j++ )
167
newShape->x[j] = x[i];
168
newShape->y[j] = y[i];
170
std::cout << x[i] << ";" << y[i] << std::endl;
173
// is the cutting point a new one ?
177
newShape->x[j] = fptx;
178
newShape->y[j] = fpty;
180
std::cout << fptx << ";" << fpty << std::endl;
185
std::cout << "J = " << j << "/" << newShape->nbPoints << std::endl;
186
std::cout << std::endl;
187
std::cout << "This: " << this << std::endl;
194
void PointSet::splitPolygons( LinkedList<PointSet*> *shapes_toProcess,
195
LinkedList<PointSet*> *shapes_final,
196
double xrm, double yrm , char *uid )
199
std::cout << "splitPolygons: " << uid << std::endl;
228
int holeS = -1; // hole start and end points
234
double labelArea = xrm * yrm;
238
while ( shapes_toProcess->size() > 0 )
241
std::cout << "Shape popping()" << std::endl;
243
shape = shapes_toProcess->pop_front();
247
nbp = shape->nbPoints;
251
std::cout << "nbp: " << nbp << std::endl;
252
std::cout << " PtSet: ";
255
for ( i = 0; i < nbp; i++ )
259
std::cout << x[i] << ";" << y[i] << std::endl;
264
std::cout << std::endl;
267
// conpute convex hull
268
shape->cHullSize = convexHullId( pts, x, y, nbp, shape->cHull );
270
cHull = shape->cHull;
271
cHullSize = shape->cHullSize;
275
std::cout << " CHull: ";
276
for ( i = 0; i < cHullSize; i++ )
278
std::cout << cHull[i] << " ";
280
std::cout << std::endl;
287
for ( ihs = 0; ihs < cHullSize; ihs++ )
289
// ihs->ihn => cHull'seg
290
ihn = ( ihs + 1 ) % cHullSize;
293
ipn = ( ips + 1 ) % nbp;
294
if ( ipn != cHull[ihn] ) // next point on shape is not the next point on cHull => there is a hole here !
298
// lookup for the deepest point in the hole
299
for ( i = ips; i != cHull[ihn]; i = ( i + 1 ) % nbp )
301
cp = vabs( cross_product( x[cHull[ihs]], y[cHull[ihs]],
302
x[cHull[ihn]], y[cHull[ihn]],
304
if ( cp - bestcp > EPSILON )
312
std::cout << "Deeper POint: " << pt << " between " << ips << " and " << cHull[ihn] << std::endl;
316
// compute the ihs->ihn->pt triangle's area
317
base = dist_euc2d( x[cHull[ihs]], y[cHull[ihs]],
318
x[cHull[ihn]], y[cHull[ihn]] );
320
b = dist_euc2d( x[cHull[ihs]], y[cHull[ihs]],
323
c = dist_euc2d( x[cHull[ihn]], y[cHull[ihn]],
326
s = ( base + b + c ) / 2; // s = half perimeter
327
area = s * ( s - base ) * ( s - b ) * ( s - c );
331
// retain the biggest area
332
if ( area - bestArea > EPSILON )
343
// we have a hole, its area, and the deppest point in hole
344
// we're going to find the second point to cup the shape
345
// holeS = hole starting point
346
// holeE = hole ending point
347
// retainedPt = deppest point in hole
348
// bestArea = area of triangle HoleS->holeE->retainedPoint
349
bestArea = sqrt( bestArea );
350
double cx, cy, dx, dy, ex, ey, fx, fy, seg_length, ptx = 0, pty = 0, fptx = 0, fpty = 0;
351
int ps = -1, pe = -1, fps = -1, fpe = -1;
352
if ( retainedPt >= 0 && bestArea > labelArea ) // there is a hole so we'll cut the shape in two new shape (only if hole area is bigger than twice labelArea)
355
std::cout << "Trou: " << retainedPt << std::endl;
356
std::cout << "Hole: " << cHull[holeS] << " -> " << cHull[holeE] << std::endl;
357
std::cout << "iterate from " << ( cHull[holeE] + 1 ) % nbp << " to " << ( cHull[holeS] - 1 + nbp ) % nbp << std::endl;
362
// iterate on all shape points except points which are in the hole
365
for ( i = ( cHull[holeE] + 1 ) % nbp; i != ( cHull[holeS] - 1 + nbp ) % nbp; i = j )
367
j = ( i + 1 ) % nbp; // i->j is shape segment not in hole
369
// compute distance between retainedPoint and segment
370
// whether perpendicular distance (if retaindPoint is fronting segment i->j)
371
// or distance between retainedPt and i or j (choose the nearest)
372
seg_length = dist_euc2d( x[i], y[i], x[j], y[j] );
373
cx = ( x[i] + x[j] ) / 2.0;
374
cy = ( y[i] + y[j] ) / 2.0;
383
std::cout << "D: " << dx << " " << dy << std::endl;
384
std::cout << "seg_length: " << seg_length << std::endl;
386
if ( seg_length < EPSILON || vabs(( b = cross_product( ex, ey, fx, fy, x[retainedPt], y[retainedPt] ) / ( seg_length ) ) ) > ( seg_length / 2 ) ) // retainedPt is not fronting i->j
388
if (( ex = dist_euc2d_sq( x[i], y[i], x[retainedPt], y[retainedPt] ) ) < ( ey = dist_euc2d_sq( x[j], y[j], x[retainedPt], y[retainedPt] ) ) )
401
else // point fronting i->j => compute pependicular distance => create a new point
403
b = cross_product( x[i], y[i], x[j], y[j], x[retainedPt], y[retainedPt] ) / seg_length;
408
if ( !computeLineIntersection( x[i], y[i], x[j], y[j], x[retainedPt], y[retainedPt], x[retainedPt] - dx, y[retainedPt] + dy, &ptx, &pty ) )
410
std::cout << "Oups ... il devrait par tomber la..." << std::endl;
413
std::cout << "intersection : " << x[i] << " " << y[i] << " " << x[j] << " " << y[j] << " " << x[retainedPt] << " " << y[retainedPt] << " " << x[retainedPt] - dx << " " << y[retainedPt] + dy << std::endl;
414
std::cout << " => " << ptx << ";" << pty << std::endl;
415
std::cout << " cxy> " << cx << ";" << cy << std::endl;
416
std::cout << " dxy> " << dx << ";" << dy << std::endl;
421
double pointX, pointY;
433
for ( k = cHull[holeS]; k != cHull[holeE]; k = ( k + 1 ) % nbp )
436
//std::cout << "test " << k << " " << l << std::endl;
437
if ( isSegIntersects( x[retainedPt], y[retainedPt], pointX, pointY, x[k], y[k], x[l], y[l] ) )
440
//std::cout << "Invalid point" << pe << ps << std::endl;
446
if ( isValid && b < c )
448
//std::cout << "new point: " << ps << " " << pe << std::endl;
455
} // for point which are not in hole
458
std::cout << " cut from " << retainedPt << " to ";
460
std::cout << "point " << fps << std::endl;
463
std::cout << "new point (" << fptx << ";" << fpty << " between " << fps << " and " << fpe << std::endl;
467
// we will cut the shapeu in two new shapes, one from [retainedPoint] to [newPoint] and one form [newPoint] to [retainedPoint]
468
int imin = retainedPt;
469
int imax = ((( fps < retainedPt && fpe < retainedPt ) || ( fps > retainedPt && fpe > retainedPt ) ) ? min( fps, fpe ) : max( fps, fpe ) );
471
int nbPtSh1, nbPtSh2; // how many points in new shapes ?
473
nbPtSh1 = imax - imin + 1 + ( fpe != fps );
475
nbPtSh1 = imax + nbp - imin + 1 + ( fpe != fps );
477
if (( imax == fps ? fpe : fps ) < imin )
478
nbPtSh2 = imin - ( imax == fps ? fpe : fps ) + 1 + ( fpe != fps );
480
nbPtSh2 = imin + nbp - ( imax == fps ? fpe : fps ) + 1 + ( fpe != fps );
484
std::cout << "imin: " << imin << " imax:" << imax << std::endl;
486
if ( retainedPt == -1 || fps == -1 || fpe == -1 )
489
std::cout << std::endl << "Failed to split feature !!! (uid=" << uid << ")" << std::endl;
494
// check for useless spliting
495
else if ( imax == imin || nbPtSh1 <= 2 || nbPtSh2 <= 2 || nbPtSh1 == nbp || nbPtSh2 == nbp )
497
shapes_final->push_back( shape );
502
PointSet *newShape = shape->extractShape( nbPtSh1, imin, imax, fps, fpe, fptx, fpty );
505
newShape->parent = shape->parent;
507
newShape->parent = shape;
511
std::cout << "push back:" << std::endl;
512
for ( i = 0; i < newShape->nbPoints; i++ )
514
std::cout << newShape->x[i] << ";" << newShape->y[i] << std::endl;
518
shapes_toProcess->push_back( newShape );
525
newShape = shape->extractShape( nbPtSh2, imax, imin, fps, fpe, fptx, fpty );
528
newShape->parent = shape->parent;
530
newShape->parent = shape;
533
std::cout << "push back:" << std::endl;
534
for ( i = 0; i < newShape->nbPoints; i++ )
536
std::cout << newShape->x[i] << ";" << newShape->y[i] << std::endl;
539
shapes_toProcess->push_back( newShape );
548
std::cout << "Put shape into shapes_final" << std::endl;
550
shapes_final->push_back( shape );
557
PointSet* PointSet::createProblemSpecificPointSet( double bbmin[2], double bbmax[2], bool *inside )
560
std::cout << "CreateProblemSpecific:" << std::endl;
562
PointSet *shape = new PointSet();
563
shape->x = new double[nbPoints];
564
shape->y = new double[nbPoints];
565
shape->nbPoints = nbPoints;
575
for ( int i = 0; i < nbPoints; i++ )
577
shape->x[i] = this->x[i];
578
shape->y[i] = this->y[i];
580
// check whether it's not outside
581
if ( x[i] < bbmin[0] || x[i] > bbmax[0] || y[i] < bbmin[1] || y[i] > bbmax[1] )
585
shape->holeOf = NULL;
586
shape->parent = NULL;
594
CHullBox * PointSet::compute_chull_bbox()
599
double bbox[4]; // xmin, ymin, xmax, ymax
609
double bb[16]; // {ax, ay, bx, by, cx, cy, dx, dy, ex, ey, fx, fy, gx, gy, hx, hy}}
613
double distNearestPoint;
620
double best_area = DBL_MAX;
621
double best_alpha = -1;
623
double best_length = 0;
624
double best_width = 0;
633
std::cout << "Compute_chull_bbox" << std::endl;
636
for ( i = 0; i < cHullSize; i++ )
639
std::cout << x[cHull[i]] << ";" << y[cHull[i]] << std::endl;
641
if ( x[cHull[i]] < bbox[0] )
642
bbox[0] = x[cHull[i]];
644
if ( x[cHull[i]] > bbox[2] )
645
bbox[2] = x[cHull[i]];
647
if ( y[cHull[i]] < bbox[1] )
648
bbox[1] = y[cHull[i]];
650
if ( y[cHull[i]] > bbox[3] )
651
bbox[3] = y[cHull[i]];
655
dref = bbox[2] - bbox[0];
657
for ( alpha_d = 0; alpha_d < 90; alpha_d++ )
659
alpha = alpha_d * M_PI / 180.0;
660
d1 = cos( alpha ) * dref;
661
d2 = sin( alpha ) * dref;
664
bb[1] = bbox[3]; // ax, ay
667
bb[5] = bbox[1]; // cx, cy
670
bb[9] = bbox[1]; // ex, ey
673
bb[13] = bbox[3]; // gx, gy
677
bb[3] = bb[1] + d2; // bx, by
679
bb[7] = bb[5] + d1; // dx, dy
681
bb[11] = bb[9] - d2; // fx, fy
682
bb[14] = bb[12] + d2;
683
bb[15] = bb[13] - d1; // hx, hy
686
for ( i = 0; i < 16; i += 4 )
689
alpha_seg = (( i / 4 > 0 ? ( i / 4 ) - 1 : 3 ) ) * M_PI / 2 + alpha;
693
for ( j = 0; j < nbPoints; j++ )
695
cp = cross_product( bb[i+2], bb[i+3], bb[i], bb[i+1], x[cHull[j]], y[cHull[j]] );
699
nearestPoint = cHull[j];
703
distNearestPoint = best_cp / dref;
705
d1 = cos( alpha_seg ) * distNearestPoint;
706
d2 = sin( alpha_seg ) * distNearestPoint;
714
// compute and compare AREA
715
width = cross_product( bb[6], bb[7], bb[4], bb[5], bb[12], bb[13] ) / dref;
716
length = cross_product( bb[2], bb[3], bb[0], bb[1], bb[8], bb[9] ) / dref;
718
area = width * length;
724
if ( best_area - area > EPSILON )
727
best_length = length;
730
memcpy( best_bb, bb, sizeof( double ) *16 );
734
// best bbox is defined
736
CHullBox * finalBb = new CHullBox();
738
for ( i = 0; i < 16; i = i + 4 )
740
computeLineIntersection( best_bb[i], best_bb[i+1], best_bb[i+2], best_bb[i+3],
741
best_bb[( i+4 ) %16], best_bb[( i+5 ) %16], best_bb[( i+6 ) %16], best_bb[( i+7 ) %16],
742
&finalBb->x[int ( i/4 )], &finalBb->y[int ( i/4 )] );
745
finalBb->alpha = best_alpha;
746
finalBb->width = best_width;
747
finalBb->length = best_length;
750
std::cout << "FINAL" << std::endl;
751
std::cout << "Length : " << best_length << std::endl;
752
std::cout << "Width : " << best_width << std::endl;
753
std::cout << "Alpha: " << best_alpha << " " << best_alpha*180 / M_PI << std::endl;
754
for ( i = 0; i < 4; i++ )
756
std::cout << finalBb->x[0] << " " << finalBb->y[0] << " ";
758
std::cout << std::endl;
765
double PointSet::getDistInside( double px, double py )
773
if ( !isPointInPolygon( nbPoints, x, y, px, py ) )
775
double d = getDist( px, py );
776
//std::cout << "Outside : " << d << std::endl;
779
d = -( d * d * d * d );
790
double width = ( xmax - xmin ) * 2;
791
double height = ( ymax - ymin ) * 2;
801
// Compute references points
802
for ( i = 0; i < 8; i++ )
835
for ( i = 0; i < nbPoints; i++ )
837
j = ( i + 1 ) % nbPoints;
839
for ( k = 0; k < 8; k++ )
842
if ( computeSegIntersection( px, py, rpx[k], rpy[k], x[i], y[i], x[j], y[j], &ix, &iy ) )
844
double d = dist_euc2d_sq( px, py, ix, iy );
849
//std::cout << "new dist for " << k << ": " << dist[k] << std::endl;
858
for ( i = 0; i < 8; i++ )
862
std::cout << "ERROR!!!!!!!!!!!!!!!!!" << std::endl;
865
//std::cout << "dist[" << i << "]: " << dist[i] << std::endl;
868
a = min( dist[0], dist[4] );
869
b = min( dist[1], dist[5] );
870
c = min( dist[2], dist[6] );
871
d = min( dist[3], dist[7] );
873
std::cout << "a: " << a << std::endl;
874
std::cout << "b: " << b << std::endl;
875
std::cout << "c: " << c << std::endl;
876
std::cout << "d: " << d << std::endl;
889
double PointSet::getDist( double px, double py, double *rx, double *ry )
891
if ( nbPoints == 1 || type == GEOS_POINT )
898
return dist_euc2d_sq( x[0], y[0], px, py );
902
int nbP = ( type == GEOS_POLYGON ? nbPoints : nbPoints - 1 );
904
double best_dist = DBL_MAX;
907
for ( a = 0; a < nbP; a++ )
909
b = ( a + 1 ) % nbPoints;
912
px2 = px - y[b] + y[a];
913
py2 = py + x[b] - x[a];
916
// (px,py)->(px2,py2) is a line perpendicular to a->b
917
// Check the line p->p2 cross the segment a->b
918
if ( computeLineSegIntersection( px, py, px2, py2,
919
x[a], y[a], x[b], y[b],
922
d = dist_euc2d_sq( px, py, ix, iy );
926
double d1 = dist_euc2d_sq( x[a], y[a], px, py );
927
double d2 = dist_euc2d_sq( x[b], y[b], px, py );
951
} // end for (a in nbPoints)
958
void PointSet::getCentroid( double &px, double &py )
960
// for explanation see this page:
961
// http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
964
double cx = 0, cy = 0, A = 0, tmp;
965
for ( i = 0; i < nbPoints; i++ )
967
j = i + 1; if ( j == nbPoints ) j = 0;
968
tmp = ( x[i] * y[j] - x[j] * y[i] );
969
cx += ( x[i] + x[j] ) * tmp;
970
cy += ( y[i] + y[j] ) * tmp;