Loading pcbnew/class_teardrop.cpp +223 −135 Original line number Diff line number Diff line Loading @@ -11,54 +11,69 @@ TEARDROP::TEARDROP() m_type = TEARDROP_NONE; } bool TEARDROP::Create(TRACK &aTrack, ENDPOINT_T endPoint, TEARDROP_TYPE type = TEARDROP_STRAIGHT) bool TEARDROP::Create(TRACK& aTrack, ENDPOINT_T aEndPoint, TEARDROP_TYPE aType = TEARDROP_STRAIGHT ) { bool result = false; BOARD_CONNECTED_ITEM *anObject = GetObjectOnEnd(aTrack, endPoint); BOARD_CONNECTED_ITEM* object = getObjectOnEnd( aTrack, aEndPoint ); VIA* aVia = NULL; if (anObject == NULL) { if( object == NULL ) { return false; } else { switch (anObject->Type()) { else { switch( object->Type() ) { case PCB_VIA_T: aVia = dynamic_cast<VIA *>(anObject); aVia = dynamic_cast<VIA*>( object ); break; case PCB_PAD_T: aVia = new VIA( NULL ); aVia->SetLayer(anObject->GetLayer()); aVia->SetPosition(anObject->GetPosition()); aVia->SetWidth(2 * dynamic_cast<D_PAD *>(anObject)->GetBoundingRadius()); aVia->SetLayer( object->GetLayer() ); aVia->SetPosition( object->GetPosition() ); aVia->SetWidth( 2 * dynamic_cast<D_PAD*>( object )->GetBoundingRadius() ); break; default: break; } } if (type == TEARDROP_STRAIGHT) { result = StraightSegments(aTrack, *aVia, 100); if( aType == TEARDROP_STRAIGHT ) { result = straightSegments( aTrack, *aVia, 100 ); } else if (type == TEARDROP_CURVED) { result = CurvedSegments(aTrack, *aVia); else if( aType == TEARDROP_CURVED ) { result = curvedSegments( aTrack, *aVia ); } return result; } bool TEARDROP::SetVector(TRACK &aTrack, const VIA & aVia, VECTOR2I &startPoint, VECTOR2I &endPoint) bool TEARDROP::setVector(TRACK& aTrack, const VIA& aVia, VECTOR2I& aStartPoint, VECTOR2I& aEndPoint ) { // Decide which end of the track is inside via and set this point as end of vector STATUS_FLAGS status = aTrack.IsPointOnEnds( aVia.GetPosition(), aVia.GetWidth() / 2 ); if (status == STARTPOINT) { startPoint = aTrack.GetEnd(); endPoint = aTrack.GetStart(); if( status == STARTPOINT ) { aStartPoint = aTrack.GetEnd(); aEndPoint = aTrack.GetStart(); } else if (status == ENDPOINT) { startPoint = aTrack.GetStart(); endPoint = aTrack.GetEnd(); else if( status == ENDPOINT ) { aStartPoint = aTrack.GetStart(); aEndPoint = aTrack.GetEnd(); } else { else { // The via is too far from any end or the track is too short return false; } Loading @@ -66,21 +81,24 @@ bool TEARDROP::SetVector(TRACK &aTrack, const VIA & aVia, VECTOR2I &startPoint, return true; } bool TEARDROP::CurvedSegments(TRACK &aTrack, const VIA &aVia) bool TEARDROP::curvedSegments( TRACK& aTrack, const VIA& aVia ) { VECTOR2I startPoint( 0, 0 ); VECTOR2I endPoint( 0, 0 ); std::vector<VECTOR2I> upperSegment; std::vector<VECTOR2I> lowerSegment; if ( !SetVector(aTrack, aVia, startPoint, endPoint) ) { if( !setVector( aTrack, aVia, startPoint, endPoint ) ) { return false; } // Check that the track is not too short double segOutsideVia = aTrack.GetLength() - (aVia.GetWidth() / 2); double minLength = (150 * aVia.GetWidth() / 2) / 100; if (segOutsideVia < minLength) { if( segOutsideVia < minLength ) { return false; } Loading @@ -88,65 +106,94 @@ bool TEARDROP::CurvedSegments(TRACK &aTrack, const VIA &aVia) VECTOR2I viaCenter( aVia.GetPosition().x, aVia.GetPosition().y ); VECTOR2I apertureUpper( 0, 0 ); VECTOR2I apertureLower( 0, 0 ); double radius = (aVia.GetWidth() / 2) - (aTrack.GetWidth() / 2); double rotationAngle = VECTOR2I( startPoint - endPoint ).Angle(); // Calculate the segments of deltoid composing the outline of a teardrop for ( int i = 10; i <= 60; i = i + 10 ) { PointOnCurve(i, radius, point); for( int i = 0; i <= 60; i = i + 10 ) { pointOnCurve( i, radius, point ); point = point.Rotate( rotationAngle ); point += viaCenter; m_coordinates.push_back( point ); if (i == 50) { if( i == 50 ) { apertureUpper = point; } } for ( int i = 300; i <= 350; i = i + 10 ) { PointOnCurve(i, radius, point); for( int i = 300; i <= 360; i = i + 10 ) { pointOnCurve( i, radius, point ); point = point.Rotate( rotationAngle ); point += viaCenter; m_coordinates.push_back( point ); if (i == 340) { if( i == 340 ) { apertureLower = point; } } // Calculate the number of segments needed to fill the area inside the teardrop if (aVia.GetWidth() / 2 > 2 * aTrack.GetWidth()) { if( aVia.GetWidth() / 2 > 2 * aTrack.GetWidth() ) { // First, calculate the distance between two points on both sides of the track and // number of iterations required to fill the zone SEG aperture( apertureUpper, apertureLower ); int numSegments = aperture.Length() / aTrack.GetWidth(); int delta = radius / numSegments; for (int iteration = 0; iteration < numSegments; iteration++) { // Second, fill the inward teardrop area for( int iteration = 0; iteration < numSegments; iteration++ ) { radius = radius - delta; for ( int i = 10; i <= 60; i = i + 10 ) { PointOnCurve(i, radius, point); for( int i = 10; i <= 60; i = i + 10 ) { pointOnCurve( i, radius, point ); point = point.Rotate( rotationAngle ); point += viaCenter; if (i == 10) { // Stop calculations in case the coordinates are inside the via if( i == 10 ) { int distance = SEG( viaCenter, point ).Length(); if (distance < aVia.GetWidth() / 2) { if( distance < aVia.GetWidth() / 2 ) { break; } } m_coordinates.push_back( point ); } lowerSegment.clear(); for ( int i = 350; i >= 300; i = i - 10 ) { PointOnCurve(i, radius, point); for( int i = 350; i >= 300; i = i - 10 ) { pointOnCurve( i, radius, point ); point = point.Rotate( rotationAngle ); point += viaCenter; if (i == 350) { // Stop calculations in case the coordinates are inside the via if( i == 350 ) { int distance = SEG( viaCenter, point ).Length(); if (distance < aVia.GetWidth() / 2) { if( distance < aVia.GetWidth() / 2 ) { break; } } lowerSegment.push_back( point ); } // Revert coordinates order for (std::vector<VECTOR2I>::reverse_iterator iter = lowerSegment.rbegin(); iter != lowerSegment.rend(); ++iter) { // Revert coordinates order. This is necessary to create tracks in correct order later on for( std::vector<VECTOR2I>::reverse_iterator iter = lowerSegment.rbegin(); iter != lowerSegment.rend(); ++iter ) { m_coordinates.push_back( *iter ); } } Loading @@ -155,27 +202,32 @@ bool TEARDROP::CurvedSegments(TRACK &aTrack, const VIA &aVia) return true; } bool TEARDROP::StraightSegments(TRACK &aTrack, const VIA &aVia, int distance = 100) bool TEARDROP::straightSegments(TRACK& aTrack, const VIA& aVia, int aDistance = 100 ) { VECTOR2I startPoint( 0, 0 ); VECTOR2I endPoint( 0, 0 ); VECTOR2I viaCenter( aVia.GetPosition().x, aVia.GetPosition().y ); if ( !SetVector(aTrack, aVia, startPoint, endPoint) ) { if( !setVector( aTrack, aVia, startPoint, endPoint ) ) { return false; } // Check that the track is not too short double segOutsideVia = aTrack.GetLength() - (aVia.GetWidth() / 2); double minLength = (distance * aVia.GetWidth() / 2) / 100; if (segOutsideVia < minLength) { double minLength = (aDistance * aVia.GetWidth() / 2) / 100; if( segOutsideVia < minLength ) { return false; } // Equation coefficients double r = (aVia.GetWidth() / 2) + ((distance * aVia.GetWidth()) / (2 *100)); double r = (aVia.GetWidth() / 2) + ( (aDistance * aVia.GetWidth()) / (2 * 100) ); double a = pow( (endPoint.x - startPoint.x), 2 ) + pow( (endPoint.y - startPoint.y), 2 ); double b = 2 * (double)(endPoint.x - startPoint.x) * (double)(startPoint.x - viaCenter.x) + 2 * (double)(endPoint.y - startPoint.y) * (double)(startPoint.y - viaCenter.y); double b = 2 * (double)(endPoint.x - startPoint.x) * (double)(startPoint.x - viaCenter.x) + 2 * (double)(endPoint.y - startPoint.y) * (double)(startPoint.y - viaCenter.y); double c = pow( (startPoint.x - viaCenter.x), 2 ) + pow( (startPoint.y - viaCenter.y), 2 ) - pow( r, 2 ); double t = 2 * c / (-b + sqrt( b * b - 4 * a * c)); Loading @@ -199,30 +251,36 @@ bool TEARDROP::StraightSegments(TRACK &aTrack, const VIA &aVia, int distance = 1 // Calculate the number of segments needed to fill the area inside the teardrop std::vector<VECTOR2I> splitPoints; if (aVia.GetWidth() / 2 > 2 * aTrack.GetWidth()) { if( aVia.GetWidth() / 2 > 2 * aTrack.GetWidth() ) { // First, calculate the intersection point of the circle and one hand of the teardrop r = aVia.GetWidth() / 2; a = pow( (upperPoint.x - startPoint.x), 2 ) + pow( (upperPoint.y - startPoint.y), 2 ); b = 2 * (double)(upperPoint.x - startPoint.x) * (double)(startPoint.x - viaCenter.x) + 2 * (double)(upperPoint.y - startPoint.y) * (double)(startPoint.y - viaCenter.y); b = 2 * (double)(upperPoint.x - startPoint.x) * (double)(startPoint.x - viaCenter.x) + 2 * (double)(upperPoint.y - startPoint.y) * (double) (startPoint.y - viaCenter.y); c = pow( (startPoint.x - viaCenter.x), 2 ) + pow( (startPoint.y - viaCenter.y), 2 ) - pow( r, 2 ); t = 2 * c / ( -b + sqrt( b * b - 4 * a * c ) ); x = (upperPoint.x - startPoint.x) * t + startPoint.x; y = (upperPoint.y - startPoint.y) * t + startPoint.y; VECTOR2I intersectionPoint( (int) x, (int) y ); // Second, calculate the distance between the given track and the intersection point SEG trackSegment(aTrack.GetStart().x, aTrack.GetStart().y, aTrack.GetEnd().x, aTrack.GetEnd().y); SEG trackSegment( aTrack.GetStart().x, aTrack.GetStart().y, aTrack.GetEnd().x, aTrack.GetEnd().y ); int dist = trackSegment.LineDistance( intersectionPoint ); int numSegments = 2 * dist / aTrack.GetWidth(); // Third, subdivide the diameter of the via and build additional segments SEG segDiameter = SEG( upperPoint, lowerPoint ); SplitSegment(segDiameter, numSegments, splitPoints); splitSegment( segDiameter, numSegments, splitPoints ); } std::list<VECTOR2I> outlinePoints; outlinePoints.push_back( upperPoint ); for (size_t i = 0; i < splitPoints.size(); i++) { for( size_t i = 0; i < splitPoints.size(); i++ ) { outlinePoints.push_back( splitPoints[i] ); } outlinePoints.push_back( lowerPoint ); Loading @@ -230,30 +288,39 @@ bool TEARDROP::StraightSegments(TRACK &aTrack, const VIA &aVia, int distance = 1 // Biuld triangles filling the teardrop int vertexNum = 0; std::list<VECTOR2I>::iterator iter = outlinePoints.begin(); while ( iter != outlinePoints.end() ) { switch (vertexNum) { while( iter != outlinePoints.end() ) { switch( vertexNum ) { case 0: m_coordinates.push_back( linePoint ); vertexNum++; break; case 1: m_coordinates.push_back( *iter ); vertexNum++; iter++; break; case 2: m_coordinates.push_back( *iter ); vertexNum = 0; iter++; break; default:break; default: break; } } // Append additional vertexies in order to finish last triangle if (vertexNum == 0) { if( vertexNum == 0 ) { m_coordinates.push_back( linePoint ); } else if (vertexNum == 2) { else if( vertexNum == 2 ) { m_coordinates.push_back( m_coordinates[m_coordinates.size() - 3] ); m_coordinates.push_back( linePoint ); } Loading @@ -261,34 +328,47 @@ bool TEARDROP::StraightSegments(TRACK &aTrack, const VIA &aVia, int distance = 1 return true; } // TODO: m_TracksConnected member is considered a temporary storage. Find another way to get an object BOARD_CONNECTED_ITEM* TEARDROP::GetObjectOnEnd(TRACK &aTrack, ENDPOINT_T endPoint) BOARD_CONNECTED_ITEM* TEARDROP::getObjectOnEnd(TRACK& aTrack, ENDPOINT_T aEndPoint ) { wxPoint trackPoint; BOARD_CONNECTED_ITEM* item = NULL; std::vector<TRACK*>::const_iterator iter; if (endPoint == ENDPOINT_START) { if( aEndPoint == ENDPOINT_START ) { trackPoint = aTrack.GetStart(); } else { else { trackPoint = aTrack.GetEnd(); } // Check for vias first for (iter = aTrack.m_TracksConnected.begin(); iter != aTrack.m_TracksConnected.end(); ++iter) { for( iter = aTrack.m_TracksConnected.begin(); iter != aTrack.m_TracksConnected.end(); ++iter ) { KICAD_T type = (*iter)->Type(); bool hitTest = (*iter)->HitTest( trackPoint ); if (type == PCB_VIA_T && hitTest == true) { if( (type == PCB_VIA_T) && (hitTest == true) ) { item = *iter; } } // Check for pads if via was not found on this end of the track if (item == NULL) { for (std::vector<D_PAD *>::iterator iter = aTrack.m_PadsConnected.begin(); iter != aTrack.m_PadsConnected.end(); ++iter) { if( item == NULL ) { for( std::vector<D_PAD*>::iterator iter = aTrack.m_PadsConnected.begin(); iter != aTrack.m_PadsConnected.end(); ++iter ) { PAD_SHAPE_T shape = (*iter)->GetShape(); bool hitTest = (*iter)->HitTest( trackPoint ); if (shape == PAD_CIRCLE && hitTest == true) { if( shape == PAD_CIRCLE && hitTest == true ) { item = *iter; } } Loading @@ -297,20 +377,28 @@ BOARD_CONNECTED_ITEM* TEARDROP::GetObjectOnEnd(TRACK &aTrack, ENDPOINT_T endPoin return item; } void TEARDROP::SplitSegment(const SEG &segment, int splits, std::vector<VECTOR2I> &points) void TEARDROP::splitSegment( const SEG& aSegment, int aSplits, std::vector<VECTOR2I>& aPoints ) { int dX = abs( (aSegment.A.x - aSegment.B.x) / aSplits ); int dY = abs( (aSegment.A.y - aSegment.B.y) / aSplits ); if( aSegment.A.x > aSegment.B.x ) { int dX = abs((segment.A.x - segment.B.x) / splits); int dY = abs((segment.A.y - segment.B.y) / splits); if (segment.A.x > segment.B.x) { dX = -dX; } if (segment.A.y > segment.B.y) { if( aSegment.A.y > aSegment.B.y ) { dY = -dY; } VECTOR2I delta( dX, dY ); points.push_back(segment.A + delta); aPoints.push_back( aSegment.A + delta ); // The last point is excluded as it will coinside with already built tracks for (int i = 1; i < splits - 1; i++) { points.push_back(points.back() + delta); for( int i = 1; i < aSplits - 1; i++ ) { aPoints.push_back( aPoints.back() + delta ); } } pcbnew/class_teardrop.h +79 −42 Original line number Diff line number Diff line Loading @@ -17,92 +17,129 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ /** * @file class_teardrop.h * @brief Definitions for teardrops. */ #ifndef CLASS_TEARDROP_H #define CLASS_TEARDROP_H #include "class_track.h" #include "geometry/seg.h" /** * @brief The TEARDROP class * is base definition of a teardrop. It is intended for calculation and holding of points which * compose a teardrop. This class does not contain any methods which create actual tracks. */ class TEARDROP { public: TEARDROP(); /** * @brief Defines the type of a teardrop. * @brief The TEARDROP_TYPE defines the type of a teardrop. */ typedef enum { typedef enum { TEARDROP_NONE, ///< The type is undefined TEARDROP_STRAIGHT, ///< The teardrop is created by two straight segments TEARDROP_CURVED ///< The teardrop is created by several segments approximating a curve } TEARDROP_TYPE; /** * @brief GetType returns the type of the teardrop. * @return TEARDROP_TYPE * @brief Function \a GetType * returns the type of the teardrop. * @return TEARDROP_TYPE - the type of the teardrop */ TEARDROP_TYPE GetType() const { return m_type; } /** * @brief Function Create creates a teardrop(s) for a given track * @param aTrack * @return \a true in case the teardrops were successfully built and \a false otherwise * @brief Function \a Create * creates a teardrop(s) for a given track. * @param [in] aTrack is a track at which teardrop(s) should be created * @param [in] aEndPoint is an end point at which a teardrop should be created * @param [in] aType defines the type of a teardrop * @return bool - \a true in case the teardrops were successfully built and \a false otherwise */ bool Create(TRACK &aTrack, ENDPOINT_T endPoint, TEARDROP_TYPE type); bool Create( TRACK& aTrack, ENDPOINT_T aEndPoint, TEARDROP_TYPE aType ); void GetCoordinates(std::vector<VECTOR2I> &points) const {points = m_coordinates;} /** * @brief Function \a GetCoordinates * returns the coordinates of created teardrop. * @param [out] aPoints is a container for coordinates */ void GetCoordinates( std::vector<VECTOR2I>& aPoints ) const { aPoints = m_coordinates; } private: ///> Contains the type of teardrop /// Contains the type of teardrop TEARDROP_TYPE m_type; ///> \a m_upperSegment and \a m_lowerSegment contain coordinates of segments composing a teardrop std::vector<VECTOR2I> m_upperSegment; std::vector<VECTOR2I> m_lowerSegment; /// Contains the actual coordinates of teardrop std::vector<VECTOR2I> m_coordinates; /** * @brief Function \a CurvedSegments computes several points on deltoid curve and moves * these points along the vector defined by \a aTrack. * @brief Function \a curvedSegments * computes several points on deltoid curve and moves these points along the vector * defined by \a aTrack. * * This function computes the coordinates of points only and does not build actual track segments. * See deltiod description and its parametric equations on [wiki page](http://en.wikipedia.org/wiki/Deltoid_curve). * @param [in] aTrack defines a vector along which the curved segments should be built * @param [in] aVia used as the center of coordinates * @return \a true in case the segments were successfully built and \a false otherwise * @return bool - \a true in case the segments were successfully built and \a false otherwise */ bool CurvedSegments(TRACK &aTrack, const VIA &aVia); bool curvedSegments( TRACK& aTrack, const VIA& aVia ); /** * @brief Function \a StraightSegments builds two tangent lines for a circle from a givent point. * @brief Function \a straightSegments * builds two tangent lines to a circle from a givent point. * * This function computes the coordinates of points only and does not build actual track segments. * @param [in] aTrack defines a vector along which the segments should be built * @param [in] aVia represents a circle to which the segments should be built * @param [in] distance is distance ratio (in percent) from circle center in respect to its diameter * @return \a true in case the segments were successfully built and \a false otherwise * @param [in] aDistance is distance ratio (in percent) from circle center in respect to its diameter * @return bool - \a true in case the segments were successfully built and \a false otherwise */ bool straightSegments( TRACK& aTrack, const VIA& aVia, int aDistance ); /** * @brief Function \a setVector * creates a vector from \a aTrack directed into \a aVia. * @param [in] aTrack is used to create a vector * @param [in] aVia is an object to which the vector should be pointed to * @param [out] aStartPoint is start point of resulting vector * @param [out] aEndPoint is end point of resulting vector * @return bool - \a true in case the vector is created successfully and \a false otherwise */ bool setVector( TRACK& aTrack, const VIA& aVia, VECTOR2I& aStartPoint, VECTOR2I& aEndPoint ); /** * @brief Function \a getObjectOnEnd * returns an object (via or pad) at the given end of a track. * @param [in] aTrack is a reference track * @param [in] aEndPoint defines the end in question * @return BOARD_CONNECTED_ITEM - the object found or NULL otherwise */ bool StraightSegments(TRACK &aTrack, const VIA &aVia, int distance); BOARD_CONNECTED_ITEM* getObjectOnEnd( TRACK& aTrack, ENDPOINT_T aEndPoint ); /** * @brief Function SetVector creates a vector from \a aTrack directed into \a aVia * @param aTrack is used to create a vector * @param startPoint is start point of resulting vector * @param endPoint is end point of resulting vector * @return \a true in case the vector is created successfully and \a false otherwise * @brief Function \a splitSegment * splits a segment into given number of subsegments. * @param [in] aSegment is a segment to be split * @param [i] aSplits is a number of splits * @param [out] aPoints is a container for split points */ bool SetVector(TRACK &aTrack, const VIA &aVia, VECTOR2I &startPoint, VECTOR2I &endPoint); void splitSegment( const SEG& aSegment, int aSplits, std::vector<VECTOR2I>& aPoints ); BOARD_CONNECTED_ITEM* GetObjectOnEnd(TRACK &aTrack, ENDPOINT_T endPoint); void SplitSegment(const SEG &segment, int splits, std::vector<VECTOR2I> &points); inline void PointOnCurve(int angle, double radius, VECTOR2I &point) { /** * @brief Function \a pointOnCurve * calculates a single point on a deltoid curve. * @param [in] aAngle is an angle at which the point should be calculated * @param [in] aRadius is the radius of a rolling circle * @param [out] aPoint is a container for calculated point */ inline void pointOnCurve( int aAngle, double aRadius, VECTOR2I& aPoint ) { double coeff = M_PI / 180.0; point.x = 2 * radius * cos(coeff * angle) + radius * cos(2 * coeff * angle); point.y = 2 * radius * sin(coeff * angle) - radius * sin(2 * coeff * angle); aPoint.x = 2 * aRadius * cos( coeff * aAngle ) + aRadius * cos( 2 * coeff * aAngle ); aPoint.y = 2 * aRadius * sin( coeff * aAngle ) - aRadius * sin( 2 * coeff * aAngle ); } }; Loading Loading
pcbnew/class_teardrop.cpp +223 −135 Original line number Diff line number Diff line Loading @@ -11,54 +11,69 @@ TEARDROP::TEARDROP() m_type = TEARDROP_NONE; } bool TEARDROP::Create(TRACK &aTrack, ENDPOINT_T endPoint, TEARDROP_TYPE type = TEARDROP_STRAIGHT) bool TEARDROP::Create(TRACK& aTrack, ENDPOINT_T aEndPoint, TEARDROP_TYPE aType = TEARDROP_STRAIGHT ) { bool result = false; BOARD_CONNECTED_ITEM *anObject = GetObjectOnEnd(aTrack, endPoint); BOARD_CONNECTED_ITEM* object = getObjectOnEnd( aTrack, aEndPoint ); VIA* aVia = NULL; if (anObject == NULL) { if( object == NULL ) { return false; } else { switch (anObject->Type()) { else { switch( object->Type() ) { case PCB_VIA_T: aVia = dynamic_cast<VIA *>(anObject); aVia = dynamic_cast<VIA*>( object ); break; case PCB_PAD_T: aVia = new VIA( NULL ); aVia->SetLayer(anObject->GetLayer()); aVia->SetPosition(anObject->GetPosition()); aVia->SetWidth(2 * dynamic_cast<D_PAD *>(anObject)->GetBoundingRadius()); aVia->SetLayer( object->GetLayer() ); aVia->SetPosition( object->GetPosition() ); aVia->SetWidth( 2 * dynamic_cast<D_PAD*>( object )->GetBoundingRadius() ); break; default: break; } } if (type == TEARDROP_STRAIGHT) { result = StraightSegments(aTrack, *aVia, 100); if( aType == TEARDROP_STRAIGHT ) { result = straightSegments( aTrack, *aVia, 100 ); } else if (type == TEARDROP_CURVED) { result = CurvedSegments(aTrack, *aVia); else if( aType == TEARDROP_CURVED ) { result = curvedSegments( aTrack, *aVia ); } return result; } bool TEARDROP::SetVector(TRACK &aTrack, const VIA & aVia, VECTOR2I &startPoint, VECTOR2I &endPoint) bool TEARDROP::setVector(TRACK& aTrack, const VIA& aVia, VECTOR2I& aStartPoint, VECTOR2I& aEndPoint ) { // Decide which end of the track is inside via and set this point as end of vector STATUS_FLAGS status = aTrack.IsPointOnEnds( aVia.GetPosition(), aVia.GetWidth() / 2 ); if (status == STARTPOINT) { startPoint = aTrack.GetEnd(); endPoint = aTrack.GetStart(); if( status == STARTPOINT ) { aStartPoint = aTrack.GetEnd(); aEndPoint = aTrack.GetStart(); } else if (status == ENDPOINT) { startPoint = aTrack.GetStart(); endPoint = aTrack.GetEnd(); else if( status == ENDPOINT ) { aStartPoint = aTrack.GetStart(); aEndPoint = aTrack.GetEnd(); } else { else { // The via is too far from any end or the track is too short return false; } Loading @@ -66,21 +81,24 @@ bool TEARDROP::SetVector(TRACK &aTrack, const VIA & aVia, VECTOR2I &startPoint, return true; } bool TEARDROP::CurvedSegments(TRACK &aTrack, const VIA &aVia) bool TEARDROP::curvedSegments( TRACK& aTrack, const VIA& aVia ) { VECTOR2I startPoint( 0, 0 ); VECTOR2I endPoint( 0, 0 ); std::vector<VECTOR2I> upperSegment; std::vector<VECTOR2I> lowerSegment; if ( !SetVector(aTrack, aVia, startPoint, endPoint) ) { if( !setVector( aTrack, aVia, startPoint, endPoint ) ) { return false; } // Check that the track is not too short double segOutsideVia = aTrack.GetLength() - (aVia.GetWidth() / 2); double minLength = (150 * aVia.GetWidth() / 2) / 100; if (segOutsideVia < minLength) { if( segOutsideVia < minLength ) { return false; } Loading @@ -88,65 +106,94 @@ bool TEARDROP::CurvedSegments(TRACK &aTrack, const VIA &aVia) VECTOR2I viaCenter( aVia.GetPosition().x, aVia.GetPosition().y ); VECTOR2I apertureUpper( 0, 0 ); VECTOR2I apertureLower( 0, 0 ); double radius = (aVia.GetWidth() / 2) - (aTrack.GetWidth() / 2); double rotationAngle = VECTOR2I( startPoint - endPoint ).Angle(); // Calculate the segments of deltoid composing the outline of a teardrop for ( int i = 10; i <= 60; i = i + 10 ) { PointOnCurve(i, radius, point); for( int i = 0; i <= 60; i = i + 10 ) { pointOnCurve( i, radius, point ); point = point.Rotate( rotationAngle ); point += viaCenter; m_coordinates.push_back( point ); if (i == 50) { if( i == 50 ) { apertureUpper = point; } } for ( int i = 300; i <= 350; i = i + 10 ) { PointOnCurve(i, radius, point); for( int i = 300; i <= 360; i = i + 10 ) { pointOnCurve( i, radius, point ); point = point.Rotate( rotationAngle ); point += viaCenter; m_coordinates.push_back( point ); if (i == 340) { if( i == 340 ) { apertureLower = point; } } // Calculate the number of segments needed to fill the area inside the teardrop if (aVia.GetWidth() / 2 > 2 * aTrack.GetWidth()) { if( aVia.GetWidth() / 2 > 2 * aTrack.GetWidth() ) { // First, calculate the distance between two points on both sides of the track and // number of iterations required to fill the zone SEG aperture( apertureUpper, apertureLower ); int numSegments = aperture.Length() / aTrack.GetWidth(); int delta = radius / numSegments; for (int iteration = 0; iteration < numSegments; iteration++) { // Second, fill the inward teardrop area for( int iteration = 0; iteration < numSegments; iteration++ ) { radius = radius - delta; for ( int i = 10; i <= 60; i = i + 10 ) { PointOnCurve(i, radius, point); for( int i = 10; i <= 60; i = i + 10 ) { pointOnCurve( i, radius, point ); point = point.Rotate( rotationAngle ); point += viaCenter; if (i == 10) { // Stop calculations in case the coordinates are inside the via if( i == 10 ) { int distance = SEG( viaCenter, point ).Length(); if (distance < aVia.GetWidth() / 2) { if( distance < aVia.GetWidth() / 2 ) { break; } } m_coordinates.push_back( point ); } lowerSegment.clear(); for ( int i = 350; i >= 300; i = i - 10 ) { PointOnCurve(i, radius, point); for( int i = 350; i >= 300; i = i - 10 ) { pointOnCurve( i, radius, point ); point = point.Rotate( rotationAngle ); point += viaCenter; if (i == 350) { // Stop calculations in case the coordinates are inside the via if( i == 350 ) { int distance = SEG( viaCenter, point ).Length(); if (distance < aVia.GetWidth() / 2) { if( distance < aVia.GetWidth() / 2 ) { break; } } lowerSegment.push_back( point ); } // Revert coordinates order for (std::vector<VECTOR2I>::reverse_iterator iter = lowerSegment.rbegin(); iter != lowerSegment.rend(); ++iter) { // Revert coordinates order. This is necessary to create tracks in correct order later on for( std::vector<VECTOR2I>::reverse_iterator iter = lowerSegment.rbegin(); iter != lowerSegment.rend(); ++iter ) { m_coordinates.push_back( *iter ); } } Loading @@ -155,27 +202,32 @@ bool TEARDROP::CurvedSegments(TRACK &aTrack, const VIA &aVia) return true; } bool TEARDROP::StraightSegments(TRACK &aTrack, const VIA &aVia, int distance = 100) bool TEARDROP::straightSegments(TRACK& aTrack, const VIA& aVia, int aDistance = 100 ) { VECTOR2I startPoint( 0, 0 ); VECTOR2I endPoint( 0, 0 ); VECTOR2I viaCenter( aVia.GetPosition().x, aVia.GetPosition().y ); if ( !SetVector(aTrack, aVia, startPoint, endPoint) ) { if( !setVector( aTrack, aVia, startPoint, endPoint ) ) { return false; } // Check that the track is not too short double segOutsideVia = aTrack.GetLength() - (aVia.GetWidth() / 2); double minLength = (distance * aVia.GetWidth() / 2) / 100; if (segOutsideVia < minLength) { double minLength = (aDistance * aVia.GetWidth() / 2) / 100; if( segOutsideVia < minLength ) { return false; } // Equation coefficients double r = (aVia.GetWidth() / 2) + ((distance * aVia.GetWidth()) / (2 *100)); double r = (aVia.GetWidth() / 2) + ( (aDistance * aVia.GetWidth()) / (2 * 100) ); double a = pow( (endPoint.x - startPoint.x), 2 ) + pow( (endPoint.y - startPoint.y), 2 ); double b = 2 * (double)(endPoint.x - startPoint.x) * (double)(startPoint.x - viaCenter.x) + 2 * (double)(endPoint.y - startPoint.y) * (double)(startPoint.y - viaCenter.y); double b = 2 * (double)(endPoint.x - startPoint.x) * (double)(startPoint.x - viaCenter.x) + 2 * (double)(endPoint.y - startPoint.y) * (double)(startPoint.y - viaCenter.y); double c = pow( (startPoint.x - viaCenter.x), 2 ) + pow( (startPoint.y - viaCenter.y), 2 ) - pow( r, 2 ); double t = 2 * c / (-b + sqrt( b * b - 4 * a * c)); Loading @@ -199,30 +251,36 @@ bool TEARDROP::StraightSegments(TRACK &aTrack, const VIA &aVia, int distance = 1 // Calculate the number of segments needed to fill the area inside the teardrop std::vector<VECTOR2I> splitPoints; if (aVia.GetWidth() / 2 > 2 * aTrack.GetWidth()) { if( aVia.GetWidth() / 2 > 2 * aTrack.GetWidth() ) { // First, calculate the intersection point of the circle and one hand of the teardrop r = aVia.GetWidth() / 2; a = pow( (upperPoint.x - startPoint.x), 2 ) + pow( (upperPoint.y - startPoint.y), 2 ); b = 2 * (double)(upperPoint.x - startPoint.x) * (double)(startPoint.x - viaCenter.x) + 2 * (double)(upperPoint.y - startPoint.y) * (double)(startPoint.y - viaCenter.y); b = 2 * (double)(upperPoint.x - startPoint.x) * (double)(startPoint.x - viaCenter.x) + 2 * (double)(upperPoint.y - startPoint.y) * (double) (startPoint.y - viaCenter.y); c = pow( (startPoint.x - viaCenter.x), 2 ) + pow( (startPoint.y - viaCenter.y), 2 ) - pow( r, 2 ); t = 2 * c / ( -b + sqrt( b * b - 4 * a * c ) ); x = (upperPoint.x - startPoint.x) * t + startPoint.x; y = (upperPoint.y - startPoint.y) * t + startPoint.y; VECTOR2I intersectionPoint( (int) x, (int) y ); // Second, calculate the distance between the given track and the intersection point SEG trackSegment(aTrack.GetStart().x, aTrack.GetStart().y, aTrack.GetEnd().x, aTrack.GetEnd().y); SEG trackSegment( aTrack.GetStart().x, aTrack.GetStart().y, aTrack.GetEnd().x, aTrack.GetEnd().y ); int dist = trackSegment.LineDistance( intersectionPoint ); int numSegments = 2 * dist / aTrack.GetWidth(); // Third, subdivide the diameter of the via and build additional segments SEG segDiameter = SEG( upperPoint, lowerPoint ); SplitSegment(segDiameter, numSegments, splitPoints); splitSegment( segDiameter, numSegments, splitPoints ); } std::list<VECTOR2I> outlinePoints; outlinePoints.push_back( upperPoint ); for (size_t i = 0; i < splitPoints.size(); i++) { for( size_t i = 0; i < splitPoints.size(); i++ ) { outlinePoints.push_back( splitPoints[i] ); } outlinePoints.push_back( lowerPoint ); Loading @@ -230,30 +288,39 @@ bool TEARDROP::StraightSegments(TRACK &aTrack, const VIA &aVia, int distance = 1 // Biuld triangles filling the teardrop int vertexNum = 0; std::list<VECTOR2I>::iterator iter = outlinePoints.begin(); while ( iter != outlinePoints.end() ) { switch (vertexNum) { while( iter != outlinePoints.end() ) { switch( vertexNum ) { case 0: m_coordinates.push_back( linePoint ); vertexNum++; break; case 1: m_coordinates.push_back( *iter ); vertexNum++; iter++; break; case 2: m_coordinates.push_back( *iter ); vertexNum = 0; iter++; break; default:break; default: break; } } // Append additional vertexies in order to finish last triangle if (vertexNum == 0) { if( vertexNum == 0 ) { m_coordinates.push_back( linePoint ); } else if (vertexNum == 2) { else if( vertexNum == 2 ) { m_coordinates.push_back( m_coordinates[m_coordinates.size() - 3] ); m_coordinates.push_back( linePoint ); } Loading @@ -261,34 +328,47 @@ bool TEARDROP::StraightSegments(TRACK &aTrack, const VIA &aVia, int distance = 1 return true; } // TODO: m_TracksConnected member is considered a temporary storage. Find another way to get an object BOARD_CONNECTED_ITEM* TEARDROP::GetObjectOnEnd(TRACK &aTrack, ENDPOINT_T endPoint) BOARD_CONNECTED_ITEM* TEARDROP::getObjectOnEnd(TRACK& aTrack, ENDPOINT_T aEndPoint ) { wxPoint trackPoint; BOARD_CONNECTED_ITEM* item = NULL; std::vector<TRACK*>::const_iterator iter; if (endPoint == ENDPOINT_START) { if( aEndPoint == ENDPOINT_START ) { trackPoint = aTrack.GetStart(); } else { else { trackPoint = aTrack.GetEnd(); } // Check for vias first for (iter = aTrack.m_TracksConnected.begin(); iter != aTrack.m_TracksConnected.end(); ++iter) { for( iter = aTrack.m_TracksConnected.begin(); iter != aTrack.m_TracksConnected.end(); ++iter ) { KICAD_T type = (*iter)->Type(); bool hitTest = (*iter)->HitTest( trackPoint ); if (type == PCB_VIA_T && hitTest == true) { if( (type == PCB_VIA_T) && (hitTest == true) ) { item = *iter; } } // Check for pads if via was not found on this end of the track if (item == NULL) { for (std::vector<D_PAD *>::iterator iter = aTrack.m_PadsConnected.begin(); iter != aTrack.m_PadsConnected.end(); ++iter) { if( item == NULL ) { for( std::vector<D_PAD*>::iterator iter = aTrack.m_PadsConnected.begin(); iter != aTrack.m_PadsConnected.end(); ++iter ) { PAD_SHAPE_T shape = (*iter)->GetShape(); bool hitTest = (*iter)->HitTest( trackPoint ); if (shape == PAD_CIRCLE && hitTest == true) { if( shape == PAD_CIRCLE && hitTest == true ) { item = *iter; } } Loading @@ -297,20 +377,28 @@ BOARD_CONNECTED_ITEM* TEARDROP::GetObjectOnEnd(TRACK &aTrack, ENDPOINT_T endPoin return item; } void TEARDROP::SplitSegment(const SEG &segment, int splits, std::vector<VECTOR2I> &points) void TEARDROP::splitSegment( const SEG& aSegment, int aSplits, std::vector<VECTOR2I>& aPoints ) { int dX = abs( (aSegment.A.x - aSegment.B.x) / aSplits ); int dY = abs( (aSegment.A.y - aSegment.B.y) / aSplits ); if( aSegment.A.x > aSegment.B.x ) { int dX = abs((segment.A.x - segment.B.x) / splits); int dY = abs((segment.A.y - segment.B.y) / splits); if (segment.A.x > segment.B.x) { dX = -dX; } if (segment.A.y > segment.B.y) { if( aSegment.A.y > aSegment.B.y ) { dY = -dY; } VECTOR2I delta( dX, dY ); points.push_back(segment.A + delta); aPoints.push_back( aSegment.A + delta ); // The last point is excluded as it will coinside with already built tracks for (int i = 1; i < splits - 1; i++) { points.push_back(points.back() + delta); for( int i = 1; i < aSplits - 1; i++ ) { aPoints.push_back( aPoints.back() + delta ); } }
pcbnew/class_teardrop.h +79 −42 Original line number Diff line number Diff line Loading @@ -17,92 +17,129 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ /** * @file class_teardrop.h * @brief Definitions for teardrops. */ #ifndef CLASS_TEARDROP_H #define CLASS_TEARDROP_H #include "class_track.h" #include "geometry/seg.h" /** * @brief The TEARDROP class * is base definition of a teardrop. It is intended for calculation and holding of points which * compose a teardrop. This class does not contain any methods which create actual tracks. */ class TEARDROP { public: TEARDROP(); /** * @brief Defines the type of a teardrop. * @brief The TEARDROP_TYPE defines the type of a teardrop. */ typedef enum { typedef enum { TEARDROP_NONE, ///< The type is undefined TEARDROP_STRAIGHT, ///< The teardrop is created by two straight segments TEARDROP_CURVED ///< The teardrop is created by several segments approximating a curve } TEARDROP_TYPE; /** * @brief GetType returns the type of the teardrop. * @return TEARDROP_TYPE * @brief Function \a GetType * returns the type of the teardrop. * @return TEARDROP_TYPE - the type of the teardrop */ TEARDROP_TYPE GetType() const { return m_type; } /** * @brief Function Create creates a teardrop(s) for a given track * @param aTrack * @return \a true in case the teardrops were successfully built and \a false otherwise * @brief Function \a Create * creates a teardrop(s) for a given track. * @param [in] aTrack is a track at which teardrop(s) should be created * @param [in] aEndPoint is an end point at which a teardrop should be created * @param [in] aType defines the type of a teardrop * @return bool - \a true in case the teardrops were successfully built and \a false otherwise */ bool Create(TRACK &aTrack, ENDPOINT_T endPoint, TEARDROP_TYPE type); bool Create( TRACK& aTrack, ENDPOINT_T aEndPoint, TEARDROP_TYPE aType ); void GetCoordinates(std::vector<VECTOR2I> &points) const {points = m_coordinates;} /** * @brief Function \a GetCoordinates * returns the coordinates of created teardrop. * @param [out] aPoints is a container for coordinates */ void GetCoordinates( std::vector<VECTOR2I>& aPoints ) const { aPoints = m_coordinates; } private: ///> Contains the type of teardrop /// Contains the type of teardrop TEARDROP_TYPE m_type; ///> \a m_upperSegment and \a m_lowerSegment contain coordinates of segments composing a teardrop std::vector<VECTOR2I> m_upperSegment; std::vector<VECTOR2I> m_lowerSegment; /// Contains the actual coordinates of teardrop std::vector<VECTOR2I> m_coordinates; /** * @brief Function \a CurvedSegments computes several points on deltoid curve and moves * these points along the vector defined by \a aTrack. * @brief Function \a curvedSegments * computes several points on deltoid curve and moves these points along the vector * defined by \a aTrack. * * This function computes the coordinates of points only and does not build actual track segments. * See deltiod description and its parametric equations on [wiki page](http://en.wikipedia.org/wiki/Deltoid_curve). * @param [in] aTrack defines a vector along which the curved segments should be built * @param [in] aVia used as the center of coordinates * @return \a true in case the segments were successfully built and \a false otherwise * @return bool - \a true in case the segments were successfully built and \a false otherwise */ bool CurvedSegments(TRACK &aTrack, const VIA &aVia); bool curvedSegments( TRACK& aTrack, const VIA& aVia ); /** * @brief Function \a StraightSegments builds two tangent lines for a circle from a givent point. * @brief Function \a straightSegments * builds two tangent lines to a circle from a givent point. * * This function computes the coordinates of points only and does not build actual track segments. * @param [in] aTrack defines a vector along which the segments should be built * @param [in] aVia represents a circle to which the segments should be built * @param [in] distance is distance ratio (in percent) from circle center in respect to its diameter * @return \a true in case the segments were successfully built and \a false otherwise * @param [in] aDistance is distance ratio (in percent) from circle center in respect to its diameter * @return bool - \a true in case the segments were successfully built and \a false otherwise */ bool straightSegments( TRACK& aTrack, const VIA& aVia, int aDistance ); /** * @brief Function \a setVector * creates a vector from \a aTrack directed into \a aVia. * @param [in] aTrack is used to create a vector * @param [in] aVia is an object to which the vector should be pointed to * @param [out] aStartPoint is start point of resulting vector * @param [out] aEndPoint is end point of resulting vector * @return bool - \a true in case the vector is created successfully and \a false otherwise */ bool setVector( TRACK& aTrack, const VIA& aVia, VECTOR2I& aStartPoint, VECTOR2I& aEndPoint ); /** * @brief Function \a getObjectOnEnd * returns an object (via or pad) at the given end of a track. * @param [in] aTrack is a reference track * @param [in] aEndPoint defines the end in question * @return BOARD_CONNECTED_ITEM - the object found or NULL otherwise */ bool StraightSegments(TRACK &aTrack, const VIA &aVia, int distance); BOARD_CONNECTED_ITEM* getObjectOnEnd( TRACK& aTrack, ENDPOINT_T aEndPoint ); /** * @brief Function SetVector creates a vector from \a aTrack directed into \a aVia * @param aTrack is used to create a vector * @param startPoint is start point of resulting vector * @param endPoint is end point of resulting vector * @return \a true in case the vector is created successfully and \a false otherwise * @brief Function \a splitSegment * splits a segment into given number of subsegments. * @param [in] aSegment is a segment to be split * @param [i] aSplits is a number of splits * @param [out] aPoints is a container for split points */ bool SetVector(TRACK &aTrack, const VIA &aVia, VECTOR2I &startPoint, VECTOR2I &endPoint); void splitSegment( const SEG& aSegment, int aSplits, std::vector<VECTOR2I>& aPoints ); BOARD_CONNECTED_ITEM* GetObjectOnEnd(TRACK &aTrack, ENDPOINT_T endPoint); void SplitSegment(const SEG &segment, int splits, std::vector<VECTOR2I> &points); inline void PointOnCurve(int angle, double radius, VECTOR2I &point) { /** * @brief Function \a pointOnCurve * calculates a single point on a deltoid curve. * @param [in] aAngle is an angle at which the point should be calculated * @param [in] aRadius is the radius of a rolling circle * @param [out] aPoint is a container for calculated point */ inline void pointOnCurve( int aAngle, double aRadius, VECTOR2I& aPoint ) { double coeff = M_PI / 180.0; point.x = 2 * radius * cos(coeff * angle) + radius * cos(2 * coeff * angle); point.y = 2 * radius * sin(coeff * angle) - radius * sin(2 * coeff * angle); aPoint.x = 2 * aRadius * cos( coeff * aAngle ) + aRadius * cos( 2 * coeff * aAngle ); aPoint.y = 2 * aRadius * sin( coeff * aAngle ) - aRadius * sin( 2 * coeff * aAngle ); } }; Loading