Commit fbe84842 authored by Cirilo Bernardo's avatar Cirilo Bernardo Committed by jean-pierre charras
Browse files

Apply IDF tools patch from Cirilo Bernardo

parent b39408b1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -542,6 +542,7 @@ add_subdirectory( potrace )
add_subdirectory( bitmap2component )
add_subdirectory( pcb_calculator )
add_subdirectory( tools )
add_subdirectory( utils )
add_subdirectory( qa )
#add_subdirectory( new )

+1 −0
Original line number Diff line number Diff line
@@ -129,6 +129,7 @@ set( PCBNEW_EXPORTERS
    exporters/export_gencad.cpp
    exporters/export_idf.cpp
    exporters/export_vrml.cpp
    exporters/idf_common.cpp
    exporters/idf.cpp
    exporters/gen_drill_report_files.cpp
    exporters/gen_modules_placefile.cpp
+0 −411
Original line number Diff line number Diff line
@@ -47,8 +47,6 @@
#include <idf.h>
#include <build_version.h>

// differences in angle smaller than MIN_ANG are considered equal
#define MIN_ANG     (0.01)
// minimum drill diameter (nanometers) - 10000 is a 0.01mm drill
#define IDF_MIN_DIA ( 10000.0 )

@@ -70,280 +68,6 @@ static bool GetIDFString( const std::string& aLine, std::string& aIDFString,

// END: IDF_LIB helper routines

bool IDF_POINT::Matches( const IDF_POINT& aPoint, double aRadius )
{
    double dx = x - aPoint.x;
    double dy = y - aPoint.y;

    double d2 = dx * dx + dy * dy;

    if( d2 <= aRadius * aRadius )
        return true;

    return false;
}


double IDF_POINT::CalcDistance( const IDF_POINT& aPoint ) const
{
    double dx   = aPoint.x - x;
    double dy   = aPoint.y - y;
    double dist = sqrt( dx * dx + dy * dy );

    return dist;
}


double IDF3::CalcAngleRad( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
{
    return atan2( aEndPoint.y - aStartPoint.y, aEndPoint.x - aStartPoint.x );
}


double IDF3::CalcAngleDeg( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
{
    double ang = CalcAngleRad( aStartPoint, aEndPoint );

    // round to thousandths of a degree
    int iang = int (ang / M_PI * 1800000.0);

    ang = iang / 10000.0;

    return ang;
}


IDF_SEGMENT::IDF_SEGMENT()
{
    angle = 0.0;
    offsetAngle = 0.0;
    radius = 0.0;
}


IDF_SEGMENT::IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
{
    angle = 0.0;
    offsetAngle = 0.0;
    radius = 0.0;
    startPoint = aStartPoint;
    endPoint = aEndPoint;
}


IDF_SEGMENT::IDF_SEGMENT( const IDF_POINT& aStartPoint,
        const IDF_POINT& aEndPoint,
        double aAngle,
        bool aFromKicad )
{
    double diff = abs( aAngle ) - 360.0;

    if( ( diff < MIN_ANG
          && diff > -MIN_ANG ) || ( aAngle < MIN_ANG && aAngle > -MIN_ANG ) || (!aFromKicad) )
    {
        angle = 0.0;
        startPoint = aStartPoint;
        endPoint = aEndPoint;

        if( diff < MIN_ANG && diff > -MIN_ANG )
        {
            angle = 360.0;
            center = aStartPoint;
            offsetAngle = 0.0;
            radius = aStartPoint.CalcDistance( aEndPoint );
        }
        else if( aAngle < MIN_ANG && aAngle > -MIN_ANG )
        {
            CalcCenterAndRadius();
        }

        return;
    }

    // we need to convert from the KiCad arc convention
    angle = aAngle;

    center = aStartPoint;

    offsetAngle = IDF3::CalcAngleDeg( aStartPoint, aEndPoint );

    radius = aStartPoint.CalcDistance( aEndPoint );

    startPoint = aEndPoint;

    double ang = offsetAngle + aAngle;
    ang = (ang / 180.0) * M_PI;

    endPoint.x  = ( radius * cos( ang ) ) + center.x;
    endPoint.y  = ( radius * sin( ang ) ) + center.y;
}


bool IDF_SEGMENT::MatchesStart( const IDF_POINT& aPoint, double aRadius )
{
    return startPoint.Matches( aPoint, aRadius );
}


bool IDF_SEGMENT::MatchesEnd( const IDF_POINT& aPoint, double aRadius )
{
    return endPoint.Matches( aPoint, aRadius );
}


void IDF_SEGMENT::CalcCenterAndRadius( void )
{
    // NOTE:  this routine does not check if the points are the same
    // or too close to be sensible in a production setting.

    double offAng = IDF3::CalcAngleRad( startPoint, endPoint );
    double d = startPoint.CalcDistance( endPoint ) / 2.0;
    double xm   = ( startPoint.x + endPoint.x ) * 0.5;
    double ym   = ( startPoint.y + endPoint.y ) * 0.5;

    radius = d / sin( angle * M_PI / 180.0 );

    if( radius < 0.0 )
    {
        radius = -radius;
    }

    // calculate the height of the triangle with base d and hypotenuse r
    double dh2 = radius * radius - d * d;

    if( dh2 < 0 )
    {
        // this should only ever happen due to rounding errors when r == d
        dh2 = 0;
    }

    double h = sqrt( dh2 );

    if( angle > 0.0 )
        offAng += M_PI2;
    else
        offAng -= M_PI2;

    if( ( angle > M_PI ) || ( angle < -M_PI ) )
        offAng += M_PI;

    center.x = h * cos( offAng ) + xm;
    center.y = h * sin( offAng ) + ym;

    offsetAngle = IDF3::CalcAngleDeg( center, startPoint );
}


bool IDF_SEGMENT::IsCircle( void )
{
    double diff = abs( angle ) - 360.0;

    if( ( diff < MIN_ANG ) && ( diff > -MIN_ANG ) )
        return true;

    return false;
}


double IDF_SEGMENT::GetMinX( void )
{
    if( angle == 0.0 )
        return std::min( startPoint.x, endPoint.x );

    // Calculate the leftmost point of the circle or arc

    if( IsCircle() )
    {
        // if only everything were this easy
        return center.x - radius;
    }

    // cases:
    // 1. CCW arc: if offset + included angle >= 180 deg then
    // MinX = center.x - radius, otherwise MinX is the
    // same as for the case of a line.
    // 2. CW arc: if offset + included angle <= -180 deg then
    // MinX = center.x - radius, otherwise MinX is the
    // same as for the case of a line.

    if( angle > 0 )
    {
        // CCW case
        if( ( offsetAngle + angle ) >= 180.0 )
        {
            return center.x - radius;
        }
        else
        {
            return std::min( startPoint.x, endPoint.x );
        }
    }

    // CW case
    if( ( offsetAngle + angle ) <= -180.0 )
    {
        return center.x - radius;
    }

    return std::min( startPoint.x, endPoint.x );
}


void IDF_SEGMENT::SwapEnds( void )
{
    if( IsCircle() )
    {
        // reverse the direction
        angle = -angle;
        return;
    }

    IDF_POINT tmp = startPoint;
    startPoint = endPoint;
    endPoint = tmp;

    if( ( angle < MIN_ANG ) && ( angle > -MIN_ANG ) )
        return;         // nothing more to do

    // change the direction of the arc
    angle = -angle;
    // calculate the new offset angle
    offsetAngle = IDF3::CalcAngleDeg( center, startPoint );
}


void IDF_OUTLINE::push( IDF_SEGMENT* item )
{
    if( !outline.empty() )
    {
        if( item->IsCircle() )
        {
            // not allowed
            wxString msg = wxT( "INVALID GEOMETRY: a circle is being added to a non-empty outline" );
            THROW_IO_ERROR( msg );
        }
        else
        {
            if( outline.back()->IsCircle() )
            {
                // we can't add lines to a circle
                wxString msg = wxT( "INVALID GEOMETRY: a line is being added to a circular outline" );
                THROW_IO_ERROR( msg );
            }
            else if( !item->MatchesStart( outline.back()->endPoint ) )
            {
                // startPoint[N] != endPoint[N -1]
                wxString msg = wxT( "INVALID GEOMETRY: disjoint segments" );
                THROW_IO_ERROR( msg );
            }
        }
    }

    outline.push_back( item );
    dir += ( outline.back()->endPoint.x - outline.back()->startPoint.x )
           * ( outline.back()->endPoint.y + outline.back()->startPoint.y );
}


IDF_DRILL_DATA::IDF_DRILL_DATA( double aDrillDia, double aPosX, double aPosY,
        IDF3::KEY_PLATING aPlating,
@@ -881,141 +605,6 @@ void IDF_BOARD::GetOffset( double& x, double& y )
}


void IDF3::GetOutline( std::list<IDF_SEGMENT*>& aLines,
        IDF_OUTLINE& aOutline )
{
    aOutline.Clear();

    // NOTE: To tell if the point order is CCW or CW,
    // sum all:  (endPoint.X[n] - startPoint.X[n])*(endPoint[n] + startPoint.Y[n])
    // If the result is >0, the direction is CW, otherwise
    // it is CCW. Note that the result cannot be 0 unless
    // we have a bounded area of 0.

    // First we find the segment with the leftmost point
    std::list<IDF_SEGMENT*>::iterator bl    = aLines.begin();
    std::list<IDF_SEGMENT*>::iterator el    = aLines.end();
    std::list<IDF_SEGMENT*>::iterator idx   = bl++;       // iterator for the object with minX

    double minx = (*idx)->GetMinX();
    double curx;

    while( bl != el )
    {
        curx = (*bl)->GetMinX();

        if( curx < minx )
        {
            minx = curx;
            idx = bl;
        }

        ++bl;
    }

    aOutline.push( *idx );
    aLines.erase( idx );

    // If the item is a circle then we're done
    if( aOutline.front()->IsCircle() )
        return;

    // Assemble the loop
    bool complete = false;  // set if loop is complete
    bool matched;           // set if a segment's end point was matched

    while( !complete )
    {
        matched = false;
        bl  = aLines.begin();
        el  = aLines.end();

        while( bl != el && !matched )
        {
            if( (*bl)->MatchesStart( aOutline.back()->endPoint ) )
            {
                if( (*bl)->IsCircle() )
                {
                    // a circle on the perimeter is pathological but we just ignore it
                    ++bl;
                }
                else
                {
                    matched = true;
                    aOutline.push( *bl );
                    aLines.erase( bl );
                }

                continue;
            }

            ++bl;
        }

        if( !matched )
        {
            // attempt to match the end points
            bl  = aLines.begin();
            el  = aLines.end();

            while( bl != el && !matched )
            {
                if( (*bl)->MatchesEnd( aOutline.back()->endPoint ) )
                {
                    if( (*bl)->IsCircle() )
                    {
                        // a circle on the perimeter is pathological but we just ignore it
                        ++bl;
                    }
                    else
                    {
                        matched = true;
                        (*bl)->SwapEnds();
                        aOutline.push( *bl );
                        aLines.erase( bl );
                    }

                    continue;
                }

                ++bl;
            }
        }

        if( !matched )
        {
            // still no match - attempt to close the loop
            if( (aOutline.size() > 1) || ( aOutline.front()->angle < -MIN_ANG )
                || ( aOutline.front()->angle > MIN_ANG ) )
            {
                // close the loop
                IDF_SEGMENT* seg = new IDF_SEGMENT( aOutline.back()->endPoint,
                        aOutline.front()->startPoint );

                if( seg )
                {
                    complete = true;
                    aOutline.push( seg );
                    break;
                }
            }

            // the outline is bad; drop the segments
            aOutline.Clear();

            return;
        }

        // check if the loop is complete
        if( aOutline.front()->MatchesStart( aOutline.back()->endPoint ) )
        {
            complete = true;
            break;
        }
    }
}


IDF_LIB::~IDF_LIB()
{
    while( !components.empty() )
+1 −263
Original line number Diff line number Diff line
@@ -31,269 +31,7 @@
#include <wx/string.h>
#include <set>
#include <string>

#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795028841
#endif

#ifndef M_PI2
#define M_PI2 ( M_PI / 2.0 )
#endif

#ifndef M_PI4
#define M_PI4 ( M_PI / 4.0 )
#endif

class IDF_POINT;
class IDF_SEGMENT;
class IDF_DRILL_DATA;
class IDF_OUTLINE;
class IDF_LIB;

namespace IDF3 {
enum KEY_OWNER
{
    UNOWNED = 0,        // < either MCAD or ECAD may modify a feature
    MCAD,               // < only MCAD may modify a feature
    ECAD                // < only ECAD may modify a feature
};

enum KEY_HOLETYPE
{
    PIN = 0,            // < drill hole is for a pin
    VIA,                // < drill hole is for a via
    MTG,                // < drill hole is for mounting
    TOOL,               // < drill hole is for tooling
    OTHER               // < user has specified a custom type
};

enum KEY_PLATING
{
    PTH = 0,            // < Plate-Through Hole
    NPTH                // < Non-Plate-Through Hole
};

enum KEY_REFDES
{
    BOARD = 0,          // < feature is associated with the board
    NOREFDES,           // < feature is associated with a component with no RefDes
    PANEL,              // < feature is associated with an IDF panel
    REFDES              // < reference designator as assigned by the CAD software
};

// calculate the angle between the horizon and the segment aStartPoint to aEndPoint
double  CalcAngleRad( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );
double  CalcAngleDeg( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );

// take contiguous elements from 'lines' and stuff them into 'outline'
void GetOutline( std::list<IDF_SEGMENT*>& aLines,
        IDF_OUTLINE& aOutline );
}


/**
 * @Struct IDF_POINT
 * represents a vector of three doubles; this may be represent
 * a point in space, or scaling, translation or rotation along
 * three axes.
 */
struct IDF_VECTOR
{
    double x;
    double y;
    double z;
};


/**
 * @Class IDF_POINT
 * represents a point
 */
class IDF_POINT
{
public:
    double x;   // < X coordinate
    double y;   // < Y coordinate

    IDF_POINT()
    {
        x = 0.0;
        y = 0.0;
    }

    /**
     * Function Matches()
     * returns true if the given coordinate point is within the given radius
     * of the point.
     * @param aPoint : coordinates of the point being compared
     * @param aRadius : radius within which the points are considered the same
     */
    bool    Matches( const IDF_POINT& aPoint, double aRadius = 1e-5 );
    double  CalcDistance( const IDF_POINT& aPoint ) const;
};


/**
 * @Class IDF_SEGMENT
 * represents a geometry segment as used in IDFv3 outlines
 */
class IDF_SEGMENT
{
private:
    /**
     * Function CalcCenterAndRadius()
     * Calculates the center, radius, and angle between center and start point given the
     * IDF compliant points and included angle.
     * @var startPoint, @var endPoint, and @var angle must be set prior as per IDFv3
     */
    void CalcCenterAndRadius( void );

public:
    IDF_POINT startPoint;   // starting point in IDF coordinates
    IDF_POINT endPoint;     // end point in IDF coordinates
    IDF_POINT   center;     // center of an arc or circle; used primarily for calculating min X
    double  angle;          // included angle (degrees) according to IDFv3 specification
    double  offsetAngle;    // angle between center and start of arc; used to speed up some calcs.
    double  radius;         // radius of the arc or circle; used to speed up some calcs.

    /**
     * Function IDF_SEGMENT()
     * initializes the internal variables
     */
    IDF_SEGMENT();

    /**
     * Function IDF_SEGMENT( start, end )
     * creates a straight segment
     */
    IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );

    /**
     * Function IDF_SEGMENT( start, end )
     * creates a straight segment, arc, or circle depending on the angle
     * @param aStartPoint : start point (center if using KiCad convention, otherwise IDF convention)
     * @param aEndPoint : end point (start of arc if using KiCad convention, otherwise IDF convention)
     * @param aAngle : included angle; the KiCad convention is equivalent to the IDF convention
     * @param fromKicad : set true if we need to convert from KiCad to IDF convention
     */
    IDF_SEGMENT( const IDF_POINT& aStartPoint,
            const IDF_POINT& aEndPoint,
            double aAngle,
            bool aFromKicad );

    /**
     * Function MatchesStart()
     * returns true if the given coordinate is within a radius 'rad'
     * of the start point.
     * @param aPoint : coordinates of the point being compared
     * @param aRadius : radius within which the points are considered the same
     */
    bool MatchesStart( const IDF_POINT& aPoint, double aRadius = 1e-3 );

    /**
     * Function MatchesEnd()
     * returns true if the given coordinate is within a radius 'rad'
     * of the end point.
     * @param aPoint : coordinates of the point being compared
     * @param aRadius : radius within which the points are considered the same
     */
    bool MatchesEnd( const IDF_POINT& aPoint, double aRadius = 1e-3 );

    /**
     * Function IsCircle()
     * returns true if this segment is a circle
     */
    bool IsCircle( void );

    /**
     * Function GetMinX()
     * returns the minimum X coordinate of this segment
     */
    double GetMinX( void );

    /**
     * Function SwapEnds()
     * Swaps the start and end points and alters internal
     * variables as necessary for arcs
     */
    void SwapEnds( void );
};


/**
 * @Class IDF_OUTLINE
 * contains segment and winding information for an IDF outline
 */
class IDF_OUTLINE
{
private:
    double dir;
    std::list<IDF_SEGMENT*> outline;

public:
    IDF_OUTLINE() { dir = 0.0; }
    ~IDF_OUTLINE() { Clear(); }

    // returns true if the current list of points represents a counterclockwise winding
    bool IsCCW( void )
    {
        if( dir > 0.0 )
            return false;

        return true;
    }

    // clears the internal list of outline segments
    void Clear( void )
    {
        dir = 0.0;

        while( !outline.empty() )
        {
            delete outline.front();
            outline.pop_front();
        }
    }

    // returns the size of the internal segment list
    size_t size( void )
    {
        return outline.size();
    }

    // returns true if the internal segment list is empty
    bool empty( void )
    {
        return outline.empty();
    }

    // return the front() of the internal segment list
    IDF_SEGMENT*& front( void )
    {
        return outline.front();
    }

    // return the back() of the internal segment list
    IDF_SEGMENT*& back( void )
    {
        return outline.back();
    }

    // return the begin() iterator of the internal segment list
    std::list<IDF_SEGMENT*>::iterator begin( void )
    {
        return outline.begin();
    }

    // return the end() iterator of the internal segment list
    std::list<IDF_SEGMENT*>::iterator end( void )
    {
        return outline.end();
    }

    // push a segment onto the internal list
    void push( IDF_SEGMENT* item );
};
#include <idf_common.h>


/**
Loading