const SHAPE_LINE_CHAIN SegmentHull ( const SHAPE_SEGMENT& aSeg, int aClearance, int aWalkaroundThickness ) { int d = aSeg.GetWidth() / 2 + aClearance + aWalkaroundThickness / 2 + HULL_MARGIN; int x = (int)( 2.0 / ( 1.0 + M_SQRT2 ) * d ); const VECTOR2I a = aSeg.GetSeg().A; const VECTOR2I b = aSeg.GetSeg().B; VECTOR2I dir = b - a; VECTOR2I p0 = dir.Perpendicular().Resize( d ); VECTOR2I ds = dir.Perpendicular().Resize( x / 2 ); VECTOR2I pd = dir.Resize( x / 2 ); VECTOR2I dp = dir.Resize( d ); SHAPE_LINE_CHAIN s; s.SetClosed( true ); s.Append( b + p0 + pd ); s.Append( b + dp + ds ); s.Append( b + dp - ds ); s.Append( b - p0 + pd ); s.Append( a - p0 - pd ); s.Append( a - dp - ds ); s.Append( a - dp + ds ); s.Append( a + p0 - pd ); // make sure the hull outline is always clockwise if( s.CSegment( 0 ).Side( a ) < 0 ) return s.Reverse(); else return s; }
bool SHAPE_POLY_SET::Parse( std::stringstream& aStream ) { std::string tmp; aStream >> tmp; if( tmp != "polyset" ) return false; aStream >> tmp; int n_polys = atoi( tmp.c_str() ); if( n_polys < 0 ) return false; for( int i = 0; i < n_polys; i++ ) { POLYGON paths; aStream >> tmp; if( tmp != "poly" ) return false; aStream >> tmp; int n_outlines = atoi( tmp.c_str() ); if( n_outlines < 0 ) return false; for( int j = 0; j < n_outlines; j++ ) { SHAPE_LINE_CHAIN outline; outline.SetClosed( true ); aStream >> tmp; int n_vertices = atoi( tmp.c_str() ); for( int v = 0; v < n_vertices; v++ ) { VECTOR2I p; aStream >> tmp; p.x = atoi( tmp.c_str() ); aStream >> tmp; p.y = atoi( tmp.c_str() ); outline.Append( p ); } paths.push_back( outline ); } m_polys.push_back( paths ); } return true; }
const SHAPE_LINE_CHAIN ConvexHull( const SHAPE_CONVEX& aConvex, int aClearance ) { // this defines the horizontal and vertical lines in the hull octagon BOX2I box = aConvex.BBox( aClearance + HULL_MARGIN ); box.Normalize(); SEG topline = SEG( VECTOR2I( box.GetX(), box.GetY() + box.GetHeight() ), VECTOR2I( box.GetX() + box.GetWidth(), box.GetY() + box.GetHeight() ) ); SEG rightline = SEG( VECTOR2I( box.GetX() + box.GetWidth(), box.GetY() + box.GetHeight() ), VECTOR2I( box.GetX() + box.GetWidth(), box.GetY() ) ); SEG bottomline = SEG( VECTOR2I( box.GetX() + box.GetWidth(), box.GetY() ), box.GetOrigin() ); SEG leftline = SEG( box.GetOrigin(), VECTOR2I( box.GetX(), box.GetY() + box.GetHeight() ) ); const SHAPE_LINE_CHAIN& vertices = aConvex.Vertices(); // top right diagonal VECTOR2I corner = box.GetOrigin() + box.GetSize(); SEG toprightline = SEG( corner, corner + VECTOR2I( box.GetHeight(), -box.GetHeight() ) ); MoveDiagonal( toprightline, vertices, aClearance ); // bottom right diagonal corner = box.GetOrigin() + VECTOR2I( box.GetWidth(), 0 ); SEG bottomrightline = SEG( corner + VECTOR2I( box.GetHeight(), box.GetHeight() ), corner ); MoveDiagonal( bottomrightline, vertices, aClearance ); // bottom left diagonal corner = box.GetOrigin(); SEG bottomleftline = SEG( corner, corner + VECTOR2I( -box.GetHeight(), box.GetHeight() ) ); MoveDiagonal( bottomleftline, vertices, aClearance ); // top left diagonal corner = box.GetOrigin() + VECTOR2I( 0, box.GetHeight() ); SEG topleftline = SEG( corner + VECTOR2I( -box.GetHeight(), -box.GetHeight() ), corner ); MoveDiagonal( topleftline, vertices, aClearance ); SHAPE_LINE_CHAIN octagon; octagon.SetClosed( true ); octagon.Append( *leftline.IntersectLines( bottomleftline ) ); octagon.Append( *bottomline.IntersectLines( bottomleftline ) ); octagon.Append( *bottomline.IntersectLines( bottomrightline ) ); octagon.Append( *rightline.IntersectLines( bottomrightline ) ); octagon.Append( *rightline.IntersectLines( toprightline ) ); octagon.Append( *topline.IntersectLines( toprightline ) ); octagon.Append( *topline.IntersectLines( topleftline ) ); octagon.Append( *leftline.IntersectLines( topleftline ) ); return octagon; }
const SHAPE_LINE_CHAIN OctagonalHull( const VECTOR2I& aP0, const VECTOR2I& aSize, int aClearance, int aChamfer ) { SHAPE_LINE_CHAIN s; s.SetClosed( true ); s.Append( aP0.x - aClearance, aP0.y - aClearance + aChamfer ); s.Append( aP0.x - aClearance + aChamfer, aP0.y - aClearance ); s.Append( aP0.x + aSize.x + aClearance - aChamfer, aP0.y - aClearance ); s.Append( aP0.x + aSize.x + aClearance, aP0.y - aClearance + aChamfer ); s.Append( aP0.x + aSize.x + aClearance, aP0.y + aSize.y + aClearance - aChamfer ); s.Append( aP0.x + aSize.x + aClearance - aChamfer, aP0.y + aSize.y + aClearance ); s.Append( aP0.x - aClearance + aChamfer, aP0.y + aSize.y + aClearance ); s.Append( aP0.x - aClearance, aP0.y + aSize.y + aClearance - aChamfer ); return s; }
// This is the same function as in board_items_to_polygon_shape_transform.cpp // but it adds the rect/trapezoid shapes with a different winding void CINFO3D_VISU::buildPadShapePolygon( const D_PAD* aPad, SHAPE_POLY_SET& aCornerBuffer, wxSize aInflateValue, int aSegmentsPerCircle, double aCorrectionFactor ) const { wxPoint corners[4]; wxPoint PadShapePos = aPad->ShapePos(); /* Note: for pad having a shape offset, * the pad position is NOT the shape position */ switch( aPad->GetShape() ) { case PAD_SHAPE_CIRCLE: case PAD_SHAPE_OVAL: case PAD_SHAPE_ROUNDRECT: aPad->TransformShapeWithClearanceToPolygon( aCornerBuffer, aInflateValue.x, aSegmentsPerCircle, aCorrectionFactor ); break; case PAD_SHAPE_TRAPEZOID: case PAD_SHAPE_RECT: { SHAPE_LINE_CHAIN aLineChain; aPad->BuildPadPolygon( corners, aInflateValue, aPad->GetOrientation() ); for( int ii = 0; ii < 4; ++ii ) { corners[3-ii] += PadShapePos; // Shift origin to position aLineChain.Append( corners[3-ii].x, corners[3-ii].y ); } aLineChain.SetClosed( true ); aCornerBuffer.AddOutline( aLineChain ); } break; default: wxFAIL_MSG( wxT( "CINFO3D_VISU::buildPadShapePolygon: found a not implemented pad shape (new shape?)" ) ); break; } }
void GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos ) { VECTOR2I origin; switch( aItem->Type() ) { case PCB_MODULE_T: { MODULE* mod = static_cast<MODULE*>( aItem ); addAnchor( mod->GetPosition(), ORIGIN | SNAPPABLE, mod ); for( D_PAD* pad = mod->Pads(); pad; pad = pad->Next() ) addAnchor( pad->GetPosition(), CORNER | SNAPPABLE, pad ); break; } case PCB_PAD_T: { D_PAD* pad = static_cast<D_PAD*>( aItem ); addAnchor( pad->GetPosition(), CORNER | SNAPPABLE, pad ); break; } case PCB_MODULE_EDGE_T: case PCB_LINE_T: { DRAWSEGMENT* dseg = static_cast<DRAWSEGMENT*>( aItem ); VECTOR2I start = dseg->GetStart(); VECTOR2I end = dseg->GetEnd(); //LAYER_ID layer = dseg->GetLayer(); switch( dseg->GetShape() ) { case S_CIRCLE: { int r = ( start - end ).EuclideanNorm(); addAnchor( start, ORIGIN | SNAPPABLE, dseg ); addAnchor( start + VECTOR2I( -r, 0 ), OUTLINE | SNAPPABLE, dseg ); addAnchor( start + VECTOR2I( r, 0 ), OUTLINE | SNAPPABLE, dseg ); addAnchor( start + VECTOR2I( 0, -r ), OUTLINE | SNAPPABLE, dseg ); addAnchor( start + VECTOR2I( 0, r ), OUTLINE | SNAPPABLE, dseg ); break; } case S_ARC: { origin = dseg->GetCenter(); addAnchor( dseg->GetArcStart(), CORNER | SNAPPABLE, dseg ); addAnchor( dseg->GetArcEnd(), CORNER | SNAPPABLE, dseg ); addAnchor( origin, ORIGIN | SNAPPABLE, dseg ); break; } case S_SEGMENT: { origin.x = start.x + ( start.x - end.x ) / 2; origin.y = start.y + ( start.y - end.y ) / 2; addAnchor( start, CORNER | SNAPPABLE, dseg ); addAnchor( end, CORNER | SNAPPABLE, dseg ); addAnchor( origin, ORIGIN, dseg ); break; } default: { origin = dseg->GetStart(); addAnchor( origin, ORIGIN | SNAPPABLE, dseg ); break; } } break; } case PCB_TRACE_T: { TRACK* track = static_cast<TRACK*>( aItem ); VECTOR2I start = track->GetStart(); VECTOR2I end = track->GetEnd(); origin.x = start.x + ( start.x - end.x ) / 2; origin.y = start.y + ( start.y - end.y ) / 2; addAnchor( start, CORNER | SNAPPABLE, track ); addAnchor( end, CORNER | SNAPPABLE, track ); addAnchor( origin, ORIGIN, track); break; } case PCB_VIA_T: addAnchor( aItem->GetPosition(), CORNER | SNAPPABLE, aItem ); break; case PCB_ZONE_AREA_T: { const CPolyLine* outline = static_cast<const ZONE_CONTAINER*>( aItem )->Outline(); int cornersCount = outline->GetCornersCount(); SHAPE_LINE_CHAIN lc; lc.SetClosed( true ); for( int i = 0; i < cornersCount; ++i ) { const VECTOR2I p ( outline->GetPos( i ) ); addAnchor( p, CORNER, aItem ); lc.Append( p ); } addAnchor( lc.NearestPoint( aRefPos ), OUTLINE, aItem ); break; } case PCB_MODULE_TEXT_T: case PCB_TEXT_T: addAnchor( aItem->GetPosition(), ORIGIN, aItem ); default: break; } }
void Convert_path_polygon_to_polygon_blocks_and_dummy_blocks( const SHAPE_POLY_SET &aMainPath, CGENERICCONTAINER2D &aDstContainer, float aBiuTo3DunitsScale, float aDivFactor, const BOARD_ITEM &aBoardItem ) { BOX2I pathBounds = aMainPath.BBox(); // Get the path wxASSERT( aMainPath.OutlineCount() == 1 ); const SHAPE_POLY_SET::POLYGON& curr_polywithholes = aMainPath.CPolygon( 0 ); wxASSERT( curr_polywithholes.size() == 1 ); const SHAPE_LINE_CHAIN& path = curr_polywithholes[0]; // a simple polygon // Convert the points to segments class CBBOX2D bbox; bbox.Reset(); // Contains the main list of segments and each segment normal interpolated SEGMENTS_WIDTH_NORMALS segments_and_normals; // Contains a closed polygon used to calc if points are inside SEGMENTS segments; segments_and_normals.resize( path.PointCount() ); segments.resize( path.PointCount() ); for( int i = 0; i < path.PointCount(); i++ ) { const VECTOR2I& a = path.CPoint( i ); const SFVEC2F point ( (float)( a.x) * aBiuTo3DunitsScale, (float)(-a.y) * aBiuTo3DunitsScale ); bbox.Union( point ); segments_and_normals[i].m_Start = point; segments[i].m_Start = point; } bbox.ScaleNextUp(); // Calc the slopes, normals and some statistics about this polygon unsigned int i; unsigned int j = segments_and_normals.size() - 1; // Temporary normal to the segment, it will later be used for interpolation std::vector< SFVEC2F > tmpSegmentNormals; tmpSegmentNormals.resize( segments_and_normals.size() ); float medOfTheSquaresSegmentLength = 0.0f; #ifdef PRINT_STATISTICS_3D_VIEWER float minLength = FLT_MAX; #endif for( i = 0; i < segments_and_normals.size(); j = i++ ) { const SFVEC2F slope = segments_and_normals[j].m_Start - segments_and_normals[i].m_Start; segments_and_normals[i].m_Precalc_slope = slope; // Calculate constants for each segment segments[i].m_inv_JY_minus_IY = 1.0f / ( segments_and_normals[j].m_Start.y - segments_and_normals[i].m_Start.y ); segments[i].m_JX_minus_IX = ( segments_and_normals[j].m_Start.x - segments_and_normals[i].m_Start.x ); // The normal orientation expect a fixed polygon orientation (!TODO: which one?) //tmpSegmentNormals[i] = glm::normalize( SFVEC2F( -slope.y, +slope.x ) ); tmpSegmentNormals[i] = glm::normalize( SFVEC2F( slope.y, -slope.x ) ); const float length = slope.x * slope.x + slope.y * slope.y; #ifdef PRINT_STATISTICS_3D_VIEWER if( length < minLength ) minLength = length; #endif medOfTheSquaresSegmentLength += length; } #ifdef PRINT_STATISTICS_3D_VIEWER float minSegmentLength = sqrt( minLength ); #endif // This calc an approximation of medium lengths, that will be used to calc // the size of the division. medOfTheSquaresSegmentLength /= segments_and_normals.size(); medOfTheSquaresSegmentLength = sqrt( medOfTheSquaresSegmentLength ); // Compute the normal interpolation // If calculate the dot between the segments, if they are above/below some // threshould it will not interpolated it (ex: if you are in a edge corner // or in a smooth transaction) j = segments_and_normals.size() - 1; for( i = 0; i < segments_and_normals.size(); j = i++ ) { const SFVEC2F normalBeforeSeg = tmpSegmentNormals[j]; const SFVEC2F normalSeg = tmpSegmentNormals[i]; const SFVEC2F normalAfterSeg = tmpSegmentNormals[ (i + 1) % segments_and_normals.size() ]; const float dotBefore = glm::dot( normalBeforeSeg, normalSeg ); const float dotAfter = glm::dot( normalAfterSeg, normalSeg ); if( dotBefore < 0.7f ) segments_and_normals[i].m_Normals.m_Start = normalSeg; else segments_and_normals[i].m_Normals.m_Start = glm::normalize( (((normalBeforeSeg * dotBefore ) + normalSeg) * 0.5f) ); if( dotAfter < 0.7f ) segments_and_normals[i].m_Normals.m_End = normalSeg; else segments_and_normals[i].m_Normals.m_End = glm::normalize( (((normalAfterSeg * dotAfter ) + normalSeg) * 0.5f) ); } if( aDivFactor == 0.0f ) aDivFactor = medOfTheSquaresSegmentLength; SFVEC2UI grid_divisions; grid_divisions.x = (unsigned int)( (bbox.GetExtent().x / aDivFactor) ); grid_divisions.y = (unsigned int)( (bbox.GetExtent().y / aDivFactor) ); grid_divisions = glm::clamp( grid_divisions , SFVEC2UI( 1, 1 ), SFVEC2UI( MAX_NR_DIVISIONS, MAX_NR_DIVISIONS ) ); // Calculate the steps advance of the grid SFVEC2F blockAdvance; blockAdvance.x = bbox.GetExtent().x / (float)grid_divisions.x; blockAdvance.y = bbox.GetExtent().y / (float)grid_divisions.y; wxASSERT( blockAdvance.x > 0.0f ); wxASSERT( blockAdvance.y > 0.0f ); const int leftToRight_inc = (pathBounds.GetRight() - pathBounds.GetLeft()) / grid_divisions.x; const int topToBottom_inc = (pathBounds.GetBottom() - pathBounds.GetTop()) / grid_divisions.y; // Statistics unsigned int stats_n_empty_blocks = 0; unsigned int stats_n_dummy_blocks = 0; unsigned int stats_n_poly_blocks = 0; unsigned int stats_sum_size_of_polygons = 0; // Step by each block of a grid trying to extract segments and create // polygon blocks int topToBottom = pathBounds.GetTop(); float blockY = bbox.Max().y; for( unsigned int iy = 0; iy < grid_divisions.y; iy++ ) { int leftToRight = pathBounds.GetLeft(); float blockX = bbox.Min().x; for( unsigned int ix = 0; ix < grid_divisions.x; ix++ ) { CBBOX2D blockBox( SFVEC2F( blockX, blockY - blockAdvance.y ), SFVEC2F( blockX + blockAdvance.x, blockY ) ); // Make the box large to it will catch (intersect) the edges blockBox.ScaleNextUp(); blockBox.ScaleNextUp(); blockBox.ScaleNextUp(); SEGMENTS_WIDTH_NORMALS extractedSegments; extractPathsFrom( segments_and_normals, blockBox, extractedSegments ); if( extractedSegments.empty() ) { SFVEC2F p1( blockBox.Min().x, blockBox.Min().y ); SFVEC2F p2( blockBox.Max().x, blockBox.Min().y ); SFVEC2F p3( blockBox.Max().x, blockBox.Max().y ); SFVEC2F p4( blockBox.Min().x, blockBox.Max().y ); if( polygon_IsPointInside( segments, p1 ) || polygon_IsPointInside( segments, p2 ) || polygon_IsPointInside( segments, p3 ) || polygon_IsPointInside( segments, p4 ) ) { // In this case, the segments are not intersecting the // polygon, so it means that if any point is inside it, // then all other are inside the polygon. // This is a full bbox inside, so add a dummy box aDstContainer.Add( new CDUMMYBLOCK2D( blockBox, aBoardItem ) ); stats_n_dummy_blocks++; } else { // Points are outside, so this block complety missed the polygon // In this case, no objects need to be added stats_n_empty_blocks++; } } else { // At this point, the borders of polygon were intersected by the // bounding box, so we must calculate a new polygon that will // close that small block. // This block will be used to calculate if points are inside // the (sub block) polygon. SHAPE_POLY_SET subBlockPoly; SHAPE_LINE_CHAIN sb = SHAPE_LINE_CHAIN( VECTOR2I( leftToRight, topToBottom ), VECTOR2I( leftToRight + leftToRight_inc, topToBottom ), VECTOR2I( leftToRight + leftToRight_inc, topToBottom + topToBottom_inc ), VECTOR2I( leftToRight, topToBottom + topToBottom_inc ) ); //sb.Append( leftToRight, topToBottom ); sb.SetClosed( true ); subBlockPoly.AddOutline( sb ); // We need here a strictly simple polygon with outlines and holes SHAPE_POLY_SET solution; solution.BooleanIntersection( aMainPath, subBlockPoly, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); OUTERS_AND_HOLES outersAndHoles; outersAndHoles.m_Holes.clear(); outersAndHoles.m_Outers.clear(); for( int idx = 0; idx < solution.OutlineCount(); idx++ ) { const SHAPE_LINE_CHAIN & outline = solution.Outline( idx ); SEGMENTS solutionSegment; polygon_Convert( outline, solutionSegment, aBiuTo3DunitsScale ); outersAndHoles.m_Outers.push_back( solutionSegment ); stats_sum_size_of_polygons += solutionSegment.size(); for( int holeIdx = 0; holeIdx < solution.HoleCount( idx ); holeIdx++ ) { const SHAPE_LINE_CHAIN & hole = solution.Hole( idx, holeIdx ); polygon_Convert( hole, solutionSegment, aBiuTo3DunitsScale ); outersAndHoles.m_Holes.push_back( solutionSegment ); stats_sum_size_of_polygons += solutionSegment.size(); } } if( !outersAndHoles.m_Outers.empty() ) { aDstContainer.Add( new CPOLYGONBLOCK2D( extractedSegments, outersAndHoles, aBoardItem ) ); stats_n_poly_blocks++; } } blockX += blockAdvance.x; leftToRight += leftToRight_inc; } blockY -= blockAdvance.y; topToBottom += topToBottom_inc; } #ifdef PRINT_STATISTICS_3D_VIEWER printf( "////////////////////////////////////////////////////////////////////////////////\n" ); printf( "Convert_path_polygon_to_polygon_blocks_and_dummy_blocks\n" ); printf( " grid_divisions (%u, %u)\n", grid_divisions.x, grid_divisions.y ); printf( " N Total Blocks %u\n", grid_divisions.x * grid_divisions.y ); printf( " N Empty Blocks %u\n", stats_n_empty_blocks ); printf( " N Dummy Blocks %u\n", stats_n_dummy_blocks ); printf( " N Polyg Blocks %u\n", stats_n_poly_blocks ); printf( " Med N Seg Poly %u\n", stats_sum_size_of_polygons / stats_n_poly_blocks ); printf( " medOfTheSquaresSegmentLength %f\n", medOfTheSquaresSegmentLength ); printf( " minSegmentLength %f\n", minSegmentLength ); printf( " aDivFactor %f\n", aDivFactor ); printf( "////////////////////////////////////////////////////////////////////////////////\n" ); #endif }
bool PNS_LINE::Walkaround( SHAPE_LINE_CHAIN aObstacle, SHAPE_LINE_CHAIN& aPre, SHAPE_LINE_CHAIN& aWalk, SHAPE_LINE_CHAIN& aPost, bool aCw ) const { const SHAPE_LINE_CHAIN& line ( CLine() ); VECTOR2I ip_start; VECTOR2I ip_end; if( line.SegmentCount() < 1 ) return false; if( aObstacle.PointInside( line.CPoint( 0 ) ) || aObstacle.PointInside( line.CPoint( -1 ) ) ) return false; SHAPE_LINE_CHAIN::INTERSECTIONS ips, ips2; line.Intersect( aObstacle, ips ); int nearest_dist = INT_MAX; int farthest_dist = 0; SHAPE_LINE_CHAIN::INTERSECTION nearest, farthest; for( int i = 0; i < (int) ips.size(); i++ ) { const VECTOR2I p = ips[i].p; int dist = line.PathLength( p ); if( dist < 0 ) return false; if( dist <= nearest_dist ) { nearest_dist = dist; nearest = ips[i]; } if( dist >= farthest_dist ) { farthest_dist = dist; farthest = ips[i]; } } if( ips.size() <= 1 || nearest.p == farthest.p ) { aPre = line; return true; } aPre = line.Slice( 0, nearest.our.Index() ); aPre.Append( nearest.p ); aPre.Simplify(); aWalk.Clear(); aWalk.SetClosed( false ); aWalk.Append( nearest.p ); int i = nearest.their.Index(); assert( nearest.their.Index() >= 0 ); assert( farthest.their.Index() >= 0 ); assert( nearest_dist <= farthest_dist ); aObstacle.Split( nearest.p ); aObstacle.Split( farthest.p ); int i_first = aObstacle.Find( nearest.p ); int i_last = aObstacle.Find( farthest.p ); i = i_first; while( i != i_last ) { aWalk.Append( aObstacle.CPoint( i ) ); i += ( aCw ? 1 : -1 ); if( i < 0 ) i = aObstacle.PointCount() - 1; else if( i == aObstacle.PointCount() ) i = 0; } aWalk.Append( farthest.p ); aWalk.Simplify(); aPost.Clear(); aPost.Append( farthest.p ); aPost.Append( line.Slice( farthest.our.Index() + 1, -1 ) ); aPost.Simplify(); return true; }
void SHAPE_POLY_SET::fractureSingle( POLYGON& paths ) { FractureEdgeSet edges; FractureEdgeSet border_edges; FractureEdge* root = NULL; bool first = true; if( paths.size() == 1 ) return; int num_unconnected = 0; for( SHAPE_LINE_CHAIN& path : paths ) { int index = 0; FractureEdge *prev = NULL, *first_edge = NULL; int x_min = std::numeric_limits<int>::max(); for( int i = 0; i < path.PointCount(); i++ ) { const VECTOR2I& p = path.CPoint( i ); if( p.x < x_min ) x_min = p.x; } for( int i = 0; i < path.PointCount(); i++ ) { FractureEdge* fe = new FractureEdge( first, &path, index++ ); if( !root ) root = fe; if( !first_edge ) first_edge = fe; if( prev ) prev->m_next = fe; if( i == path.PointCount() - 1 ) fe->m_next = first_edge; prev = fe; edges.push_back( fe ); if( !first ) { if( fe->m_p1.x == x_min ) border_edges.push_back( fe ); } if( !fe->m_connected ) num_unconnected++; } first = false; // first path is always the outline } // keep connecting holes to the main outline, until there's no holes left... while( num_unconnected > 0 ) { int x_min = std::numeric_limits<int>::max(); FractureEdge* smallestX = NULL; // find the left-most hole edge and merge with the outline for( FractureEdgeSet::iterator i = border_edges.begin(); i != border_edges.end(); ++i ) { int xt = (*i)->m_p1.x; if( ( xt < x_min ) && ! (*i)->m_connected ) { x_min = xt; smallestX = *i; } } num_unconnected -= processEdge( edges, smallestX ); } paths.clear(); SHAPE_LINE_CHAIN newPath; newPath.SetClosed( true ); FractureEdge* e; for( e = root; e->m_next != root; e = e->m_next ) newPath.Append( e->m_p1 ); newPath.Append( e->m_p1 ); for( FractureEdgeSet::iterator i = edges.begin(); i != edges.end(); ++i ) delete *i; paths.push_back( newPath ); }