void Poly::cleanup(double epsilon) { vertices = simplified(vertices, epsilon); if (!closed) return; uint n_vert = vertices.size(); vector<Vector2d> invert; invert.insert(invert.end(),vertices.begin()+n_vert/2,vertices.end()); invert.insert(invert.end(),vertices.begin(),vertices.begin()+n_vert/2); vertices = simplified(invert, epsilon); //calcHole(); }
void _cleanup_path(PathIterator& path, const agg::trans_affine& trans, bool remove_nans, bool do_clip, const agg::rect_base<double>& rect, e_snap_mode snap_mode, double stroke_width, bool do_simplify, bool return_curves, std::vector<double>& vertices, std::vector<npy_uint8>& codes) { typedef agg::conv_transform<PathIterator> transformed_path_t; typedef PathNanRemover<transformed_path_t> nan_removal_t; typedef PathClipper<nan_removal_t> clipped_t; typedef PathSnapper<clipped_t> snapped_t; typedef PathSimplifier<snapped_t> simplify_t; typedef agg::conv_curve<simplify_t> curve_t; transformed_path_t tpath(path, trans); nan_removal_t nan_removed(tpath, remove_nans, path.has_curves()); clipped_t clipped(nan_removed, do_clip, rect); snapped_t snapped(clipped, snap_mode, path.total_vertices(), stroke_width); simplify_t simplified(snapped, do_simplify, path.simplify_threshold()); vertices.reserve(path.total_vertices() * 2); codes.reserve(path.total_vertices()); if (return_curves) { __cleanup_path(simplified, vertices, codes); } else { curve_t curve(simplified); __cleanup_path(curve, vertices, codes); } }
Py::Object _path_module::convert_path_to_polygons(const Py::Tuple& args) { typedef agg::conv_transform<PathIterator> transformed_path_t; typedef SimplifyPath<transformed_path_t> simplify_t; typedef agg::conv_curve<simplify_t> curve_t; typedef std::vector<double> vertices_t; args.verify_length(4); PathIterator path(args[0]); agg::trans_affine trans = py_to_agg_transformation_matrix(args[1], false); double width = Py::Float(args[2]); double height = Py::Float(args[3]); bool simplify = path.should_simplify() && width != 0.0 && height != 0.0; transformed_path_t tpath(path, trans); simplify_t simplified(tpath, false, simplify, width, height); curve_t curve(simplified); Py::List polygons; vertices_t polygon; double x, y; unsigned code; polygon.reserve(path.total_vertices() * 2); while ((code = curve.vertex(&x, &y)) != agg::path_cmd_stop) { if ((code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) { if (polygon.size() >= 2) { polygon.push_back(polygon[0]); polygon.push_back(polygon[1]); _add_polygon(polygons, polygon); } polygon.clear(); } else { if (code == agg::path_cmd_move_to) { _add_polygon(polygons, polygon); polygon.clear(); } polygon.push_back(x); polygon.push_back(y); } } _add_polygon(polygons, polygon); return polygons; }
// Douglas-Peucker algorithm vector<Vector2d> simplified(const vector<Vector2d> &vert, double epsilon) { if (epsilon == 0) return vert; uint n_vert = vert.size(); if (n_vert<3) return vert; double dmax = 0; //Find the point with the maximum distance from line start-end uint index = 0; Vector2d normal = normalV(vert.back()-vert.front()); normal.normalize(); if( (normal.length()==0) || ((abs(normal.length())-1)>epsilon) ) return vert; for (uint i = 1; i < n_vert-1 ; i++) { double dist = abs((vert[i]-vert.front()).dot(normal)); if (dist >= epsilon && dist > dmax) { index = i; dmax = dist; } } vector<Vector2d> newvert; if (index > 0) // there is a point > epsilon { // divide at max dist point and cleanup both parts recursively vector<Vector2d> part1; part1.insert(part1.end(), vert.begin(), vert.begin()+index+1); vector<Vector2d> c1 = simplified(part1, epsilon); vector<Vector2d> part2; part2.insert(part2.end(), vert.begin()+index, vert.end()); vector<Vector2d> c2 = simplified(part2, epsilon); newvert.insert(newvert.end(), c1.begin(), c1.end()-1); newvert.insert(newvert.end(), c2.begin(), c2.end()); } else { // all points are nearer than espilon newvert.push_back(vert.front()); newvert.push_back(vert.back()); } return newvert; }
void FakeVimProxy::indentRegion(int beginBlock, int endBlock, QChar typedChar) { QPlainTextEdit *ed = qobject_cast<QPlainTextEdit *>(m_widget); if (!ed) return; const auto indentSize = theFakeVimSetting(FakeVim::Internal::ConfigShiftWidth) ->value().toInt(); QTextDocument *doc = ed->document(); QTextBlock startBlock = doc->findBlockByNumber(beginBlock); // Record line lengths for mark adjustments QVector<int> lineLengths(endBlock - beginBlock + 1); QTextBlock block = startBlock; for (int i = beginBlock; i <= endBlock; ++i) { const auto line = block.text(); lineLengths[i - beginBlock] = line.length(); if (typedChar.unicode() == 0 && line.simplified().isEmpty()) { // clear empty lines QTextCursor cursor(block); while (!cursor.atBlockEnd()) cursor.deleteChar(); } else { const auto previousBlock = block.previous(); const auto previousLine = previousBlock.isValid() ? previousBlock.text() : QString(); int indent = firstNonSpace(previousLine); if (typedChar == '}') indent = std::max(0, indent - indentSize); else if ( previousLine.endsWith("{") ) indent += indentSize; const auto indentString = QString(" ").repeated(indent); QTextCursor cursor(block); cursor.beginEditBlock(); cursor.movePosition(QTextCursor::StartOfBlock); cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, firstNonSpace(line)); cursor.removeSelectedText(); cursor.insertText(indentString); cursor.endEditBlock(); } block = block.next(); } }
void GroupSocket::connectToHost() { bool retry = false; if (socket.state() != QAbstractSocket::UnconnectedState) { retry = true; socket.abort(); } reset(); timer.start(); const auto &groupConfig = getConfig().groupManager; const auto remoteHost = groupConfig.host; const auto remotePort = static_cast<quint16>(groupConfig.remotePort); emit sendLog(QString("%1 to remote host %2:%3") .arg(retry ? "Reconnecting" : "Connecting") .arg(remoteHost.simplified().constData()) .arg(remotePort)); socket.connectToHost(remoteHost, remotePort); }
SEXP CallProxy::eval(){ if( TYPEOF(call) == LANGSXP ){ if( can_simplify(call) ){ SlicingIndex indices(0,subsets.nrows()) ; while(simplified(indices)) ; set_call(call) ; } int n = proxies.size() ; for( int i=0; i<n; i++){ proxies[i].set( subsets[proxies[i].symbol] ) ; } return call.eval(env) ; } else if( TYPEOF(call) == SYMSXP) { // SYMSXP if( subsets.count(call) ) return subsets.get_variable(call) ; return call.eval(env) ; } return call ; }
bool PNS_TOPOLOGY::SimplifyLine( PNS_LINE* aLine ) { if( !aLine->LinkedSegments() || !aLine->SegmentCount() ) return false; PNS_SEGMENT* root = ( *aLine->LinkedSegments() )[0]; PNS_LINE l = m_world->AssembleLine( root ); SHAPE_LINE_CHAIN simplified( l.CLine() ); simplified.Simplify(); if( simplified.PointCount() != l.PointCount() ) { PNS_LINE lnew( l ); m_world->Remove( &l ); lnew.SetShape( simplified ); m_world->Add( &lnew ); return true; } return false; }
Py::Object _path_module::convert_to_svg(const Py::Tuple& args) { args.verify_length(5); PathIterator path(args[0]); agg::trans_affine trans = py_to_agg_transformation_matrix(args[1].ptr(), false); Py::Object clip_obj = args[2]; bool do_clip; agg::rect_base<double> clip_rect(0, 0, 0, 0); if (clip_obj.isNone() || !clip_obj.isTrue()) { do_clip = false; } else { double x1, y1, x2, y2; Py::Tuple clip_tuple(clip_obj); x1 = Py::Float(clip_tuple[0]); y1 = Py::Float(clip_tuple[1]); x2 = Py::Float(clip_tuple[2]); y2 = Py::Float(clip_tuple[3]); clip_rect.init(x1, y1, x2, y2); do_clip = true; } bool simplify; Py::Object simplify_obj = args[3]; if (simplify_obj.isNone()) { simplify = path.should_simplify(); } else { simplify = simplify_obj.isTrue(); } int precision = Py::Int(args[4]); #if PY_VERSION_HEX < 0x02070000 char format[64]; snprintf(format, 64, "%s.%dg", "%", precision); #endif typedef agg::conv_transform<PathIterator> transformed_path_t; typedef PathNanRemover<transformed_path_t> nan_removal_t; typedef PathClipper<nan_removal_t> clipped_t; typedef PathSimplifier<clipped_t> simplify_t; transformed_path_t tpath(path, trans); nan_removal_t nan_removed(tpath, true, path.has_curves()); clipped_t clipped(nan_removed, do_clip, clip_rect); simplify_t simplified(clipped, simplify, path.simplify_threshold()); size_t buffersize = path.total_vertices() * (precision + 5) * 4; char* buffer = (char *)malloc(buffersize); char* p = buffer; const char codes[] = {'M', 'L', 'Q', 'C'}; const int waits[] = { 1, 1, 2, 3}; int wait = 0; unsigned code; double x = 0, y = 0; while ((code = simplified.vertex(&x, &y)) != agg::path_cmd_stop) { if (wait == 0) { *p++ = '\n'; if (code == 0x4f) { *p++ = 'z'; *p++ = '\n'; continue; } *p++ = codes[code-1]; wait = waits[code-1]; } else { *p++ = ' '; } #if PY_VERSION_HEX >= 0x02070000 char* str; str = PyOS_double_to_string(x, 'g', precision, 0, NULL); p += snprintf(p, buffersize - (p - buffer), str); PyMem_Free(str); *p++ = ' '; str = PyOS_double_to_string(y, 'g', precision, 0, NULL); p += snprintf(p, buffersize - (p - buffer), str); PyMem_Free(str); #else char str[64]; PyOS_ascii_formatd(str, 64, format, x); p += snprintf(p, buffersize - (p - buffer), str); *p++ = ' '; PyOS_ascii_formatd(str, 64, format, y); p += snprintf(p, buffersize - (p - buffer), str); #endif --wait; } #if PY3K PyObject* result = PyUnicode_FromStringAndSize(buffer, p - buffer); #else PyObject* result = PyString_FromStringAndSize(buffer, p - buffer); #endif free(buffer); return Py::Object(result, true); }
else pieces.append(typeString + " " + parameter.name()); } signature = "(" + pieces.join(", ") + ")"; } QDomElement contextElement = document.createElement("context"); QDomElement nameElement = document.createElement("name"); nameElement.appendChild(document.createTextNode(nodeName + signature)); contextElement.appendChild(nameElement); QDomElement messageElement = document.createElement("message"); contextElement.appendChild(messageElement); QDomElement sourceElement = document.createElement("source"); QString sourceText = simplified(node->doc().source()); if (!signature.isEmpty() && signature != "()" && !sourceText.contains("\\fn")) sourceText.prepend(QString("\\fn %1%2\n").arg(nodeName).arg(signature)); sourceElement.appendChild(document.createTextNode(sourceText)); messageElement.appendChild(sourceElement); QDomElement translationElement = document.createElement("translation"); translationElement.setAttribute("type", "unfinished"); messageElement.appendChild(translationElement); QDomElement locationElement = document.createElement("location"); locationElement.setAttribute("filename", node->doc().location().filePath()); locationElement.setAttribute("line", node->doc().location().lineNo()); messageElement.appendChild(locationElement); contexts.append(contextElement);
int delaunayTriang(const vector<Vector2d> &points, vector<Triangle> &triangles, double z) { #define PTRIANGULATOR 0 #if PTRIANGULATOR vector<Vector2d> spoints = simplified(points, 1); uint npoints = spoints.size(); vector<double> xpoints(npoints); vector<double> ypoints(npoints); for (uint i = 0; i < npoints; i++) { xpoints[i] = spoints[npoints-i-1].x(); ypoints[i] = spoints[npoints-i-1].y(); } polytri::PolygonTriangulator pt(xpoints, ypoints); const polytri::PolygonTriangulator::Triangles * tr = pt.triangles(); uint ntr = tr->size(); cerr << npoints << " -> " << ntr << endl; triangles.resize(ntr); uint itri=0; for (polytri::PolygonTriangulator::Triangles::const_iterator itr = tr->begin(); itr != tr->end(); ++itr) { const polytri::PolygonTriangulator::Triangle t = *itr; triangles[itri] = Triangle(Vector3d(xpoints[t[0]], ypoints[t[0]], z), Vector3d(xpoints[t[1]], ypoints[t[1]], z), Vector3d(xpoints[t[2]], ypoints[t[2]], z)); itri++; } return ntr; #endif // struct triangulateio in; // in.numberofpoints = npoints; // in.numberofpointattributes = (int)0; // in.pointlist = // in.pointattributelist = NULL; // in.pointmarkerlist = (int *) NULL; // in.numberofsegments = 0; // in.numberofholes = 0; // in.numberofregions = 0; // in.regionlist = (REAL *) NULL; // delclass = new piyush; // piyush *pdelclass = (piyush *)delclass; // triswitches.push_back('\0'); // char *ptris = &triswitches[0]; // pmesh = new piyush::__pmesh; // pbehavior = new piyush::__pbehavior; // piyush::__pmesh * tpmesh = (piyush::__pmesh *) pmesh; // piyush::__pbehavior * tpbehavior = (piyush::__pbehavior *) pbehavior; // pdelclass->triangleinit(tpmesh); // pdelclass->parsecommandline(1, &ptris, tpbehavior); // pdelclass->transfernodes(tpmesh, tpbehavior, in.pointlist, // in.pointattributelist, // in.pointmarkerlist, in.numberofpoints, // in.numberofpointattributes); // tpmesh->hullsize = pdelclass->delaunay(tpmesh, tpbehavior); // /* Ensure that no vertex can be mistaken for a triangular bounding */ // /* box vertex in insertvertex(). */ // tpmesh->infvertex1 = (piyush::vertex) NULL; // tpmesh->infvertex2 = (piyush::vertex) NULL; // tpmesh->infvertex3 = (piyush::vertex) NULL; // /* Calculate the number of edges. */ // tpmesh->edges = (3l * tpmesh->triangles.items + tpmesh->hullsize) / 2l; // pdelclass->numbernodes(tpmesh, tpbehavior); /////////////////////////////////////////////// triangle++ crap // typedef reviver::dpoint <double, 2> Point; // vector<Point> p(points.size()); // for (uint i = 0; i < p.size(); i++) { // p[i] = Point(points[i].x(),points[i].y()); // } // tpp::Delaunay del(p); // /* // -p Triangulates a Planar Straight Line Graph (.poly file). // -r Refines a previously generated mesh. // -q Quality mesh generation. A minimum angle may be specified. // -a Applies a maximum triangle area constraint. // -u Applies a user-defined triangle constraint. // -A Applies attributes to identify triangles in certain regions. // -c Encloses the convex hull with segments. // -D Conforming Delaunay: all triangles are truly Delaunay. // -j Jettison unused vertices from output .node file. // -e Generates an edge list. // -v Generates a Voronoi diagram. // -n Generates a list of triangle neighbors. // -g Generates an .off file for Geomview. // -B Suppresses output of boundary information. // -P Suppresses output of .poly file. // -N Suppresses output of .node file. // -E Suppresses output of .ele file. // -I Suppresses mesh iteration numbers. // -O Ignores holes in .poly file. // -X Suppresses use of exact arithmetic. // -z Numbers all items starting from zero (rather than one). // -o2 Generates second-order subparametric elements. // -Y Suppresses boundary segment splitting. // -S Specifies maximum number of added Steiner points. // -i Uses incremental method, rather than divide-and-conquer. // -F Uses Fortune's sweepline algorithm, rather than d-and-c. // -l Uses vertical cuts only, rather than alternating cuts. // -s Force segments into mesh by splitting (instead of using CDT). // -C Check consistency of final mesh. // -Q Quiet: No terminal output except errors. // */ // string switches = "pq0DzQ"; // del.Triangulate(switches); // int ntri = del.ntriangles(); // if (ntri>0) { // triangles.resize(ntri); // uint itri=0; // for (tpp::Delaunay::fIterator dit = del.fbegin(); dit != del.fend(); ++dit) { // Point pA = del.point_at_vertex_id(del.Org (dit)); // Point pB = del.point_at_vertex_id(del.Dest(dit)); // Point pC = del.point_at_vertex_id(del.Apex(dit)); // triangles[itri] = Triangle(Vector3d(pA[0],pA[1],z), // Vector3d(pB[0],pB[1],z), // Vector3d(pC[0],pC[1],z)); // // Vector2d vA = points[del.Org (dit)]; // // Vector2d vB = points[del.Dest(dit)]; // // Vector2d vC = points[del.Apex(dit)]; // // triangles[itri] = Triangle(Vector3d(vA.x(),vA.y(),z), // // Vector3d(vB.x(),vB.y(),z), // // Vector3d(vC.x(),vC.y(),z)); // itri++; // } // } // return ntri; return 0; }
/// @par /// /// The raw contours will match the region outlines exactly. The @p maxError and @p maxEdgeLen /// parameters control how closely the simplified contours will match the raw contours. /// /// Simplified contours are generated such that the vertices for portals between areas match up. /// (They are considered mandatory vertices.) /// /// Setting @p maxEdgeLength to zero will disabled the edge length feature. /// /// See the #rcConfig documentation for more information on the configuration parameters. /// /// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, const float maxError, const int maxEdgeLen, rcContourSet& cset, const int buildFlags) { rcAssert(ctx); const int w = chf.width; const int h = chf.height; const int borderSize = chf.borderSize; ctx->startTimer(RC_TIMER_BUILD_CONTOURS); rcVcopy(cset.bmin, chf.bmin); rcVcopy(cset.bmax, chf.bmax); if (borderSize > 0) { // If the heightfield was build with bordersize, remove the offset. const float pad = borderSize*chf.cs; cset.bmin[0] += pad; cset.bmin[2] += pad; cset.bmax[0] -= pad; cset.bmax[2] -= pad; } cset.cellSizeXZ = chf.cs; cset.cellSizeY = chf.ch; cset.width = chf.width - chf.borderSize*2; cset.height = chf.height - chf.borderSize*2; cset.borderSize = chf.borderSize; int maxContours = rcMax((int)chf.maxRegions, 8); cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM); if (!cset.conts) return false; cset.nconts = 0; rcScopedDelete<unsigned char> flags = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP); if (!flags) { ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' (%d).", chf.spanCount); return false; } ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE); // Mark boundaries. for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { const rcCompactCell& c = chf.cells[x+y*w]; for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { unsigned char res = 0; const rcCompactSpan& s = chf.spans[i]; if (!chf.spans[i].regionID || (chf.spans[i].regionID & RC_BORDER_REG)) { flags[i] = 0; continue; } for (int dir = 0; dir < 4; ++dir) { unsigned short r = 0; if (rcGetCon(s, dir) != RC_NOT_CONNECTED) { const int ax = x + rcGetDirOffsetX(dir); const int ay = y + rcGetDirOffsetY(dir); const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); r = chf.spans[ai].regionID; } if (r == chf.spans[i].regionID) res |= (1 << dir); } flags[i] = res ^ 0xf; // Inverse, mark non connected edges. } } } ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE); rcIntArray verts(256); rcIntArray simplified(64); for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { const rcCompactCell& c = chf.cells[x+y*w]; for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { if (flags[i] == 0 || flags[i] == 0xf) { flags[i] = 0; continue; } const unsigned short reg = chf.spans[i].regionID; if (!reg || (reg & RC_BORDER_REG)) continue; const navAreaMask areaMask = chf.areaMasks[ i ]; verts.resize(0); simplified.resize(0); ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE); walkContour(x, y, i, chf, flags, verts); ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE); ctx->startTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY); simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags); removeDegenerateSegments(simplified); ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY); // Store region->contour remap info. // Create contour. if (simplified.size()/4 >= 3) { if (cset.nconts >= maxContours) { // Allocate more contours. // This happens when a region has holes. const int oldMax = maxContours; maxContours *= 2; rcContour* newConts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM); for (int j = 0; j < cset.nconts; ++j) { newConts[j] = cset.conts[j]; // Reset source pointers to prevent data deletion. cset.conts[j].verts = 0; cset.conts[j].rverts = 0; } rcFree(cset.conts); cset.conts = newConts; ctx->log(RC_LOG_WARNING, "rcBuildContours: Expanding max contours from %d to %d.", oldMax, maxContours); } rcContour* cont = &cset.conts[cset.nconts++]; cont->nverts = simplified.size()/4; cont->verts = (int*)rcAlloc(sizeof(int)*cont->nverts*4, RC_ALLOC_PERM); if (!cont->verts) { ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'verts' (%d).", cont->nverts); return false; } memcpy(cont->verts, &simplified[0], sizeof(int)*cont->nverts*4); if (borderSize > 0) { // If the heightfield was build with bordersize, remove the offset. for (int j = 0; j < cont->nverts; ++j) { int* v = &cont->verts[j*4]; v[0] -= borderSize; v[2] -= borderSize; } } cont->nrverts = verts.size()/4; cont->rverts = (int*)rcAlloc(sizeof(int)*cont->nrverts*4, RC_ALLOC_PERM); if (!cont->rverts) { ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'rverts' (%d).", cont->nrverts); return false; } memcpy(cont->rverts, &verts[0], sizeof(int)*cont->nrverts*4); if (borderSize > 0) { // If the heightfield was build with bordersize, remove the offset. for (int j = 0; j < cont->nrverts; ++j) { int* v = &cont->rverts[j*4]; v[0] -= borderSize; v[2] -= borderSize; } } cont->reg = reg; cont->areaMask = areaMask; } } } } // Merge holes if needed. if (cset.nconts > 0) { // Calculate winding of all polygons. rcScopedDelete<char> winding = (char*)rcAlloc(sizeof(char)*cset.nconts, RC_ALLOC_TEMP); if (!winding) { ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'hole' (%d).", cset.nconts); return false; } int nholes = 0; for (int i = 0; i < cset.nconts; ++i) { rcContour& cont = cset.conts[i]; // If the contour is wound backwards, it is a hole. winding[i] = calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0 ? -1 : 1; if (winding[i] < 0) nholes++; } if (nholes > 0) { // Collect outline contour and holes contours per region. // We assume that there is one outline and multiple holes. const int nregions = chf.maxRegions+1; rcScopedDelete<rcContourRegion> regions = (rcContourRegion*)rcAlloc(sizeof(rcContourRegion)*nregions, RC_ALLOC_TEMP); if (!regions) { ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'regions' (%d).", nregions); return false; } memset(regions, 0, sizeof(rcContourRegion)*nregions); rcScopedDelete<rcContourHole> holes = (rcContourHole*)rcAlloc(sizeof(rcContourHole)*cset.nconts, RC_ALLOC_TEMP); if (!holes) { ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'holes' (%d).", cset.nconts); return false; } memset(holes, 0, sizeof(rcContourHole)*cset.nconts); for (int i = 0; i < cset.nconts; ++i) { rcContour& cont = cset.conts[i]; // Positively would contours are outlines, negative holes. if (winding[i] > 0) { if (regions[cont.reg].outline) ctx->log(RC_LOG_ERROR, "rcBuildContours: Multiple outlines for region %d.", cont.reg); regions[cont.reg].outline = &cont; } else { regions[cont.reg].nholes++; } } int index = 0; for (int i = 0; i < nregions; i++) { if (regions[i].nholes > 0) { regions[i].holes = &holes[index]; index += regions[i].nholes; regions[i].nholes = 0; } } for (int i = 0; i < cset.nconts; ++i) { rcContour& cont = cset.conts[i]; rcContourRegion& reg = regions[cont.reg]; if (winding[i] < 0) reg.holes[reg.nholes++].contour = &cont; } // Finally merge each regions holes into the outline. for (int i = 0; i < nregions; i++) { rcContourRegion& reg = regions[i]; if (!reg.nholes) continue; if (reg.outline) { mergeRegionHoles(ctx, reg); } else { // The region does not have an outline. // This can happen if the contour becaomes selfoverlapping because of // too aggressive simplification settings. ctx->log(RC_LOG_ERROR, "rcBuildContours: Bad outline for region %d, contour simplification is likely too aggressive.", i); } } } } ctx->stopTimer(RC_TIMER_BUILD_CONTOURS); return true; }
void ExPoly::cleanup(double epsilon) { outer.vertices = simplified(outer.vertices, epsilon); for (uint i=0; i < holes.size(); i++) holes[i].vertices = simplified(holes[i].vertices, epsilon); }