SHAPE_LINE_CHAIN dragCornerInternal( const SHAPE_LINE_CHAIN& aOrigin, const VECTOR2I& aP ) { optional<SHAPE_LINE_CHAIN> picked; int i; int d = 2; if( aOrigin.CSegment( -1 ).Length() > 100000 * 30 ) // fixme: constant/parameter? d = 1; for( i = aOrigin.SegmentCount() - d; i >= 0; i-- ) { DIRECTION_45 d_start ( aOrigin.CSegment( i ) ); VECTOR2I p_start = aOrigin.CPoint( i ); SHAPE_LINE_CHAIN paths[2]; DIRECTION_45 dirs[2]; DIRECTION_45 d_prev = ( i > 0 ? DIRECTION_45( aOrigin.CSegment( i - 1 ) ) : DIRECTION_45() ); for( int j = 0; j < 2; j++ ) { paths[j] = d_start.BuildInitialTrace( p_start, aP, j ); dirs[j] = DIRECTION_45( paths[j].CSegment( 0 ) ); } for( int j = 0; j < 2; j++ ) { if( dirs[j] == d_start ) { picked = paths[j]; break; } } if( picked ) break; for( int j = 0; j < 2; j++ ) { if( dirs[j].IsObtuse( d_prev ) ) { picked = paths[j]; break; } } if( picked ) break; } if( picked ) { SHAPE_LINE_CHAIN path = aOrigin.Slice( 0, i ); path.Append( *picked ); return path; } return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP, true ); }
static void polygon_Convert( const SHAPE_LINE_CHAIN &aPath, SEGMENTS &aOutSegment, float aBiuTo3DunitsScale ) { aOutSegment.resize( aPath.PointCount() ); for( int j = 0; j < aPath.PointCount(); j++ ) { const VECTOR2I &a = aPath.CPoint( j ); aOutSegment[j].m_Start = SFVEC2F( (float) a.x * aBiuTo3DunitsScale, (float)-a.y * aBiuTo3DunitsScale ); } unsigned int i; unsigned int j = aOutSegment.size () - 1; for( i = 0; i < aOutSegment.size (); j = i++ ) { // Calculate constants for each segment aOutSegment[i].m_inv_JY_minus_IY = 1.0f / ( aOutSegment[j].m_Start.y - aOutSegment[i].m_Start.y ); aOutSegment[i].m_JX_minus_IX = (aOutSegment[j].m_Start.x - aOutSegment[i].m_Start.x); } }
bool PNS_LINE_PLACER::buildInitialLine( const VECTOR2I& aP, PNS_LINE& aHead ) { SHAPE_LINE_CHAIN l; if( m_p_start == aP ) { l.Clear(); } else { if( Settings().GetFreeAngleMode() && Settings().Mode() == RM_MarkObstacles ) { l = SHAPE_LINE_CHAIN( m_p_start, aP ); } else { l = m_direction.BuildInitialTrace( m_p_start, aP ); } if( l.SegmentCount() > 1 && m_orthoMode ) { VECTOR2I newLast = l.CSegment( 0 ).LineProject( l.CPoint( -1 ) ); l.Remove( -1, -1 ); l.Point( 1 ) = newLast; } } aHead.SetShape( l ); if( !m_placingVia ) return true; PNS_VIA v( makeVia( aP ) ); v.SetNet( aHead.Net() ); if( m_currentMode == RM_MarkObstacles ) { aHead.AppendVia( v ); return true; } VECTOR2I force; VECTOR2I lead = aP - m_p_start; bool solidsOnly = ( m_currentMode != RM_Walkaround ); if( v.PushoutForce( m_currentNode, lead, force, solidsOnly, 40 ) ) { SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace( m_p_start, aP + force ); aHead = PNS_LINE( aHead, line ); v.SetPos( v.Pos() + force ); return true; } return false; // via placement unsuccessful }
const ClipperLib::Path SHAPE_POLY_SET::convert( const SHAPE_LINE_CHAIN& aPath ) { Path c_path; for(int i = 0; i < aPath.PointCount(); i++) { const VECTOR2I& vertex = aPath.CPoint(i); c_path.push_back(ClipperLib::IntPoint ( vertex.x, vertex.y ) ); } return c_path; }
void CLAYER_TRIANGLES::AddToMiddleContourns( const SHAPE_LINE_CHAIN &outlinePath, float zBot, float zTop, double aBiuTo3Du, bool aInvertFaceDirection ) { std::vector< SFVEC2F >contournPoints; contournPoints.clear(); contournPoints.reserve( outlinePath.PointCount() + 2 ); const VECTOR2I &firstV = outlinePath.CPoint( 0 ); SFVEC2F lastV = SFVEC2F( firstV.x * aBiuTo3Du, -firstV.y * aBiuTo3Du ); contournPoints.push_back( lastV ); for( unsigned int i = 1; i < (unsigned int)outlinePath.PointCount(); ++i ) { const VECTOR2I & v = outlinePath.CPoint( i ); const SFVEC2F vf = SFVEC2F( v.x * aBiuTo3Du, -v.y * aBiuTo3Du ); if( vf != lastV ) // Do not add repeated points { lastV = vf; contournPoints.push_back( vf ); } } // Add first position fo the list to close the path if( lastV != contournPoints[0] ) contournPoints.push_back( contournPoints[0] ); AddToMiddleContourns( contournPoints, zBot, zTop, aInvertFaceDirection ); }
void PNS_MEANDER_SHAPE::arc( int aRadius, bool aSide ) { if( aRadius <= 0 ) { turn( aSide ? -90 : 90 ); return; } VECTOR2D dir = m_currentDir.Resize( (double) aRadius ); SHAPE_LINE_CHAIN arc = circleQuad( m_currentPos, dir, aSide ); m_currentPos = arc.CPoint( -1 ); m_currentDir = dir.Rotate( aSide ? -M_PI / 2.0 : M_PI / 2.0 ); m_currentTarget->Append ( arc ); }
const Path SHAPE_POLY_SET::convertToClipper( const SHAPE_LINE_CHAIN& aPath, bool aRequiredOrientation ) { Path c_path; for( int i = 0; i < aPath.PointCount(); i++ ) { const VECTOR2I& vertex = aPath.CPoint( i ); c_path.push_back( IntPoint( vertex.x, vertex.y ) ); } if( Orientation( c_path ) != aRequiredOrientation ) ReversePath( c_path ); return c_path; }
SHAPE_LINE_CHAIN PNS_MEANDER_SHAPE::genMeanderShape( VECTOR2D aP, VECTOR2D aDir, bool aSide, PNS_MEANDER_TYPE aType, int aAmpl, int aBaselineOffset ) { const PNS_MEANDER_SETTINGS& st = Settings(); int cr = cornerRadius(); int offset = aBaselineOffset; int spc = spacing(); if( aSide ) offset *= -1; VECTOR2D dir_u_b( aDir.Resize( offset ) ); VECTOR2D dir_v_b( dir_u_b.Perpendicular() ); if( 2 * cr > aAmpl ) { cr = aAmpl / 2; } if( 2 * cr > spc ) { cr = spc / 2; } SHAPE_LINE_CHAIN lc; start( &lc, aP + dir_v_b, aDir ); switch( aType ) { case MT_EMPTY: { lc.Append( aP + dir_v_b + aDir ); break; } case MT_START: { arc( cr - offset, false ); uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr ); forward( std::min( cr - offset, cr + offset ) ); forward( std::abs( offset ) ); break; } case MT_FINISH: { start( &lc, aP - dir_u_b, aDir ); turn ( 90 ); forward( std::min( cr - offset, cr + offset ) ); forward( std::abs( offset ) ); uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr ); arc( cr - offset, false ); break; } case MT_TURN: { start( &lc, aP - dir_u_b, aDir ); turn( 90 ); forward( std::abs( offset ) ); uShape ( aAmpl - cr, cr + offset, spc - 2 * cr ); forward( std::abs( offset ) ); break; } case MT_SINGLE: { arc( cr - offset, false ); uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr ); arc( cr - offset, false ); lc.Append( aP + dir_v_b + aDir.Resize ( 2 * st.m_spacing ) ); break; } default: break; } if( aSide ) { SEG axis ( aP, aP + aDir ); for( int i = 0; i < lc.PointCount(); i++ ) lc.Point( i ) = reflect( lc.CPoint( i ), axis ); } return lc; }
void ZONE_FILLER::buildUnconnectedThermalStubsPolygonList( SHAPE_POLY_SET& aCornerBuffer, const ZONE_CONTAINER* aZone, const SHAPE_POLY_SET& aRawFilledArea, double aArcCorrection, double aRoundPadThermalRotation ) const { SHAPE_LINE_CHAIN spokes; BOX2I itemBB; VECTOR2I ptTest[4]; auto zoneBB = aRawFilledArea.BBox(); int zone_clearance = aZone->GetZoneClearance(); int biggest_clearance = m_board->GetDesignSettings().GetBiggestClearanceValue(); biggest_clearance = std::max( biggest_clearance, zone_clearance ); zoneBB.Inflate( biggest_clearance ); // half size of the pen used to draw/plot zones outlines int pen_radius = aZone->GetMinThickness() / 2; for( auto module : m_board->Modules() ) { for( auto pad : module->Pads() ) { // Rejects non-standard pads with tht-only thermal reliefs if( aZone->GetPadConnection( pad ) == PAD_ZONE_CONN_THT_THERMAL && pad->GetAttribute() != PAD_ATTRIB_STANDARD ) continue; if( aZone->GetPadConnection( pad ) != PAD_ZONE_CONN_THERMAL && aZone->GetPadConnection( pad ) != PAD_ZONE_CONN_THT_THERMAL ) continue; if( !pad->IsOnLayer( aZone->GetLayer() ) ) continue; if( pad->GetNetCode() != aZone->GetNetCode() ) continue; // Calculate thermal bridge half width int thermalBridgeWidth = aZone->GetThermalReliefCopperBridge( pad ) - aZone->GetMinThickness(); if( thermalBridgeWidth <= 0 ) continue; // we need the thermal bridge half width // with a small extra size to be sure we create a stub // slightly larger than the actual stub thermalBridgeWidth = ( thermalBridgeWidth + 4 ) / 2; int thermalReliefGap = aZone->GetThermalReliefGap( pad ); itemBB = pad->GetBoundingBox(); itemBB.Inflate( thermalReliefGap ); if( !( itemBB.Intersects( zoneBB ) ) ) continue; // Thermal bridges are like a segment from a starting point inside the pad // to an ending point outside the pad // calculate the ending point of the thermal pad, outside the pad VECTOR2I endpoint; endpoint.x = ( pad->GetSize().x / 2 ) + thermalReliefGap; endpoint.y = ( pad->GetSize().y / 2 ) + thermalReliefGap; // Calculate the starting point of the thermal stub // inside the pad VECTOR2I startpoint; int copperThickness = aZone->GetThermalReliefCopperBridge( pad ) - aZone->GetMinThickness(); if( copperThickness < 0 ) copperThickness = 0; // Leave a small extra size to the copper area inside to pad copperThickness += KiROUND( IU_PER_MM * 0.04 ); startpoint.x = std::min( pad->GetSize().x, copperThickness ); startpoint.y = std::min( pad->GetSize().y, copperThickness ); startpoint.x /= 2; startpoint.y /= 2; // This is a CIRCLE pad tweak // for circle pads, the thermal stubs orientation is 45 deg double fAngle = pad->GetOrientation(); if( pad->GetShape() == PAD_SHAPE_CIRCLE ) { endpoint.x = KiROUND( endpoint.x * aArcCorrection ); endpoint.y = endpoint.x; fAngle = aRoundPadThermalRotation; } // contour line width has to be taken into calculation to avoid "thermal stub bleed" endpoint.x += pen_radius; endpoint.y += pen_radius; // compute north, south, west and east points for zone connection. ptTest[0] = VECTOR2I( 0, endpoint.y ); // lower point ptTest[1] = VECTOR2I( 0, -endpoint.y ); // upper point ptTest[2] = VECTOR2I( endpoint.x, 0 ); // right point ptTest[3] = VECTOR2I( -endpoint.x, 0 ); // left point // Test all sides for( int i = 0; i < 4; i++ ) { // rotate point RotatePoint( ptTest[i], fAngle ); // translate point ptTest[i] += pad->ShapePos(); if( aRawFilledArea.Contains( ptTest[i] ) ) continue; spokes.Clear(); // polygons are rectangles with width of copper bridge value switch( i ) { case 0: // lower stub spokes.Append( -thermalBridgeWidth, endpoint.y ); spokes.Append( +thermalBridgeWidth, endpoint.y ); spokes.Append( +thermalBridgeWidth, startpoint.y ); spokes.Append( -thermalBridgeWidth, startpoint.y ); break; case 1: // upper stub spokes.Append( -thermalBridgeWidth, -endpoint.y ); spokes.Append( +thermalBridgeWidth, -endpoint.y ); spokes.Append( +thermalBridgeWidth, -startpoint.y ); spokes.Append( -thermalBridgeWidth, -startpoint.y ); break; case 2: // right stub spokes.Append( endpoint.x, -thermalBridgeWidth ); spokes.Append( endpoint.x, thermalBridgeWidth ); spokes.Append( +startpoint.x, thermalBridgeWidth ); spokes.Append( +startpoint.x, -thermalBridgeWidth ); break; case 3: // left stub spokes.Append( -endpoint.x, -thermalBridgeWidth ); spokes.Append( -endpoint.x, thermalBridgeWidth ); spokes.Append( -startpoint.x, thermalBridgeWidth ); spokes.Append( -startpoint.x, -thermalBridgeWidth ); break; } aCornerBuffer.NewOutline(); // add computed polygon to list for( int ic = 0; ic < spokes.PointCount(); ic++ ) { auto cpos = spokes.CPoint( ic ); RotatePoint( cpos, fAngle ); // Rotate according to module orientation cpos += pad->ShapePos(); // Shift origin to position aCornerBuffer.Append( cpos ); } } } } }
/** * Function OuputOnePolygon * write one polygon to output file. * Polygon coordinates are expected scaled by the polygon extraction function */ void BITMAPCONV_INFO::OuputOnePolygon( SHAPE_LINE_CHAIN & aPolygon, const char* aBrdLayerName ) { int ii, jj; VECTOR2I currpoint; int offsetX = (int)( m_PixmapWidth / 2 * m_ScaleX ); int offsetY = (int)( m_PixmapHeight / 2 * m_ScaleY ); const VECTOR2I startpoint = aPolygon.CPoint( 0 ); switch( m_Format ) { case POSTSCRIPT_FMT: offsetY = (int)( m_PixmapHeight * m_ScaleY ); fprintf( m_Outfile, "newpath\n%d %d moveto\n", startpoint.x, offsetY - startpoint.y ); jj = 0; for( ii = 1; ii < aPolygon.PointCount(); ii++ ) { currpoint = aPolygon.CPoint( ii ); fprintf( m_Outfile, " %d %d lineto", currpoint.x, offsetY - currpoint.y ); if( jj++ > 6 ) { jj = 0; fprintf( m_Outfile, ("\n") ); } } fprintf( m_Outfile, "\nclosepath fill\n" ); break; case PCBNEW_KICAD_MOD: { double width = 0.01; // outline thickness in mm fprintf( m_Outfile, " (fp_poly (pts" ); jj = 0; for( ii = 0; ii < aPolygon.PointCount(); ii++ ) { currpoint = aPolygon.CPoint( ii ); fprintf( m_Outfile, " (xy %f %f)", ( currpoint.x - offsetX ) / 1e6, ( currpoint.y - offsetY ) / 1e6 ); if( jj++ > 6 ) { jj = 0; fprintf( m_Outfile, ("\n ") ); } } // Close polygon fprintf( m_Outfile, " (xy %f %f) )", ( startpoint.x - offsetX ) / 1e6, ( startpoint.y - offsetY ) / 1e6 ); fprintf( m_Outfile, "(layer %s) (width %f)\n )\n", aBrdLayerName, width ); } break; case KICAD_LOGO: fprintf( m_Outfile, " (pts" ); // Internal units = micron, file unit = mm jj = 0; for( ii = 0; ii < aPolygon.PointCount(); ii++ ) { currpoint = aPolygon.CPoint( ii ); fprintf( m_Outfile, " (xy %.3f %.3f)", ( currpoint.x - offsetX ) / 1e3, ( currpoint.y - offsetY ) / 1e3 ); if( jj++ > 4 ) { jj = 0; fprintf( m_Outfile, ("\n ") ); } } // Close polygon fprintf( m_Outfile, " (xy %.3f %.3f) )\n", ( startpoint.x - offsetX ) / 1e3, ( startpoint.y - offsetY ) / 1e3 ); break; case EESCHEMA_FMT: fprintf( m_Outfile, "P %d 0 0 1", (int) aPolygon.PointCount() + 1 ); for( ii = 0; ii < aPolygon.PointCount(); ii++ ) { currpoint = aPolygon.CPoint( ii ); fprintf( m_Outfile, " %d %d", currpoint.x - offsetX, currpoint.y - offsetY ); } // Close polygon fprintf( m_Outfile, " %d %d", startpoint.x - offsetX, startpoint.y - offsetY ); fprintf( m_Outfile, " F\n" ); break; } }
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; }
bool SHAPE_POLY_SET::pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aPath ) const { int result = 0; int cnt = aPath.PointCount(); if ( !aPath.BBox().Contains( aP ) ) // test with bounding box first return false; if( cnt < 3 ) return false; VECTOR2I ip = aPath.CPoint( 0 ); for( int i = 1; i <= cnt; ++i ) { VECTOR2I ipNext = ( i == cnt ? aPath.CPoint( 0 ) : aPath.CPoint( i ) ); if( ipNext.y == aP.y ) { if( ( ipNext.x == aP.x ) || ( ip.y == aP.y && ( ( ipNext.x > aP.x ) == ( ip.x < aP.x ) ) ) ) return true; } if( ( ip.y < aP.y ) != ( ipNext.y < aP.y ) ) { if( ip.x >= aP.x ) { if( ipNext.x > aP.x ) result = 1 - result; else { int64_t d = (int64_t)( ip.x - aP.x ) * (int64_t)( ipNext.y - aP.y ) - (int64_t)( ipNext.x - aP.x ) * (int64_t)( ip.y - aP.y ); if( !d ) return true; if( ( d > 0 ) == ( ipNext.y > ip.y ) ) result = 1 - result; } } else { if( ipNext.x > aP.x ) { int64_t d = (int64_t)( ip.x - aP.x ) * (int64_t)( ipNext.y - aP.y ) - (int64_t)( ipNext.x - aP.x ) * (int64_t)( ip.y - aP.y ); if( !d ) return true; if( ( d > 0 ) == ( ipNext.y > ip.y ) ) result = 1 - result; } } } ip = ipNext; } return result ? true : false; }