1 /** 2 * @file idf_common.h 3 */ 4 5 /* 6 * This program source code file is part of KiCad, a free EDA CAD application. 7 * 8 * Copyright (C) 2013-2017 Cirilo Bernardo 9 * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. 10 * 11 * This program is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU General Public License 13 * as published by the Free Software Foundation; either version 2 14 * of the License, or (at your option) any later version. 15 * 16 * This program is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with this program; if not, you may find one here: 23 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 24 * or you may search the http://www.gnu.org website for the version 2 license, 25 * or you may write to the Free Software Foundation, Inc., 26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 27 */ 28 29 #ifndef IDF_COMMON_H 30 #define IDF_COMMON_H 31 32 #include <list> 33 #include <fstream> 34 #include <exception> 35 #include <string> 36 #include <cmath> 37 38 // differences in angle smaller than MIN_ANG are considered equal 39 #define MIN_ANG (0.01) 40 41 class IDF_POINT; 42 class IDF_SEGMENT; 43 class IDF_DRILL_DATA; 44 class IDF_OUTLINE; 45 class IDF_LIB; 46 47 48 struct IDF_ERROR : std::exception 49 { 50 std::string message; 51 52 IDF_ERROR( const char* aSourceFile, const char* aSourceMethod, int aSourceLine, 53 const std::string& aMessage ) noexcept; 54 55 virtual ~IDF_ERROR() noexcept; 56 57 virtual const char* what() const noexcept override; 58 }; 59 60 61 namespace IDF3 { 62 63 /** 64 * State values for the IDF parser's input. 65 */ 66 enum FILE_STATE 67 { 68 FILE_START = 0, // no data has been read; expecting .HEADER 69 FILE_HEADER, // header has been read; expecting .BOARD_OUTLINE 70 FILE_OUTLINE, // board outline has been read; most sections can be accepted 71 FILE_PLACEMENT, // placement has been read; no further sections can be accepted 72 FILE_INVALID, // file is invalid 73 FILE_ERROR // other errors while processing the file 74 }; 75 76 /** 77 * The supported IDF versions (3.0 and 2.0 ONLY). 78 */ 79 enum IDF_VERSION 80 { 81 IDF_V2 = 0, // version 2 has read support only; files written as IDFv3 82 IDF_V3 // version 3 has full read/write support 83 }; 84 85 /** 86 * The type of CAD which has ownership an object. 87 */ 88 enum KEY_OWNER 89 { 90 UNOWNED = 0, //< either MCAD or ECAD may modify a feature 91 MCAD, //< only MCAD may modify a feature 92 ECAD //< only ECAD may modify a feature 93 }; 94 95 /** 96 * The purpose of an IDF hole. 97 */ 98 enum KEY_HOLETYPE 99 { 100 PIN = 0, //< drill hole is for a pin 101 VIA, //< drill hole is for a via 102 MTG, //< drill hole is for mounting 103 TOOL, //< drill hole is for tooling 104 OTHER //< user has specified a custom type 105 }; 106 107 /** 108 * The plating condition of a hole. 109 */ 110 enum KEY_PLATING 111 { 112 PTH = 0, //< Plate-Through Hole 113 NPTH //< Non-Plate-Through Hole 114 }; 115 116 /** 117 * A component's Reference Designator. 118 */ 119 enum KEY_REFDES 120 { 121 BOARD = 0, //< feature is associated with the board 122 NOREFDES, //< feature is associated with a component with no RefDes 123 PANEL, //< feature is associated with an IDF panel 124 REFDES //< reference designator as assigned by the CAD software 125 }; 126 127 /** 128 * The class of CAD program which is opening or modifying a file. 129 */ 130 enum CAD_TYPE 131 { 132 CAD_ELEC = 0, //< An Electrical CAD is opening/modifying the file 133 CAD_MECH, //< A Mechanical CAD is opening/modifying the file 134 CAD_INVALID 135 }; 136 137 /** 138 * The various IDF layer classes and groupings. 139 */ 140 enum IDF_LAYER 141 { 142 LYR_TOP = 0, 143 LYR_BOTTOM, 144 LYR_BOTH, 145 LYR_INNER, 146 LYR_ALL, 147 LYR_INVALID 148 }; 149 150 /** 151 * The class of outline. 152 */ 153 enum OUTLINE_TYPE 154 { 155 OTLN_BOARD = 0, 156 OTLN_OTHER, 157 OTLN_PLACE, 158 OTLN_ROUTE, 159 OTLN_PLACE_KEEPOUT, 160 OTLN_ROUTE_KEEPOUT, 161 OTLN_VIA_KEEPOUT, 162 OTLN_GROUP_PLACE, 163 OTLN_COMPONENT, 164 OTLN_INVALID 165 }; 166 167 /** 168 * Whether a component is a mechanical or electrical part. 169 */ 170 enum COMP_TYPE 171 { 172 COMP_ELEC = 0, //< Component library object is an electrical part 173 COMP_MECH, //< Component library object is a mechanical part 174 COMP_INVALID 175 }; 176 177 /** 178 * The native unit of the board and of component outlines. 179 */ 180 enum IDF_UNIT 181 { 182 UNIT_MM = 0, //< Units in the file are in millimeters 183 UNIT_THOU, //< Units in the file are in mils (aka thou) 184 UNIT_TNM, //< Deprecated Ten Nanometer Units from IDFv2 185 UNIT_INVALID 186 }; 187 188 /** 189 * The placement status of a component. 190 */ 191 enum IDF_PLACEMENT 192 { 193 PS_UNPLACED = 0, //< component location on the board has not been specified 194 PS_PLACED, //< component location has been specified and may be modified by ECAD or MCAD 195 PS_MCAD, //< component location has been specified and may only be modified by MCAD 196 PS_ECAD, //< component location has been specified and may only be modified by ECAD 197 PS_INVALID 198 }; 199 200 /** 201 * Calculate the angle (radians) between the horizon and the segment aStartPoint to aEndPoint. 202 * 203 * @param aStartPoint is the start point of a line segment. 204 * @param aEndPoint is the end point of a line segment. 205 * @return the angle in radians. 206 */ 207 double CalcAngleRad( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint ); 208 209 /** 210 * Calculate the angle (degrees) between the horizon and the segment aStartPoint to aEndPoint. 211 * 212 * @param aStartPoint is the start point of a line segment. 213 * @param aEndPoint is the end point of a line segment. 214 * @return the angle in degrees. 215 */ 216 double CalcAngleDeg( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint ); 217 218 /** 219 * Take contiguous elements from 'aLines' and stuffs them into 'aOutline'; elements put 220 * into the outline are deleted from aLines. 221 * 222 * This function is useful for sorting the jumbled mess of line segments and arcs which represent 223 * a board outline and cutouts in KiCad. The function will determine which segment element within 224 * aLines contains the leftmost point and retrieve the outline of which that segment is part. 225 * 226 * @param aLines (input/output) is a list of IDF segments which comprise an outline and cutouts. 227 * @param aOutline (output) is the ordered set of segments/ 228 */ 229 void GetOutline( std::list<IDF_SEGMENT*>& aLines, IDF_OUTLINE& aOutline ); 230 231 #ifdef DEBUG_IDF 232 // prints out segment information for debug purposes 233 void PrintSeg( IDF_SEGMENT* aSegment ); 234 #endif 235 } 236 237 238 /** 239 * An entry in the NOTE section of an IDF file. 240 */ 241 class IDF_NOTE 242 { 243 public: 244 IDF_NOTE(); 245 246 /** 247 * Set the text to be stored as a NOTE entry. 248 */ 249 void SetText( const std::string& aText ); 250 251 /** 252 * Set the position (mm) of the NOTE entry. 253 */ 254 void SetPosition( double aXpos, double aYpos ); 255 256 /** 257 * Set the height and length (mm) of the NOTE entry. 258 */ 259 void SetSize( double aHeight, double aLength ); 260 261 /** 262 * @return the string stored in the note entry. 263 */ 264 const std::string& GetText(); 265 266 /** 267 * @return the position (mm) of the note entry. 268 */ 269 void GetPosition( double& aXpos, double& aYpos ); 270 271 /** 272 * @return the height and length (mm) of the note entry. 273 */ 274 void GetSize( double& aHeight, double& aLength ); 275 276 private: 277 friend class IDF3_BOARD; 278 279 /** 280 * Read a note entry from an IDFv3 file. 281 * 282 * @param aBoardFile is an open BOARD file; the file position must be set to the start of 283 * a NOTE entry. 284 * @param aBoardState is the parser's current state value. 285 * @param aBoardUnit is the BOARD file's native units (MM or THOU). 286 * @return true if a note item was read, false otherwise. 287 * @throw In case of unrecoverable errors. 288 */ 289 bool readNote( std::istream& aBoardFile, IDF3::FILE_STATE& aBoardState, 290 IDF3::IDF_UNIT aBoardUnit ); 291 292 /** 293 * Write a note entry to an IDFv3 file. 294 * 295 * @param aBoardFile is an open BOARD file; the file position must be within a NOTE section. 296 * @param aBoardUnit is the BOARD file's native units (MM or THOU). 297 * @return true if the item was successfully written, false otherwise. 298 * @throw In case of unrecoverable error. 299 */ 300 bool writeNote( std::ostream& aBoardFile, IDF3::IDF_UNIT aBoardUnit ); 301 302 std::string text; // note text as per IDFv3 303 double xpos; // text X position as per IDFv3 304 double ypos; // text Y position as per IDFv3 305 double height; // text height as per IDFv3 306 double length; // text length as per IDFv3 307 }; 308 309 310 /** 311 * A drilled hole. 312 * 313 * Responsible for writing this information to a file in compliance with the IDFv3 specification. 314 */ 315 class IDF_DRILL_DATA 316 { 317 public: 318 /** 319 * Create an empty drill entry which can be populated by the read() function. 320 */ 321 IDF_DRILL_DATA(); 322 323 /** 324 * Create a drill entry with information compliant with the IDFv3 specifications. 325 * 326 * @param aDrillDia is the drill diameter. 327 * @param aPosX is the X coordinate of the drill center. 328 * @param aPosY is the Y coordinate of the drill center. 329 * @param aPlating is a plating flag, PTH or NPTH. 330 * @param aRefDes is the component Reference Designator. 331 * @param aHoleType is the type of hole. 332 * @param aOwner is one of MCAD, ECAD, UNOWNED. 333 */ 334 IDF_DRILL_DATA( double aDrillDia, double aPosX, double aPosY, 335 IDF3::KEY_PLATING aPlating, 336 const std::string& aRefDes, 337 const std::string& aHoleType, 338 IDF3::KEY_OWNER aOwner ); 339 340 /** 341 * Return true if the given drill diameter and location matches the diameter and location 342 * of this IDF_DRILL_DATA object. 343 * 344 * @param aDrillDia is the drill diameter (mm). 345 * @param aPosX is the X position (mm) of the drilled hole. 346 * @param aPosY is the Y position (mm) of the drilled hole. 347 * @return true if the diameter and position match this object. 348 */ 349 bool Matches( double aDrillDia, double aPosX, double aPosY ) const; 350 351 /** 352 * @return the drill diameter in mm. 353 */ 354 double GetDrillDia() const; 355 356 /** 357 * @return the drill's X position in mm. 358 */ 359 double GetDrillXPos() const; 360 361 /** 362 * @return the drill's Y position in mm. 363 */ 364 double GetDrillYPos() const; 365 366 /** 367 * @return the plating value (PTH, NPTH). 368 */ 369 IDF3::KEY_PLATING GetDrillPlating(); 370 371 /** 372 * @return the reference designator of the hole; this may be a component reference designator, 373 * BOARD, or NOREFDES as per IDFv3. 374 */ 375 const std::string& GetDrillRefDes(); 376 377 /** 378 * @return the classification of the hole; this may be one of PIN, VIA, MTG, TOOL, or a 379 * user-specified string. 380 */ 381 const std::string& GetDrillHoleType(); 382 GetDrillOwner()383 IDF3::KEY_OWNER GetDrillOwner() const 384 { 385 return owner; 386 } 387 388 private: 389 friend class IDF3_BOARD; 390 friend class IDF3_COMPONENT; 391 392 /** 393 * Read a drill entry from an IDFv3 file 394 * 395 * @param aBoardFile is an open IDFv3 file; the file position must be within the DRILLED_HOLES 396 * section. 397 * @param aBoardUnit is the board file's native unit (MM or THOU). 398 * @param aBoardState is the state value of the parser. 399 * @return true if data was successfully read, otherwise false. 400 * @throw in case of an unrecoverable error. 401 */ 402 bool read( std::istream& aBoardFile, IDF3::IDF_UNIT aBoardUnit, IDF3::FILE_STATE aBoardState, 403 IDF3::IDF_VERSION aIdfVersion ); 404 405 /** 406 * Write a single line representing a hole within a .DRILLED_HOLES section. 407 * 408 * @param aBoardFile is an open BOARD file 409 * @param aBoardUnit is the native unit of the output file 410 * @throw in case of an unrecoverable error. 411 */ 412 void write( std::ostream& aBoardFile, IDF3::IDF_UNIT aBoardUnit ); 413 414 double dia; 415 double x; 416 double y; 417 IDF3::KEY_PLATING plating; 418 IDF3::KEY_REFDES kref; 419 IDF3::KEY_HOLETYPE khole; 420 std::string refdes; 421 std::string holetype; 422 IDF3::KEY_OWNER owner; 423 }; 424 425 426 /** 427 * A point as used by the various IDF related classes. 428 */ 429 class IDF_POINT 430 { 431 public: IDF_POINT()432 IDF_POINT() 433 { 434 x = 0.0; 435 y = 0.0; 436 } 437 IDF_POINT(double aX,double aY)438 IDF_POINT( double aX, double aY ) 439 { 440 x = aX; 441 y = aY; 442 } 443 444 /** 445 * Return true if the given coordinate point is within the given radius of the point. 446 * 447 * @param aPoint is the coordinates of the point being compared. 448 * @param aRadius is the radius (mm) within which the points are considered the same. 449 * @return true if this point matches the given point. 450 */ 451 bool Matches( const IDF_POINT& aPoint, double aRadius = 1e-5 ) const; 452 453 /** 454 * Return the Euclidean distance between this point and the given point. 455 * 456 * @param aPoint is the coordinates of the point whose distance is to be determined. 457 * @return double is the distance between this point and aPoint. 458 */ 459 double CalcDistance( const IDF_POINT& aPoint ) const; 460 461 double x; // < X coordinate 462 double y; // < Y coordinate 463 }; 464 465 466 /** 467 * A segment as used in IDFv3 outlines. 468 * 469 * It may be any of an arc, line segment, or circle 470 */ 471 class IDF_SEGMENT 472 { 473 public: 474 /** 475 * Initialize the internal variables. 476 */ 477 IDF_SEGMENT(); 478 479 /** 480 * Create a straight segment. 481 */ 482 IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint ); 483 484 /** 485 * Create a straight segment, arc, or circle depending on the angle. 486 * 487 * @param aStartPoint is the start point (center if using KiCad convention, otherwise IDF 488 * convention) 489 * @param aEndPoint is the end point (start of arc if using KiCad convention, otherwise IDF 490 * convention) 491 * @param aAngle is the included angle; the KiCad convention is equivalent to the IDF convention 492 * @param fromKicad set to true if we need to convert from KiCad to IDF convention. 493 */ 494 IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint, double aAngle, 495 bool aFromKicad ); 496 497 /** 498 * Return true if the given coordinate is within a radius 'rad' of the start point. 499 * 500 * @param aPoint are the coordinates of the point (mm) being compared. 501 * @param aRadius is the radius (mm) within which the points are considered the same. 502 * @return true if the given point matches the start point of this segment. 503 */ 504 bool MatchesStart( const IDF_POINT& aPoint, double aRadius = 1e-3 ); 505 506 /** 507 * Return true if the given coordinate is within a radius 'rad' of the end point. 508 * 509 * @param aPoint are the coordinates (mm) of the point being compared. 510 * @param aRadius is the radius (mm) within which the points are considered the same. 511 * @return true if the given point matches the end point of this segment. 512 */ 513 bool MatchesEnd( const IDF_POINT& aPoint, double aRadius = 1e-3 ); 514 515 /** 516 * @return true if this segment is a circle. 517 */ 518 bool IsCircle(); 519 520 /** 521 * @return the minimum X coordinate of this segment. 522 */ 523 double GetMinX(); 524 525 /** 526 * Swap the start and end points and alters internal variables as necessary for arcs. 527 */ 528 void SwapEnds(); 529 530 private: 531 /** 532 * Calculate the center, radius, and angle between center and start point given the 533 * IDF compliant points and included angle. 534 * 535 * @var startPoint, @var endPoint, and @var angle must be set prior as per IDFv3. 536 */ 537 void CalcCenterAndRadius(); 538 539 public: 540 IDF_POINT startPoint; ///< starting point coordinates in mm 541 IDF_POINT endPoint; ///< end point coordinates in mm 542 543 ///< center of an arc or circle; internally calculated and not to be set by the user. 544 IDF_POINT center; 545 double angle; ///< included angle (degrees) according to IDFv3 specification 546 double offsetAngle; ///< angle between center and start of arc; internally calculated 547 double radius; ///< radius of the arc or circle; internally calculated 548 }; 549 550 551 /** 552 * A segment and winding information for an IDF outline. 553 */ 554 class IDF_OUTLINE 555 { 556 public: IDF_OUTLINE()557 IDF_OUTLINE() { dir = 0.0; } ~IDF_OUTLINE()558 ~IDF_OUTLINE() { Clear(); } 559 560 /** 561 * @return true if the current list of points represents a counterclockwise winding. 562 */ 563 bool IsCCW(); 564 565 /** 566 * @returns true if this outline is a circle. 567 */ 568 bool IsCircle(); 569 570 /** 571 * Clear the internal list of outline segments. 572 */ Clear()573 void Clear() 574 { 575 dir = 0.0; 576 577 while( !outline.empty() ) 578 { 579 delete outline.front(); 580 outline.pop_front(); 581 } 582 } 583 584 /** 585 * @return the size of the internal segment list. 586 */ size()587 size_t size() 588 { 589 return outline.size(); 590 } 591 592 /** 593 * @return true if the internal segment list is empty. 594 */ empty()595 bool empty() 596 { 597 return outline.empty(); 598 } 599 600 /** 601 * @return the front() iterator of the internal segment list. 602 */ front()603 IDF_SEGMENT*& front() 604 { 605 return outline.front(); 606 } 607 608 /** 609 * @return the back() iterator of the internal segment list. 610 */ back()611 IDF_SEGMENT*& back() 612 { 613 return outline.back(); 614 } 615 616 /** 617 * @return the begin() iterator of the internal segment list. 618 */ begin()619 std::list<IDF_SEGMENT*>::iterator begin() 620 { 621 return outline.begin(); 622 } 623 624 /** 625 * @return the end() iterator of the internal segment list. 626 */ end()627 std::list<IDF_SEGMENT*>::iterator end() 628 { 629 return outline.end(); 630 } 631 632 /** 633 * Add a segment to the internal segment list. 634 * 635 * Segments must be added in order so that startPoint[N] == endPoint[N - 1]. 636 * 637 * @param item is a pointer to the segment to add to the outline. 638 * @return true if the segment was added, otherwise false (outline restrictions have been 639 * violated). 640 */ 641 bool push( IDF_SEGMENT* item ); 642 643 private: 644 double dir; // accumulator to help determine winding direction 645 std::list<IDF_SEGMENT*> outline; // sequential segments comprising an outline 646 }; 647 648 #endif // IDF_COMMON_H 649