kicad/utils/idftools/idf_common.h

649 lines
18 KiB
C++

/**
* @file idf_common.h
*/
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2017 Cirilo Bernardo
* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef IDF_COMMON_H
#define IDF_COMMON_H
#include <list>
#include <fstream>
#include <exception>
#include <string>
#include <cmath>
// differences in angle smaller than MIN_ANG are considered equal
#define MIN_ANG (0.01)
class IDF_POINT;
class IDF_SEGMENT;
class IDF_DRILL_DATA;
class IDF_OUTLINE;
class IDF_LIB;
struct IDF_ERROR : std::exception
{
std::string message;
IDF_ERROR( const char* aSourceFile, const char* aSourceMethod, int aSourceLine,
const std::string& aMessage ) noexcept;
virtual ~IDF_ERROR() noexcept;
virtual const char* what() const noexcept override;
};
namespace IDF3 {
/**
* State values for the IDF parser's input.
*/
enum FILE_STATE
{
FILE_START = 0, // no data has been read; expecting .HEADER
FILE_HEADER, // header has been read; expecting .BOARD_OUTLINE
FILE_OUTLINE, // board outline has been read; most sections can be accepted
FILE_PLACEMENT, // placement has been read; no further sections can be accepted
FILE_INVALID, // file is invalid
FILE_ERROR // other errors while processing the file
};
/**
* The supported IDF versions (3.0 and 2.0 ONLY).
*/
enum IDF_VERSION
{
IDF_V2 = 0, // version 2 has read support only; files written as IDFv3
IDF_V3 // version 3 has full read/write support
};
/**
* The type of CAD which has ownership an object.
*/
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
};
/**
* The purpose of an IDF hole.
*/
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
};
/**
* The plating condition of a hole.
*/
enum KEY_PLATING
{
PTH = 0, //< Plate-Through Hole
NPTH //< Non-Plate-Through Hole
};
/**
* A component's Reference Designator.
*/
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
};
/**
* The class of CAD program which is opening or modifying a file.
*/
enum CAD_TYPE
{
CAD_ELEC = 0, //< An Electrical CAD is opening/modifying the file
CAD_MECH, //< A Mechanical CAD is opening/modifying the file
CAD_INVALID
};
/**
* The various IDF layer classes and groupings.
*/
enum IDF_LAYER
{
LYR_TOP = 0,
LYR_BOTTOM,
LYR_BOTH,
LYR_INNER,
LYR_ALL,
LYR_INVALID
};
/**
* The class of outline.
*/
enum OUTLINE_TYPE
{
OTLN_BOARD = 0,
OTLN_OTHER,
OTLN_PLACE,
OTLN_ROUTE,
OTLN_PLACE_KEEPOUT,
OTLN_ROUTE_KEEPOUT,
OTLN_VIA_KEEPOUT,
OTLN_GROUP_PLACE,
OTLN_COMPONENT,
OTLN_INVALID
};
/**
* Whether a component is a mechanical or electrical part.
*/
enum COMP_TYPE
{
COMP_ELEC = 0, //< Component library object is an electrical part
COMP_MECH, //< Component library object is a mechanical part
COMP_INVALID
};
/**
* The native unit of the board and of component outlines.
*/
enum IDF_UNIT
{
UNIT_MM = 0, //< Units in the file are in millimeters
UNIT_THOU, //< Units in the file are in mils (aka thou)
UNIT_TNM, //< Deprecated Ten Nanometer Units from IDFv2
UNIT_INVALID
};
/**
* The placement status of a component.
*/
enum IDF_PLACEMENT
{
PS_UNPLACED = 0, //< component location on the board has not been specified
PS_PLACED, //< component location has been specified and may be modified by ECAD or MCAD
PS_MCAD, //< component location has been specified and may only be modified by MCAD
PS_ECAD, //< component location has been specified and may only be modified by ECAD
PS_INVALID
};
/**
* Calculate the angle (radians) between the horizon and the segment aStartPoint to aEndPoint.
*
* @param aStartPoint is the start point of a line segment.
* @param aEndPoint is the end point of a line segment.
* @return the angle in radians.
*/
double CalcAngleRad( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );
/**
* Calculate the angle (degrees) between the horizon and the segment aStartPoint to aEndPoint.
*
* @param aStartPoint is the start point of a line segment.
* @param aEndPoint is the end point of a line segment.
* @return the angle in degrees.
*/
double CalcAngleDeg( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );
/**
* Take contiguous elements from 'aLines' and stuffs them into 'aOutline'; elements put
* into the outline are deleted from aLines.
*
* This function is useful for sorting the jumbled mess of line segments and arcs which represent
* a board outline and cutouts in KiCad. The function will determine which segment element within
* aLines contains the leftmost point and retrieve the outline of which that segment is part.
*
* @param aLines (input/output) is a list of IDF segments which comprise an outline and cutouts.
* @param aOutline (output) is the ordered set of segments/
*/
void GetOutline( std::list<IDF_SEGMENT*>& aLines, IDF_OUTLINE& aOutline );
#ifdef DEBUG_IDF
// prints out segment information for debug purposes
void PrintSeg( IDF_SEGMENT* aSegment );
#endif
}
/**
* An entry in the NOTE section of an IDF file.
*/
class IDF_NOTE
{
public:
IDF_NOTE();
/**
* Set the text to be stored as a NOTE entry.
*/
void SetText( const std::string& aText );
/**
* Set the position (mm) of the NOTE entry.
*/
void SetPosition( double aXpos, double aYpos );
/**
* Set the height and length (mm) of the NOTE entry.
*/
void SetSize( double aHeight, double aLength );
/**
* @return the string stored in the note entry.
*/
const std::string& GetText();
/**
* @return the position (mm) of the note entry.
*/
void GetPosition( double& aXpos, double& aYpos );
/**
* @return the height and length (mm) of the note entry.
*/
void GetSize( double& aHeight, double& aLength );
private:
friend class IDF3_BOARD;
/**
* Read a note entry from an IDFv3 file.
*
* @param aBoardFile is an open BOARD file; the file position must be set to the start of
* a NOTE entry.
* @param aBoardState is the parser's current state value.
* @param aBoardUnit is the BOARD file's native units (MM or THOU).
* @return true if a note item was read, false otherwise.
* @throw In case of unrecoverable errors.
*/
bool readNote( std::istream& aBoardFile, IDF3::FILE_STATE& aBoardState,
IDF3::IDF_UNIT aBoardUnit );
/**
* Write a note entry to an IDFv3 file.
*
* @param aBoardFile is an open BOARD file; the file position must be within a NOTE section.
* @param aBoardUnit is the BOARD file's native units (MM or THOU).
* @return true if the item was successfully written, false otherwise.
* @throw In case of unrecoverable error.
*/
bool writeNote( std::ostream& aBoardFile, IDF3::IDF_UNIT aBoardUnit );
std::string text; // note text as per IDFv3
double xpos; // text X position as per IDFv3
double ypos; // text Y position as per IDFv3
double height; // text height as per IDFv3
double length; // text length as per IDFv3
};
/**
* A drilled hole.
*
* Responsible for writing this information to a file in compliance with the IDFv3 specification.
*/
class IDF_DRILL_DATA
{
public:
/**
* Create an empty drill entry which can be populated by the read() function.
*/
IDF_DRILL_DATA();
/**
* Create a drill entry with information compliant with the IDFv3 specifications.
*
* @param aDrillDia is the drill diameter.
* @param aPosX is the X coordinate of the drill center.
* @param aPosY is the Y coordinate of the drill center.
* @param aPlating is a plating flag, PTH or NPTH.
* @param aRefDes is the component Reference Designator.
* @param aHoleType is the type of hole.
* @param aOwner is one of MCAD, ECAD, UNOWNED.
*/
IDF_DRILL_DATA( double aDrillDia, double aPosX, double aPosY,
IDF3::KEY_PLATING aPlating,
const std::string& aRefDes,
const std::string& aHoleType,
IDF3::KEY_OWNER aOwner );
/**
* Return true if the given drill diameter and location matches the diameter and location
* of this IDF_DRILL_DATA object.
*
* @param aDrillDia is the drill diameter (mm).
* @param aPosX is the X position (mm) of the drilled hole.
* @param aPosY is the Y position (mm) of the drilled hole.
* @return true if the diameter and position match this object.
*/
bool Matches( double aDrillDia, double aPosX, double aPosY ) const;
/**
* @return the drill diameter in mm.
*/
double GetDrillDia() const;
/**
* @return the drill's X position in mm.
*/
double GetDrillXPos() const;
/**
* @return the drill's Y position in mm.
*/
double GetDrillYPos() const;
/**
* @return the plating value (PTH, NPTH).
*/
IDF3::KEY_PLATING GetDrillPlating();
/**
* @return the reference designator of the hole; this may be a component reference designator,
* BOARD, or NOREFDES as per IDFv3.
*/
const std::string& GetDrillRefDes();
/**
* @return the classification of the hole; this may be one of PIN, VIA, MTG, TOOL, or a
* user-specified string.
*/
const std::string& GetDrillHoleType();
IDF3::KEY_OWNER GetDrillOwner() const
{
return owner;
}
private:
friend class IDF3_BOARD;
friend class IDF3_COMPONENT;
/**
* Read a drill entry from an IDFv3 file
*
* @param aBoardFile is an open IDFv3 file; the file position must be within the DRILLED_HOLES
* section.
* @param aBoardUnit is the board file's native unit (MM or THOU).
* @param aBoardState is the state value of the parser.
* @return true if data was successfully read, otherwise false.
* @throw in case of an unrecoverable error.
*/
bool read( std::istream& aBoardFile, IDF3::IDF_UNIT aBoardUnit, IDF3::FILE_STATE aBoardState,
IDF3::IDF_VERSION aIdfVersion );
/**
* Write a single line representing a hole within a .DRILLED_HOLES section.
*
* @param aBoardFile is an open BOARD file
* @param aBoardUnit is the native unit of the output file
* @throw in case of an unrecoverable error.
*/
void write( std::ostream& aBoardFile, IDF3::IDF_UNIT aBoardUnit );
double dia;
double x;
double y;
IDF3::KEY_PLATING plating;
IDF3::KEY_REFDES kref;
IDF3::KEY_HOLETYPE khole;
std::string refdes;
std::string holetype;
IDF3::KEY_OWNER owner;
};
/**
* A point as used by the various IDF related classes.
*/
class IDF_POINT
{
public:
IDF_POINT()
{
x = 0.0;
y = 0.0;
}
IDF_POINT( double aX, double aY )
{
x = aX;
y = aY;
}
/**
* Return true if the given coordinate point is within the given radius of the point.
*
* @param aPoint is the coordinates of the point being compared.
* @param aRadius is the radius (mm) within which the points are considered the same.
* @return true if this point matches the given point.
*/
bool Matches( const IDF_POINT& aPoint, double aRadius = 1e-5 ) const;
/**
* Return the Euclidean distance between this point and the given point.
*
* @param aPoint is the coordinates of the point whose distance is to be determined.
* @return double is the distance between this point and aPoint.
*/
double CalcDistance( const IDF_POINT& aPoint ) const;
double x; // < X coordinate
double y; // < Y coordinate
};
/**
* A segment as used in IDFv3 outlines.
*
* It may be any of an arc, line segment, or circle
*/
class IDF_SEGMENT
{
public:
/**
* Initialize the internal variables.
*/
IDF_SEGMENT();
/**
* Create a straight segment.
*/
IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );
/**
* Create a straight segment, arc, or circle depending on the angle.
*
* @param aStartPoint is the start point (center if using KiCad convention, otherwise IDF
* convention)
* @param aEndPoint is the end point (start of arc if using KiCad convention, otherwise IDF
* convention)
* @param aAngle is the included angle; the KiCad convention is equivalent to the IDF convention
* @param fromKicad set to 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 );
/**
* Return true if the given coordinate is within a radius 'rad' of the start point.
*
* @param aPoint are the coordinates of the point (mm) being compared.
* @param aRadius is the radius (mm) within which the points are considered the same.
* @return true if the given point matches the start point of this segment.
*/
bool MatchesStart( const IDF_POINT& aPoint, double aRadius = 1e-3 );
/**
* Return true if the given coordinate is within a radius 'rad' of the end point.
*
* @param aPoint are the coordinates (mm) of the point being compared.
* @param aRadius is the radius (mm) within which the points are considered the same.
* @return true if the given point matches the end point of this segment.
*/
bool MatchesEnd( const IDF_POINT& aPoint, double aRadius = 1e-3 );
/**
* @return true if this segment is a circle.
*/
bool IsCircle();
/**
* @return the minimum X coordinate of this segment.
*/
double GetMinX();
/**
* Swap the start and end points and alters internal variables as necessary for arcs.
*/
void SwapEnds();
private:
/**
* Calculate 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();
public:
IDF_POINT startPoint; ///< starting point coordinates in mm
IDF_POINT endPoint; ///< end point coordinates in mm
///< center of an arc or circle; internally calculated and not to be set by the user.
IDF_POINT center;
double angle; ///< included angle (degrees) according to IDFv3 specification
double offsetAngle; ///< angle between center and start of arc; internally calculated
double radius; ///< radius of the arc or circle; internally calculated
};
/**
* A segment and winding information for an IDF outline.
*/
class IDF_OUTLINE
{
public:
IDF_OUTLINE() { dir = 0.0; }
~IDF_OUTLINE() { Clear(); }
/**
* @return true if the current list of points represents a counterclockwise winding.
*/
bool IsCCW();
/**
* @returns true if this outline is a circle.
*/
bool IsCircle();
/**
* Clear the internal list of outline segments.
*/
void Clear()
{
dir = 0.0;
while( !outline.empty() )
{
delete outline.front();
outline.pop_front();
}
}
/**
* @return the size of the internal segment list.
*/
size_t size()
{
return outline.size();
}
/**
* @return true if the internal segment list is empty.
*/
bool empty()
{
return outline.empty();
}
/**
* @return the front() iterator of the internal segment list.
*/
IDF_SEGMENT*& front()
{
return outline.front();
}
/**
* @return the back() iterator of the internal segment list.
*/
IDF_SEGMENT*& back()
{
return outline.back();
}
/**
* @return the begin() iterator of the internal segment list.
*/
std::list<IDF_SEGMENT*>::iterator begin()
{
return outline.begin();
}
/**
* @return the end() iterator of the internal segment list.
*/
std::list<IDF_SEGMENT*>::iterator end()
{
return outline.end();
}
/**
* Add a segment to the internal segment list.
*
* Segments must be added in order so that startPoint[N] == endPoint[N - 1].
*
* @param item is a pointer to the segment to add to the outline.
* @return true if the segment was added, otherwise false (outline restrictions have been
* violated).
*/
bool push( IDF_SEGMENT* item );
private:
double dir; // accumulator to help determine winding direction
std::list<IDF_SEGMENT*> outline; // sequential segments comprising an outline
};
#endif // IDF_COMMON_H