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