void PointSet::splitPolygons( QLinkedList<PointSet *> &shapes_toProcess, QLinkedList<PointSet *> &shapes_final, double xrm, double yrm ) { int i, j; int nbp; double *x = nullptr; double *y = nullptr; int *pts = nullptr; int *cHull = nullptr; int cHullSize; double cp; double bestcp = 0; double bestArea = 0; double area; double base; double b, c; double s; int ihs; int ihn; int ips; int ipn; int holeS = -1; // hole start and end points int holeE = -1; int retainedPt = -1; int pt = 0; double labelArea = xrm * yrm; PointSet *shape = nullptr; while ( !shapes_toProcess.isEmpty() ) { shape = shapes_toProcess.takeFirst(); x = shape->x; y = shape->y; nbp = shape->nbPoints; pts = new int[nbp]; for ( i = 0; i < nbp; i++ ) { pts[i] = i; } // conpute convex hull shape->cHullSize = GeomFunction::convexHullId( pts, x, y, nbp, shape->cHull ); cHull = shape->cHull; cHullSize = shape->cHullSize; bestArea = 0; retainedPt = -1; // lookup for a hole for ( ihs = 0; ihs < cHullSize; ihs++ ) { // ihs->ihn => cHull'seg ihn = ( ihs + 1 ) % cHullSize; ips = cHull[ihs]; ipn = ( ips + 1 ) % nbp; if ( ipn != cHull[ihn] ) // next point on shape is not the next point on cHull => there is a hole here ! { bestcp = 0; pt = -1; // lookup for the deepest point in the hole for ( i = ips; i != cHull[ihn]; i = ( i + 1 ) % nbp ) { cp = std::fabs( GeomFunction::cross_product( x[cHull[ihs]], y[cHull[ihs]], x[cHull[ihn]], y[cHull[ihn]], x[i], y[i] ) ); if ( cp - bestcp > EPSILON ) { bestcp = cp; pt = i; } } if ( pt != -1 ) { // compute the ihs->ihn->pt triangle's area base = GeomFunction::dist_euc2d( x[cHull[ihs]], y[cHull[ihs]], x[cHull[ihn]], y[cHull[ihn]] ); b = GeomFunction::dist_euc2d( x[cHull[ihs]], y[cHull[ihs]], x[pt], y[pt] ); c = GeomFunction::dist_euc2d( x[cHull[ihn]], y[cHull[ihn]], x[pt], y[pt] ); s = ( base + b + c ) / 2; // s = half perimeter area = s * ( s - base ) * ( s - b ) * ( s - c ); if ( area < 0 ) area = -area; // retain the biggest area if ( area - bestArea > EPSILON ) { bestArea = area; retainedPt = pt; holeS = ihs; holeE = ihn; } } } } // we have a hole, its area, and the deppest point in hole // we're going to find the second point to cup the shape // holeS = hole starting point // holeE = hole ending point // retainedPt = deppest point in hole // bestArea = area of triangle HoleS->holeE->retainedPoint bestArea = std::sqrt( bestArea ); double cx, cy, dx, dy, ex, ey, fx, fy, seg_length, ptx = 0, pty = 0, fptx = 0, fpty = 0; int ps = -1, pe = -1, fps = -1, fpe = -1; 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) { c = std::numeric_limits<double>::max(); // iterate on all shape points except points which are in the hole bool isValid; int k, l; for ( i = ( cHull[holeE] + 1 ) % nbp; i != ( cHull[holeS] - 1 + nbp ) % nbp; i = j ) { j = ( i + 1 ) % nbp; // i->j is shape segment not in hole // compute distance between retainedPoint and segment // whether perpendicular distance (if retaindPoint is fronting segment i->j) // or distance between retainedPt and i or j (choose the nearest) seg_length = GeomFunction::dist_euc2d( x[i], y[i], x[j], y[j] ); cx = ( x[i] + x[j] ) / 2.0; cy = ( y[i] + y[j] ) / 2.0; dx = cy - y[i]; dy = cx - x[i]; ex = cx - dx; ey = cy + dy; fx = cx + dx; fy = cy - dy; if ( seg_length < EPSILON || std::fabs( ( b = GeomFunction::cross_product( ex, ey, fx, fy, x[retainedPt], y[retainedPt] ) / ( seg_length ) ) ) > ( seg_length / 2 ) ) // retainedPt is not fronting i->j { if ( ( ex = GeomFunction::dist_euc2d_sq( x[i], y[i], x[retainedPt], y[retainedPt] ) ) < ( ey = GeomFunction::dist_euc2d_sq( x[j], y[j], x[retainedPt], y[retainedPt] ) ) ) { b = ex; ps = i; pe = i; } else { b = ey; ps = j; pe = j; } } else // point fronting i->j => compute pependicular distance => create a new point { b = GeomFunction::cross_product( x[i], y[i], x[j], y[j], x[retainedPt], y[retainedPt] ) / seg_length; b *= b; ps = i; pe = j; if ( !GeomFunction::computeLineIntersection( x[i], y[i], x[j], y[j], x[retainedPt], y[retainedPt], x[retainedPt] - dx, y[retainedPt] + dy, &ptx, &pty ) ) { //error - it should intersect the line } } isValid = true; double pointX, pointY; if ( ps == pe ) { pointX = x[pe]; pointY = y[pe]; } else { pointX = ptx; pointY = pty; } for ( k = cHull[holeS]; k != cHull[holeE]; k = ( k + 1 ) % nbp ) { l = ( k + 1 ) % nbp; if ( GeomFunction::isSegIntersects( x[retainedPt], y[retainedPt], pointX, pointY, x[k], y[k], x[l], y[l] ) ) { isValid = false; break; } } if ( isValid && b < c ) { c = b; fps = ps; fpe = pe; fptx = ptx; fpty = pty; } } // for point which are not in hole // we will cut the shapeu in two new shapes, one from [retainedPoint] to [newPoint] and one form [newPoint] to [retainedPoint] int imin = retainedPt; int imax = ( ( ( fps < retainedPt && fpe < retainedPt ) || ( fps > retainedPt && fpe > retainedPt ) ) ? std::min( fps, fpe ) : std::max( fps, fpe ) ); int nbPtSh1, nbPtSh2; // how many points in new shapes ? if ( imax > imin ) nbPtSh1 = imax - imin + 1 + ( fpe != fps ); else nbPtSh1 = imax + nbp - imin + 1 + ( fpe != fps ); if ( ( imax == fps ? fpe : fps ) < imin ) nbPtSh2 = imin - ( imax == fps ? fpe : fps ) + 1 + ( fpe != fps ); else nbPtSh2 = imin + nbp - ( imax == fps ? fpe : fps ) + 1 + ( fpe != fps ); if ( retainedPt == -1 || fps == -1 || fpe == -1 ) { if ( shape->parent ) delete shape; } // check for useless spliting else if ( imax == imin || nbPtSh1 <= 2 || nbPtSh2 <= 2 || nbPtSh1 == nbp || nbPtSh2 == nbp ) { shapes_final.append( shape ); } else { PointSet *newShape = shape->extractShape( nbPtSh1, imin, imax, fps, fpe, fptx, fpty ); if ( shape->parent ) newShape->parent = shape->parent; else newShape->parent = shape; shapes_toProcess.append( newShape ); if ( imax == fps ) imax = fpe; else imax = fps; newShape = shape->extractShape( nbPtSh2, imax, imin, fps, fpe, fptx, fpty ); if ( shape->parent ) newShape->parent = shape->parent; else newShape->parent = shape; shapes_toProcess.append( newShape ); if ( shape->parent ) delete shape; } } else { shapes_final.append( shape ); } delete[] pts; } }