const SHAPE_POLY_SET ConvertPolyListToPolySet( const CPOLYGONS_LIST& aList ) { SHAPE_POLY_SET rv; unsigned corners_count = aList.GetCornersCount(); // Enter main outline: this is the first contour unsigned ic = 0; if( !corners_count ) return rv; int index = 0; while( ic < corners_count ) { int hole = -1; if( index == 0 ) { rv.NewOutline(); hole = -1; } else { hole = rv.NewHole(); } while( ic < corners_count ) { rv.Append( aList.GetX( ic ), aList.GetY( ic ), 0, hole ); if( aList.IsEndContour( ic ) ) break; ic++; } ic++; index++; } return rv; }
/** * Function ConvertOutlineToPolygon * build a polygon (with holes) from a DRAWSEGMENT list, which is expected to be * a outline, therefore a closed main outline with perhaps closed inner outlines. * These closed inner outlines are considered as holes in the main outline * @param aSegList the initial list of drawsegments (only lines, circles and arcs). * @param aPolygons will contain the complex polygon. * @param aTolerance is the max distance between points that is still accepted as connected (internal units) * @param aErrorText is a wxString to return error message. * @param aErrorLocation is the optional position of the error in the outline */ bool ConvertOutlineToPolygon( std::vector<DRAWSEGMENT*>& aSegList, SHAPE_POLY_SET& aPolygons, wxString* aErrorText, unsigned int aTolerance, wxPoint* aErrorLocation ) { if( aSegList.size() == 0 ) return true; wxString msg; // Make a working copy of aSegList, because the list is modified during calculations std::vector< DRAWSEGMENT* > segList = aSegList; DRAWSEGMENT* graphic; wxPoint prevPt; // Find edge point with minimum x, this should be in the outer polygon // which will define the perimeter Edge.Cuts polygon. wxPoint xmin = wxPoint( INT_MAX, 0 ); int xmini = 0; for( size_t i = 0; i < segList.size(); i++ ) { graphic = (DRAWSEGMENT*) segList[i]; switch( graphic->GetShape() ) { case S_SEGMENT: { if( graphic->GetStart().x < xmin.x ) { xmin = graphic->GetStart(); xmini = i; } if( graphic->GetEnd().x < xmin.x ) { xmin = graphic->GetEnd(); xmini = i; } } break; case S_ARC: // Freerouter does not yet understand arcs, so approximate // an arc with a series of short lines and put those // line segments into the !same! PATH. { wxPoint pstart = graphic->GetArcStart(); wxPoint center = graphic->GetCenter(); double angle = -graphic->GetAngle(); double radius = graphic->GetRadius(); int steps = GetArcToSegmentCount( radius, ARC_LOW_DEF, angle / 10.0 ); wxPoint pt; for( int step = 1; step<=steps; ++step ) { double rotation = ( angle * step ) / steps; pt = pstart; RotatePoint( &pt, center, rotation ); if( pt.x < xmin.x ) { xmin = pt; xmini = i; } } } break; case S_CIRCLE: { wxPoint pt = graphic->GetCenter(); // pt has minimum x point pt.x -= graphic->GetRadius(); // when the radius <= 0, this is a mal-formed circle. Skip it if( graphic->GetRadius() > 0 && pt.x < xmin.x ) { xmin = pt; xmini = i; } } break; case S_CURVE: { graphic->RebuildBezierToSegmentsPointsList( graphic->GetWidth() ); for( unsigned int jj = 0; jj < graphic->GetBezierPoints().size(); jj++ ) { wxPoint pt = graphic->GetBezierPoints()[jj]; if( pt.x < xmin.x ) { xmin = pt; xmini = i; } } } break; case S_POLYGON: { const auto poly = graphic->GetPolyShape(); MODULE* module = aSegList[0]->GetParentModule(); double orientation = module ? module->GetOrientation() : 0.0; VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 ); for( auto iter = poly.CIterate(); iter; iter++ ) { auto pt = *iter; RotatePoint( pt, orientation ); pt += offset; if( pt.x < xmin.x ) { xmin.x = pt.x; xmin.y = pt.y; xmini = i; } } } break; default: break; } } // Grab the left most point, assume its on the board's perimeter, and see if we // can put enough graphics together by matching endpoints to formulate a cohesive // polygon. graphic = (DRAWSEGMENT*) segList[xmini]; // The first DRAWSEGMENT is in 'graphic', ok to remove it from 'items' segList.erase( segList.begin() + xmini ); // Output the Edge.Cuts perimeter as circle or polygon. if( graphic->GetShape() == S_CIRCLE ) { int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_LOW_DEF, 360.0 ); TransformCircleToPolygon( aPolygons, graphic->GetCenter(), graphic->GetRadius(), steps ); } else if( graphic->GetShape() == S_POLYGON ) { MODULE* module = graphic->GetParentModule(); // NULL for items not in footprints double orientation = module ? module->GetOrientation() : 0.0; VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 ); aPolygons.NewOutline(); for( auto it = graphic->GetPolyShape().CIterate( 0 ); it; it++ ) { auto pt = *it; RotatePoint( pt, orientation ); pt += offset; aPolygons.Append( pt ); } } else { // Polygon start point. Arbitrarily chosen end of the // segment and build the poly from here. wxPoint startPt = wxPoint( graphic->GetEnd() ); prevPt = graphic->GetEnd(); aPolygons.NewOutline(); aPolygons.Append( prevPt ); // Do not append the other end point yet of this 'graphic', this first // 'graphic' might be an arc or a curve. for(;;) { switch( graphic->GetShape() ) { case S_SEGMENT: { wxPoint nextPt; // Use the line segment end point furthest away from // prevPt as we assume the other end to be ON prevPt or // very close to it. if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) ) nextPt = graphic->GetEnd(); else nextPt = graphic->GetStart(); aPolygons.Append( nextPt ); prevPt = nextPt; } break; case S_ARC: // We do not support arcs in polygons, so approximate // an arc with a series of short lines and put those // line segments into the !same! PATH. { wxPoint pstart = graphic->GetArcStart(); wxPoint pend = graphic->GetArcEnd(); wxPoint pcenter = graphic->GetCenter(); double angle = -graphic->GetAngle(); double radius = graphic->GetRadius(); int steps = GetArcToSegmentCount( radius, ARC_LOW_DEF, angle / 10.0 ); if( !close_enough( prevPt, pstart, aTolerance ) ) { wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), aTolerance ) ); angle = -angle; std::swap( pstart, pend ); } wxPoint nextPt; for( int step = 1; step<=steps; ++step ) { double rotation = ( angle * step ) / steps; nextPt = pstart; RotatePoint( &nextPt, pcenter, rotation ); aPolygons.Append( nextPt ); } prevPt = nextPt; } break; case S_CURVE: // We do not support Bezier curves in polygons, so approximate // with a series of short lines and put those // line segments into the !same! PATH. { wxPoint nextPt; bool reverse = false; // Use the end point furthest away from // prevPt as we assume the other end to be ON prevPt or // very close to it. if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) ) nextPt = graphic->GetEnd(); else { nextPt = graphic->GetStart(); reverse = true; } if( reverse ) { for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- ) aPolygons.Append( graphic->GetBezierPoints()[jj] ); } else { for( size_t jj = 0; jj < graphic->GetBezierPoints().size(); jj++ ) aPolygons.Append( graphic->GetBezierPoints()[jj] ); } prevPt = nextPt; } break; default: if( aErrorText ) { msg.Printf( "Unsupported DRAWSEGMENT type %s.", BOARD_ITEM::ShowShape( graphic->GetShape() ) ); *aErrorText << msg << "\n"; } if( aErrorLocation ) *aErrorLocation = graphic->GetPosition(); return false; } // Get next closest segment. graphic = findPoint( prevPt, segList, aTolerance ); // If there are no more close segments, check if the board // outline polygon can be closed. if( !graphic ) { if( close_enough( startPt, prevPt, aTolerance ) ) { // Close the polygon back to start point // aPolygons.Append( startPt ); // not needed } else { if( aErrorText ) { msg.Printf( _( "Unable to find segment with an endpoint of (%s, %s)." ), StringFromValue( MILLIMETRES, prevPt.x, true ), StringFromValue( MILLIMETRES, prevPt.y, true ) ); *aErrorText << msg << "\n"; } if( aErrorLocation ) *aErrorLocation = prevPt; return false; } break; } } } while( segList.size() ) { // emit a signal layers keepout for every interior polygon left... int hole = aPolygons.NewHole(); graphic = (DRAWSEGMENT*) segList[0]; segList.erase( segList.begin() ); // Both circles and polygons on the edge cuts layer are closed items that // do not connect to other elements, so we process them independently if( graphic->GetShape() == S_POLYGON ) { MODULE* module = graphic->GetParentModule(); // NULL for items not in footprints double orientation = module ? module->GetOrientation() : 0.0; VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 ); for( auto it = graphic->GetPolyShape().CIterate(); it; it++ ) { auto val = *it; RotatePoint( val, orientation ); val += offset; aPolygons.Append( val, -1, hole ); } } else if( graphic->GetShape() == S_CIRCLE ) { // make a circle by segments; wxPoint center = graphic->GetCenter(); double angle = 3600.0; wxPoint start = center; int radius = graphic->GetRadius(); int steps = GetArcToSegmentCount( radius, ARC_LOW_DEF, 360.0 ); wxPoint nextPt; start.x += radius; for( int step = 0; step < steps; ++step ) { double rotation = ( angle * step ) / steps; nextPt = start; RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y, rotation ); aPolygons.Append( nextPt, -1, hole ); } } else { // Polygon start point. Arbitrarily chosen end of the // segment and build the poly from here. wxPoint startPt( graphic->GetEnd() ); prevPt = graphic->GetEnd(); aPolygons.Append( prevPt, -1, hole ); // do not append the other end point yet, this first 'graphic' might be an arc for(;;) { switch( graphic->GetShape() ) { case S_SEGMENT: { wxPoint nextPt; // Use the line segment end point furthest away from // prevPt as we assume the other end to be ON prevPt or // very close to it. if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) ) { nextPt = graphic->GetEnd(); } else { nextPt = graphic->GetStart(); } prevPt = nextPt; aPolygons.Append( prevPt, -1, hole ); } break; case S_ARC: // Freerouter does not yet understand arcs, so approximate // an arc with a series of short lines and put those // line segments into the !same! PATH. { wxPoint pstart = graphic->GetArcStart(); wxPoint pend = graphic->GetArcEnd(); wxPoint pcenter = graphic->GetCenter(); double angle = -graphic->GetAngle(); int radius = graphic->GetRadius(); int steps = GetArcToSegmentCount( radius, ARC_LOW_DEF, angle / 10.0 ); if( !close_enough( prevPt, pstart, aTolerance ) ) { wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), aTolerance ) ); angle = -angle; std::swap( pstart, pend ); } wxPoint nextPt; for( int step = 1; step <= steps; ++step ) { double rotation = ( angle * step ) / steps; nextPt = pstart; RotatePoint( &nextPt, pcenter, rotation ); aPolygons.Append( nextPt, -1, hole ); } prevPt = nextPt; } break; case S_CURVE: // We do not support Bezier curves in polygons, so approximate // with a series of short lines and put those // line segments into the !same! PATH. { wxPoint nextPt; bool reverse = false; // Use the end point furthest away from // prevPt as we assume the other end to be ON prevPt or // very close to it. if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) ) nextPt = graphic->GetEnd(); else { nextPt = graphic->GetStart(); reverse = true; } if( reverse ) { for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- ) aPolygons.Append( graphic->GetBezierPoints()[jj], -1, hole ); } else { for( size_t jj = 0; jj < graphic->GetBezierPoints().size(); jj++ ) aPolygons.Append( graphic->GetBezierPoints()[jj], -1, hole ); } prevPt = nextPt; } break; default: if( aErrorText ) { msg.Printf( "Unsupported DRAWSEGMENT type %s.", BOARD_ITEM::ShowShape( graphic->GetShape() ) ); *aErrorText << msg << "\n"; } if( aErrorLocation ) *aErrorLocation = graphic->GetPosition(); return false; } // Get next closest segment. graphic = findPoint( prevPt, segList, aTolerance ); // If there are no more close segments, check if polygon // can be closed. if( !graphic ) { if( close_enough( startPt, prevPt, aTolerance ) ) { // Close the polygon back to start point // aPolygons.Append( startPt, -1, hole ); // not needed } else { if( aErrorText ) { msg.Printf( _( "Unable to find segment with an endpoint of (%s, %s)." ), StringFromValue( MILLIMETRES, prevPt.x, true ), StringFromValue( MILLIMETRES, prevPt.y, true ) ); *aErrorText << msg << "\n"; } if( aErrorLocation ) *aErrorLocation = prevPt; return false; } break; } } } } return true; }