Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
K
kicad-source-mirror
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
Elphel
kicad-source-mirror
Commits
7a110d0c
Commit
7a110d0c
authored
Jun 05, 2014
by
unknown
Committed by
jean-pierre charras
Jun 05, 2014
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
IDF tools: code cleanup and debugging
parent
60a86853
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
440 additions
and
2623 deletions
+440
-2623
CMakeLists.txt
pcbnew/CMakeLists.txt
+4
-2
dialog_export_idf.cpp
pcbnew/dialogs/dialog_export_idf.cpp
+1
-1
export_idf.cpp
pcbnew/exporters/export_idf.cpp
+202
-35
idf.cpp
pcbnew/exporters/idf.cpp
+0
-1443
idf.h
pcbnew/exporters/idf.h
+0
-321
idf_common.cpp
pcbnew/exporters/idf_common.cpp
+0
-484
idf_common.h
pcbnew/exporters/idf_common.h
+0
-290
test_donut.emn
utils/idftools/idf_examples/test_donut.emn
+45
-0
test_donut.emp
utils/idftools/idf_examples/test_donut.emp
+5
-0
idf_outlines.cpp
utils/idftools/idf_outlines.cpp
+4
-0
idf_parser.cpp
utils/idftools/idf_parser.cpp
+172
-33
idf_parser.h
utils/idftools/idf_parser.h
+7
-3
vrml_layer.cpp
utils/idftools/vrml_layer.cpp
+0
-11
No files found.
pcbnew/CMakeLists.txt
View file @
7a110d0c
...
...
@@ -36,6 +36,7 @@ include_directories(
./exporters
../lib_dxf
./import_dxf
../utils/idftools
${
INC_AFTER
}
)
...
...
@@ -136,8 +137,6 @@ 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
exporters/gendrill_Excellon_writer.cpp
...
...
@@ -385,6 +384,7 @@ if( KICAD_SCRIPTING_MODULES )
common
pcad2kicadpcb
lib_dxf
idf3
${
GITHUB_PLUGIN_LIBRARIES
}
polygon
bitmaps
...
...
@@ -565,6 +565,7 @@ if( USE_KIWAY_DLLS )
bitmaps
gal
lib_dxf
idf3
${
GITHUB_PLUGIN_LIBRARIES
}
${
wxWidgets_LIBRARIES
}
${
OPENGL_LIBRARIES
}
...
...
@@ -633,6 +634,7 @@ else() # milestone A) kills this off:
bitmaps
gal
lib_dxf
idf3
${
GITHUB_PLUGIN_LIBRARIES
}
${
wxWidgets_LIBRARIES
}
${
OPENGL_LIBRARIES
}
...
...
pcbnew/dialogs/dialog_export_idf.cpp
View file @
7a110d0c
...
...
@@ -36,7 +36,7 @@
#define OPTKEY_IDF_THOU wxT( "IDFExportThou" )
bool
Export_IDF3
(
BOARD
*
aPcb
,
const
wxString
&
aFullFileName
,
double
aUseThou
);
bool
Export_IDF3
(
BOARD
*
aPcb
,
const
wxString
&
aFullFileName
,
bool
aUseThou
);
class
DIALOG_EXPORT_IDF3
:
public
DIALOG_EXPORT_IDF3_BASE
...
...
pcbnew/exporters/export_idf.cpp
View file @
7a110d0c
...
...
@@ -33,8 +33,9 @@
#include <class_board.h>
#include <class_module.h>
#include <class_edge_mod.h>
#include <idf.h>
#include <idf
_parser
.h>
#include <3d_struct.h>
#include <build_version.h>
// assumed default graphical line thickness: 10000 IU == 0.1mm
#define LINE_WIDTH (100000)
...
...
@@ -45,15 +46,15 @@
* the data into a form which can be output as an IDFv3 compliant
* BOARD_OUTLINE section.
*/
static
void
idf_export_outline
(
BOARD
*
aPcb
,
IDF_BOARD
&
aIDFBoard
)
static
void
idf_export_outline
(
BOARD
*
aPcb
,
IDF
3
_BOARD
&
aIDFBoard
)
{
double
scale
=
aIDFBoard
.
GetScale
();
double
scale
=
aIDFBoard
.
Get
User
Scale
();
DRAWSEGMENT
*
graphic
;
// KiCad graphical item
IDF_POINT
sp
,
ep
;
// start and end points from KiCad item
std
::
list
<
IDF_SEGMENT
*
>
lines
;
// IDF intermediate form of KiCad graphical item
IDF_OUTLINE
outline
;
// graphical items forming an outline or cutout
IDF_OUTLINE
*
outline
=
NULL
;
// graphical items forming an outline or cutout
// NOTE: IMPLEMENTATION
// If/when component cutouts are allowed, we must implement them separately. Cutouts
...
...
@@ -61,7 +62,7 @@ static void idf_export_outline( BOARD* aPcb, IDF_BOARD& aIDFBoard )
// The module cutouts should be handled via the idf_export_module() routine.
double
offX
,
offY
;
aIDFBoard
.
GetOffset
(
offX
,
offY
);
aIDFBoard
.
Get
User
Offset
(
offX
,
offY
);
// Retrieve segments and arcs from the board
for
(
BOARD_ITEM
*
item
=
aPcb
->
m_Drawings
;
item
;
item
=
item
->
Next
()
)
...
...
@@ -129,22 +130,31 @@ static void idf_export_outline( BOARD* aPcb, IDF_BOARD& aIDFBoard )
// note: we do not use a try/catch block here since we intend
// to simply ignore unclosed loops and continue processing
// until we're out of segments to process
IDF3
::
GetOutline
(
lines
,
outline
);
outline
=
new
IDF_OUTLINE
;
IDF3
::
GetOutline
(
lines
,
*
outline
);
if
(
outline
.
empty
()
)
if
(
outline
->
empty
()
)
goto
UseBoundingBox
;
aIDFBoard
.
AddOutline
(
outline
);
aIDFBoard
.
AddBoardOutline
(
outline
);
outline
=
NULL
;
// get all cutouts and write them out
while
(
!
lines
.
empty
()
)
{
IDF3
::
GetOutline
(
lines
,
outline
);
if
(
!
outline
)
outline
=
new
IDF_OUTLINE
;
if
(
outline
.
empty
()
)
IDF3
::
GetOutline
(
lines
,
*
outline
);
if
(
outline
->
empty
()
)
{
outline
->
Clear
();
continue
;
}
aIDFBoard
.
AddOutline
(
outline
);
aIDFBoard
.
AddBoardOutline
(
outline
);
outline
=
NULL
;
}
return
;
...
...
@@ -158,7 +168,10 @@ UseBoundingBox:
lines
.
pop_front
();
}
outline
.
Clear
();
if
(
outline
)
outline
->
Clear
();
else
outline
=
new
IDF_OUTLINE
;
// fetch a rectangular bounding box for the board;
// there is always some uncertainty in the board dimensions
...
...
@@ -192,7 +205,7 @@ UseBoundingBox:
p2
.
x
=
px
[
0
];
p2
.
y
=
py
[
0
];
outline
.
push
(
new
IDF_SEGMENT
(
p1
,
p2
)
);
outline
->
push
(
new
IDF_SEGMENT
(
p1
,
p2
)
);
for
(
int
i
=
1
;
i
<
4
;
++
i
)
{
...
...
@@ -201,10 +214,10 @@ UseBoundingBox:
p2
.
x
=
px
[
i
];
p2
.
y
=
py
[
i
];
outline
.
push
(
new
IDF_SEGMENT
(
p1
,
p2
)
);
outline
->
push
(
new
IDF_SEGMENT
(
p1
,
p2
)
);
}
aIDFBoard
.
AddOutline
(
outline
);
aIDFBoard
.
Add
Board
Outline
(
outline
);
}
...
...
@@ -216,7 +229,7 @@ UseBoundingBox:
* the library ELECTRICAL section.
*/
static
void
idf_export_module
(
BOARD
*
aPcb
,
MODULE
*
aModule
,
IDF_BOARD
&
aIDFBoard
)
IDF
3
_BOARD
&
aIDFBoard
)
{
// Reference Designator
std
::
string
crefdes
=
TO_UTF8
(
aModule
->
GetReference
()
);
...
...
@@ -243,14 +256,14 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule,
// Export pads
double
drill
,
x
,
y
;
double
scale
=
aIDFBoard
.
GetScale
();
double
scale
=
aIDFBoard
.
Get
User
Scale
();
IDF3
::
KEY_PLATING
kplate
;
std
::
string
pintype
;
std
::
string
tstr
;
double
dx
,
dy
;
aIDFBoard
.
GetOffset
(
dx
,
dy
);
aIDFBoard
.
Get
User
Offset
(
dx
,
dy
);
for
(
D_PAD
*
pad
=
aModule
->
Pads
();
pad
;
pad
=
pad
->
Next
()
)
{
...
...
@@ -313,7 +326,19 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule,
}
else
{
aIDFBoard
.
AddDrill
(
drill
,
x
,
y
,
kplate
,
crefdes
,
pintype
,
IDF3
::
ECAD
);
IDF_DRILL_DATA
*
dp
=
new
IDF_DRILL_DATA
(
drill
,
x
,
y
,
kplate
,
crefdes
,
pintype
,
IDF3
::
ECAD
);
if
(
!
aIDFBoard
.
AddDrill
(
dp
)
)
{
delete
dp
;
std
::
ostringstream
ostr
;
ostr
<<
__FILE__
<<
":"
<<
__LINE__
<<
":"
<<
__FUNCTION__
;
ostr
<<
"(): could not add drill"
;
throw
std
::
runtime_error
(
ostr
.
str
()
);
}
}
}
}
...
...
@@ -321,6 +346,8 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule,
// add any valid models to the library item list
std
::
string
refdes
;
IDF3_COMPONENT
*
comp
=
NULL
;
for
(
S3D_MASTER
*
modfile
=
aModule
->
Models
();
modfile
!=
0
;
modfile
=
modfile
->
Next
()
)
{
if
(
!
modfile
->
Is3DType
(
S3D_MASTER
::
FILE3D_IDF
)
)
...
...
@@ -330,14 +357,26 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule,
{
refdes
=
TO_UTF8
(
aModule
->
GetReference
()
);
// NOREFDES cannot be used or else the software gets confused
// when writing out the placement data due to conflicting
// placement and layer specifications; to work around this we
// create a (hopefully) unique refdes for our exported part.
if
(
refdes
.
empty
()
||
!
refdes
.
compare
(
"~"
)
)
refdes
=
aIDFBoard
.
GetRefDes
();
refdes
=
aIDFBoard
.
Get
New
RefDes
();
}
IDF3_COMP_OUTLINE
*
outline
;
outline
=
aIDFBoard
.
GetComponentOutline
(
modfile
->
GetShape3DName
()
);
if
(
!
outline
)
throw
(
std
::
runtime_error
(
aIDFBoard
.
GetError
()
)
);
double
rotz
=
aModule
->
GetOrientation
()
/
10.0
;
double
locx
=
modfile
->
m_MatPosition
.
x
;
double
locy
=
modfile
->
m_MatPosition
.
y
;
double
locz
=
modfile
->
m_MatPosition
.
z
;
double
lrot
=
modfile
->
m_MatRotation
.
z
;
bool
top
=
(
aModule
->
GetLayer
()
==
LAYER_N_BACK
)
?
false
:
true
;
...
...
@@ -348,12 +387,12 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule,
RotatePoint
(
&
locx
,
&
locy
,
aModule
->
GetOrientation
()
);
locy
=
-
locy
;
}
if
(
!
top
)
{
RotatePoint
(
&
locx
,
&
locy
,
aModule
->
GetOrientation
()
);
locy
=
-
locy
;
rotz
-=
modfile
->
m_MatRotation
.
z
;
rotz
=
180.0
-
rotz
;
if
(
rotz
>=
360.0
)
...
...
@@ -363,10 +402,97 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule,
while
(
rotz
<=
-
360.0
)
rotz
+=
360.0
;
}
locx
+=
aModule
->
GetPosition
().
x
*
scale
+
dx
;
locy
+=
-
aModule
->
GetPosition
().
y
*
scale
+
dy
;
if
(
comp
==
NULL
)
comp
=
aIDFBoard
.
FindComponent
(
refdes
)
;
aIDFBoard
.
PlaceComponent
(
modfile
->
GetShape3DName
(),
refdes
,
locx
,
locy
,
locz
,
rotz
,
top
);
if
(
comp
==
NULL
)
{
comp
=
new
IDF3_COMPONENT
(
&
aIDFBoard
);
if
(
comp
==
NULL
)
throw
(
std
::
runtime_error
(
aIDFBoard
.
GetError
()
)
);
comp
->
SetRefDes
(
refdes
);
if
(
top
)
comp
->
SetPosition
(
aModule
->
GetPosition
().
x
*
scale
+
dx
,
-
aModule
->
GetPosition
().
y
*
scale
+
dy
,
rotz
,
IDF3
::
LYR_TOP
);
else
comp
->
SetPosition
(
aModule
->
GetPosition
().
x
*
scale
+
dx
,
-
aModule
->
GetPosition
().
y
*
scale
+
dy
,
rotz
,
IDF3
::
LYR_BOTTOM
);
comp
->
SetPlacement
(
IDF3
::
PS_ECAD
);
aIDFBoard
.
AddComponent
(
comp
);
}
else
{
double
refX
,
refY
,
refA
;
IDF3
::
IDF_LAYER
side
;
if
(
!
comp
->
GetPosition
(
refX
,
refY
,
refA
,
side
)
)
{
// place the item
if
(
top
)
comp
->
SetPosition
(
aModule
->
GetPosition
().
x
*
scale
+
dx
,
-
aModule
->
GetPosition
().
y
*
scale
+
dy
,
rotz
,
IDF3
::
LYR_TOP
);
else
comp
->
SetPosition
(
aModule
->
GetPosition
().
x
*
scale
+
dx
,
-
aModule
->
GetPosition
().
y
*
scale
+
dy
,
rotz
,
IDF3
::
LYR_BOTTOM
);
}
else
{
// check that the retrieved component matches this one
refX
=
refX
-
(
aModule
->
GetPosition
().
x
*
scale
+
dx
);
refY
=
refY
-
(
-
aModule
->
GetPosition
().
y
*
scale
+
dy
);
refA
=
refA
-
rotz
;
refA
*=
refA
;
refX
*=
refX
;
refY
*=
refY
;
refX
+=
refY
;
// conditions: same side, X,Y coordinates within 10 microns,
// angle within 0.01 degree
if
(
(
top
&&
side
==
IDF3
::
LYR_BOTTOM
)
||
(
!
top
&&
side
==
IDF3
::
LYR_TOP
)
||
(
refA
>
0.0001
)
||
(
refX
>
0.0001
)
)
{
comp
->
GetPosition
(
refX
,
refY
,
refA
,
side
);
std
::
ostringstream
ostr
;
ostr
<<
"* "
<<
__FILE__
<<
":"
<<
__LINE__
<<
":"
<<
__FUNCTION__
<<
"():
\n
"
;
ostr
<<
"* conflicting Reference Designator '"
<<
refdes
<<
"'
\n
"
;
ostr
<<
"* X loc: "
<<
(
aModule
->
GetPosition
().
x
*
scale
+
dx
);
ostr
<<
" vs. "
<<
refX
<<
"
\n
"
;
ostr
<<
"* Y loc: "
<<
(
-
aModule
->
GetPosition
().
y
*
scale
+
dy
);
ostr
<<
" vs. "
<<
refY
<<
"
\n
"
;
ostr
<<
"* angle: "
<<
rotz
;
ostr
<<
" vs. "
<<
refA
<<
"
\n
"
;
if
(
top
)
ostr
<<
"* TOP vs. "
;
else
ostr
<<
"* BOTTOM vs. "
;
if
(
side
==
IDF3
::
LYR_TOP
)
ostr
<<
"TOP"
;
else
ostr
<<
"BOTTOM"
;
throw
(
std
::
runtime_error
(
ostr
.
str
()
)
);
}
}
}
// create the local data ...
IDF3_COMP_OUTLINE_DATA
*
data
=
new
IDF3_COMP_OUTLINE_DATA
(
comp
,
outline
);
data
->
SetOffsets
(
locx
,
locy
,
locz
,
lrot
);
comp
->
AddOutlineData
(
data
);
}
return
;
...
...
@@ -378,21 +504,45 @@ static void idf_export_module( BOARD* aPcb, MODULE* aModule,
* generates IDFv3 compliant board (*.emn) and library (*.emp)
* files representing the user's PCB design.
*/
bool
Export_IDF3
(
BOARD
*
aPcb
,
const
wxString
&
aFullFileName
,
double
aUseThou
)
bool
Export_IDF3
(
BOARD
*
aPcb
,
const
wxString
&
aFullFileName
,
bool
aUseThou
)
{
IDF
_BOARD
idfBoard
;
IDF
3_BOARD
idfBoard
(
IDF3
::
CAD_ELEC
)
;
SetLocaleTo_C_standard
();
try
bool
ok
=
true
;
double
scale
=
1e-6
;
// we must scale internal units to mm for IDF
IDF3
::
IDF_UNIT
idfUnit
;
if
(
aUseThou
)
{
idfUnit
=
IDF3
::
UNIT_THOU
;
idfBoard
.
SetUserPrecision
(
1
);
}
else
{
idfBoard
.
Setup
(
aPcb
->
GetFileName
(),
aFullFileName
,
aUseThou
,
aPcb
->
GetDesignSettings
().
GetBoardThickness
()
);
idfUnit
=
IDF3
::
UNIT_MM
;
idfBoard
.
SetUserPrecision
(
5
);
}
wxFileName
brdName
=
aPcb
->
GetFileName
();
idfBoard
.
SetUserScale
(
scale
);
idfBoard
.
SetBoardThickness
(
aPcb
->
GetDesignSettings
().
GetBoardThickness
()
*
scale
);
idfBoard
.
SetBoardName
(
TO_UTF8
(
brdName
.
GetFullName
()
)
);
idfBoard
.
SetBoardVersion
(
0
);
idfBoard
.
SetLibraryVersion
(
0
);
std
::
ostringstream
ostr
;
ostr
<<
"Created by KiCad "
<<
TO_UTF8
(
GetBuildVersion
()
);
idfBoard
.
SetIDFSource
(
ostr
.
str
()
);
try
{
// set up the global offsets
EDA_RECT
bbox
=
aPcb
->
ComputeBoundingBox
(
true
);
idfBoard
.
Set
Offset
(
-
bbox
.
Centre
().
x
*
idfBoard
.
GetScale
()
,
bbox
.
Centre
().
y
*
idfBoard
.
GetScale
()
);
idfBoard
.
Set
UserOffset
(
-
bbox
.
Centre
().
x
*
scale
,
bbox
.
Centre
().
y
*
scale
);
// Export the board outline
idf_export_outline
(
aPcb
,
idfBoard
);
...
...
@@ -401,15 +551,32 @@ bool Export_IDF3( BOARD* aPcb, const wxString& aFullFileName, double aUseThou )
for
(
MODULE
*
module
=
aPcb
->
m_Modules
;
module
!=
0
;
module
=
module
->
Next
()
)
idf_export_module
(
aPcb
,
module
,
idfBoard
);
idfBoard
.
Finish
();
if
(
!
idfBoard
.
WriteFile
(
aFullFileName
,
idfUnit
,
false
)
)
{
wxString
msg
;
msg
<<
_
(
"IDF Export Failed:
\n
"
)
<<
FROM_UTF8
(
idfBoard
.
GetError
().
c_str
()
);
wxMessageBox
(
msg
);
ok
=
false
;
}
}
catch
(
const
IO_ERROR
&
ioe
)
{
wxLogDebug
(
wxT
(
"An error occurred attemping export to IDFv3.
\n\n
Error: %s"
),
GetChars
(
ioe
.
errorText
)
);
wxString
msg
;
msg
<<
_
(
"IDF Export Failed:
\n
"
)
<<
ioe
.
errorText
;
wxMessageBox
(
msg
);
ok
=
false
;
}
catch
(
std
::
exception
&
e
)
{
wxString
msg
;
msg
<<
_
(
"IDF Export Failed:
\n
"
)
<<
FROM_UTF8
(
e
.
what
()
);
wxMessageBox
(
msg
);
ok
=
false
;
}
SetLocaleTo_Default
();
return
true
;
return
ok
;
}
pcbnew/exporters/idf.cpp
deleted
100644 → 0
View file @
60a86853
/**
* file: idf.cpp
*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2014 Cirilo Bernardo
*
* 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
*/
// TODO: Consider using different precision formats for THOU vs MM output
// Keep in mind that THOU cannot represent MM very well although MM can
// represent 1 THOU with 4 decimal places. For modern manufacturing we
// are interested in a resolution of about 0.1 THOU.
#include <list>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <cctype>
#include <strings.h>
#include <pgm_base.h>
#include <wx/config.h>
#include <wx/file.h>
#include <wx/filename.h>
#include <macros.h>
#include <richio.h>
#include <idf.h>
#include <build_version.h>
// minimum drill diameter (nanometers) - 10000 is a 0.01mm drill
#define IDF_MIN_DIA ( 10000.0 )
// minimum board thickness; this is about 0.012mm (0.5 mils)
// which is about the thickness of a single kapton layer typically
// used in a flexible design.
#define IDF_MIN_BRD_THICKNESS (12000)
// START: a few routines to help IDF_LIB but which may be of general use in the future
// as IDF support develops
// fetch a line from the given input file and trim the ends
static
bool
FetchIDFLine
(
std
::
ifstream
&
aModel
,
std
::
string
&
aLine
,
bool
&
isComment
);
// extract an IDF string and move the index to point to the character after the substring
static
bool
GetIDFString
(
const
std
::
string
&
aLine
,
std
::
string
&
aIDFString
,
bool
&
hasQuotes
,
int
&
aIndex
);
// END: IDF_LIB helper routines
IDF_DRILL_DATA
::
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
)
{
if
(
aDrillDia
<
0.3
)
dia
=
0.3
;
else
dia
=
aDrillDia
;
x
=
aPosX
;
y
=
aPosY
;
plating
=
aPlating
;
if
(
!
aRefDes
.
compare
(
"BOARD"
)
)
{
kref
=
IDF3
::
BOARD
;
}
else
if
(
aRefDes
.
empty
()
||
!
aRefDes
.
compare
(
"NOREFDES"
)
)
{
kref
=
IDF3
::
NOREFDES
;
}
else
if
(
!
aRefDes
.
compare
(
"PANEL"
)
)
{
kref
=
IDF3
::
PANEL
;
}
else
{
kref
=
IDF3
::
REFDES
;
refdes
=
aRefDes
;
}
if
(
!
aHoleType
.
compare
(
"PIN"
)
)
{
khole
=
IDF3
::
PIN
;
}
else
if
(
!
aHoleType
.
compare
(
"VIA"
)
)
{
khole
=
IDF3
::
VIA
;
}
else
if
(
aHoleType
.
empty
()
||
!
aHoleType
.
compare
(
"MTG"
)
)
{
khole
=
IDF3
::
MTG
;
}
else
if
(
!
aHoleType
.
compare
(
"TOOL"
)
)
{
khole
=
IDF3
::
TOOL
;
}
else
{
khole
=
IDF3
::
OTHER
;
holetype
=
aHoleType
;
}
owner
=
aOwner
;
}
// IDF_DRILL_DATA::IDF_DRILL_DATA( ... )
bool
IDF_DRILL_DATA
::
Write
(
FILE
*
aLayoutFile
)
{
// TODO: check stream integrity and return 'false' as appropriate
if
(
!
aLayoutFile
)
return
false
;
std
::
string
holestr
;
std
::
string
refstr
;
std
::
string
ownstr
;
std
::
string
pltstr
;
switch
(
khole
)
{
case
IDF3
:
:
PIN
:
holestr
=
"PIN"
;
break
;
case
IDF3
:
:
VIA
:
holestr
=
"VIA"
;
break
;
case
IDF3
:
:
TOOL
:
holestr
=
"TOOL"
;
break
;
case
IDF3
:
:
OTHER
:
holestr
=
"
\"
"
+
holetype
+
"
\"
"
;
break
;
default
:
holestr
=
"MTG"
;
break
;
}
switch
(
kref
)
{
case
IDF3
:
:
BOARD
:
refstr
=
"BOARD"
;
break
;
case
IDF3
:
:
PANEL
:
refstr
=
"PANEL"
;
break
;
case
IDF3
:
:
REFDES
:
refstr
=
"
\"
"
+
refdes
+
"
\"
"
;
break
;
default
:
refstr
=
"NOREFDES"
;
break
;
}
if
(
plating
==
IDF3
::
PTH
)
pltstr
=
"PTH"
;
else
pltstr
=
"NPTH"
;
switch
(
owner
)
{
case
IDF3
:
:
MCAD
:
ownstr
=
"MCAD"
;
break
;
case
IDF3
:
:
ECAD
:
ownstr
=
"ECAD"
;
break
;
default
:
ownstr
=
"UNOWNED"
;
}
fprintf
(
aLayoutFile
,
"%.3f %.5f %.5f %s %s %s %s
\n
"
,
dia
,
x
,
y
,
pltstr
.
c_str
(),
refstr
.
c_str
(),
holestr
.
c_str
(),
ownstr
.
c_str
()
);
return
true
;
}
// IDF_DRILL_DATA::Write( aLayoutFile )
IDF_BOARD
::
IDF_BOARD
()
{
refdesIndex
=
0
;
outlineIndex
=
0
;
scale
=
1e-6
;
boardThickness
=
1.6
;
// default to 1.6mm thick boards
useThou
=
false
;
// by default we want mm output
hasBrdOutlineHdr
=
false
;
layoutFile
=
NULL
;
libFile
=
NULL
;
}
IDF_BOARD
::~
IDF_BOARD
()
{
// simply close files if they are open; do not attempt
// anything else since a previous exception may have left
// data in a bad state.
if
(
layoutFile
!=
NULL
)
{
fclose
(
layoutFile
);
layoutFile
=
NULL
;
}
if
(
libFile
!=
NULL
)
{
fclose
(
libFile
);
libFile
=
NULL
;
}
}
bool
IDF_BOARD
::
Setup
(
wxString
aBoardName
,
wxString
aFullFileName
,
bool
aUseThou
,
int
aBoardThickness
)
{
if
(
aBoardThickness
<
IDF_MIN_BRD_THICKNESS
)
return
false
;
if
(
aUseThou
)
{
useThou
=
true
;
scale
=
1e-3
/
25.4
;
}
else
{
useThou
=
false
;
scale
=
1e-6
;
}
boardThickness
=
aBoardThickness
*
scale
;
wxFileName
brdname
(
aBoardName
);
wxFileName
idfname
(
aFullFileName
);
// open the layout file
idfname
.
SetExt
(
wxT
(
"emn"
)
);
layoutFile
=
wxFopen
(
aFullFileName
,
wxT
(
"wt"
)
);
if
(
layoutFile
==
NULL
)
return
false
;
// open the library file
idfname
.
SetExt
(
wxT
(
"emp"
)
);
libFile
=
wxFopen
(
idfname
.
GetFullPath
(),
wxT
(
"wt"
)
);
if
(
libFile
==
NULL
)
{
fclose
(
layoutFile
);
layoutFile
=
NULL
;
return
false
;
}
wxDateTime
tdate
(
time
(
NULL
)
);
fprintf
(
layoutFile
,
".HEADER
\n
"
"BOARD_FILE 3.0
\"
Created by KiCad %s
\"
"
" %.4u/%.2u/%.2u.%.2u:%.2u:%.2u 1
\n
"
"
\"
%s
\"
%s
\n
"
".END_HEADER
\n\n
"
,
TO_UTF8
(
GetBuildVersion
()
),
tdate
.
GetYear
(),
tdate
.
GetMonth
()
+
1
,
tdate
.
GetDay
(),
tdate
.
GetHour
(),
tdate
.
GetMinute
(),
tdate
.
GetSecond
(),
TO_UTF8
(
brdname
.
GetFullName
()
),
useThou
?
"THOU"
:
"MM"
);
fprintf
(
libFile
,
".HEADER
\n
"
"LIBRARY_FILE 3.0
\"
Created by KiCad %s
\"
%.4d/%.2d/%.2d.%.2d:%.2d:%.2d 1
\n
"
".END_HEADER
\n\n
"
,
TO_UTF8
(
GetBuildVersion
()
),
tdate
.
GetYear
(),
tdate
.
GetMonth
()
+
1
,
tdate
.
GetDay
(),
tdate
.
GetHour
(),
tdate
.
GetMinute
(),
tdate
.
GetSecond
()
);
return
true
;
}
bool
IDF_BOARD
::
Finish
(
void
)
{
// Steps to finalize the board and library files:
// 1. (emn) close the BOARD_OUTLINE section
// 2. (emn) write out the DRILLED_HOLES section
// 3. (emp) finalize the library file
// 4. (emn) write out the COMPONENT_PLACEMENT section
if
(
layoutFile
==
NULL
||
libFile
==
NULL
)
return
false
;
// Finalize the board outline section
fprintf
(
layoutFile
,
".END_BOARD_OUTLINE
\n\n
"
);
// Write out the drill section
bool
ok
=
WriteDrills
();
// populate the library (*.emp) file and write the
// PLACEMENT section
if
(
ok
)
ok
=
IDFLib
.
WriteFiles
(
layoutFile
,
libFile
);
fclose
(
libFile
);
libFile
=
NULL
;
fclose
(
layoutFile
);
layoutFile
=
NULL
;
return
ok
;
}
bool
IDF_BOARD
::
AddOutline
(
IDF_OUTLINE
&
aOutline
)
{
if
(
!
layoutFile
)
return
false
;
// TODO: check the stream integrity
std
::
list
<
IDF_SEGMENT
*>::
iterator
bo
;
std
::
list
<
IDF_SEGMENT
*>::
iterator
eo
;
if
(
!
hasBrdOutlineHdr
)
{
fprintf
(
layoutFile
,
".BOARD_OUTLINE ECAD
\n
%.5f
\n
"
,
boardThickness
);
hasBrdOutlineHdr
=
true
;
}
if
(
aOutline
.
size
()
==
1
)
{
if
(
!
aOutline
.
front
()
->
IsCircle
()
)
return
false
;
// this is a bad outline
// NOTE: a circle always has an angle of 360, never -360,
// otherwise SolidWorks chokes on the file.
fprintf
(
layoutFile
,
"%d %.5f %.5f 0
\n
"
,
outlineIndex
,
aOutline
.
front
()
->
startPoint
.
x
,
aOutline
.
front
()
->
startPoint
.
y
);
fprintf
(
layoutFile
,
"%d %.5f %.5f 360
\n
"
,
outlineIndex
,
aOutline
.
front
()
->
endPoint
.
x
,
aOutline
.
front
()
->
endPoint
.
y
);
++
outlineIndex
;
return
true
;
}
// ensure that the very last point is the same as the very first point
aOutline
.
back
()
->
endPoint
=
aOutline
.
front
()
->
startPoint
;
// check if we must reverse things
if
(
(
aOutline
.
IsCCW
()
&&
(
outlineIndex
>
0
)
)
||
(
(
!
aOutline
.
IsCCW
()
)
&&
(
outlineIndex
==
0
)
)
)
{
eo
=
aOutline
.
begin
();
bo
=
aOutline
.
end
();
--
bo
;
// for the first item we write out both points
if
(
aOutline
.
front
()
->
angle
<
MIN_ANG
&&
aOutline
.
front
()
->
angle
>
-
MIN_ANG
)
{
fprintf
(
layoutFile
,
"%d %.5f %.5f 0
\n
"
,
outlineIndex
,
aOutline
.
front
()
->
endPoint
.
x
,
aOutline
.
front
()
->
endPoint
.
y
);
fprintf
(
layoutFile
,
"%d %.5f %.5f 0
\n
"
,
outlineIndex
,
aOutline
.
front
()
->
startPoint
.
x
,
aOutline
.
front
()
->
startPoint
.
y
);
}
else
{
fprintf
(
layoutFile
,
"%d %.5f %.5f 0
\n
"
,
outlineIndex
,
aOutline
.
front
()
->
endPoint
.
x
,
aOutline
.
front
()
->
endPoint
.
y
);
fprintf
(
layoutFile
,
"%d %.5f %.5f %.5f
\n
"
,
outlineIndex
,
aOutline
.
front
()
->
startPoint
.
x
,
aOutline
.
front
()
->
startPoint
.
y
,
-
aOutline
.
front
()
->
angle
);
}
// for all other segments we only write out the start point
while
(
bo
!=
eo
)
{
if
(
(
*
bo
)
->
angle
<
MIN_ANG
&&
(
*
bo
)
->
angle
>
-
MIN_ANG
)
{
fprintf
(
layoutFile
,
"%d %.5f %.5f 0
\n
"
,
outlineIndex
,
(
*
bo
)
->
startPoint
.
x
,
(
*
bo
)
->
startPoint
.
y
);
}
else
{
fprintf
(
layoutFile
,
"%d %.5f %.5f %.5f
\n
"
,
outlineIndex
,
(
*
bo
)
->
startPoint
.
x
,
(
*
bo
)
->
startPoint
.
y
,
-
(
*
bo
)
->
angle
);
}
--
bo
;
}
}
else
{
bo
=
aOutline
.
begin
();
eo
=
aOutline
.
end
();
// for the first item we write out both points
if
(
(
*
bo
)
->
angle
<
MIN_ANG
&&
(
*
bo
)
->
angle
>
-
MIN_ANG
)
{
fprintf
(
layoutFile
,
"%d %.5f %.5f 0
\n
"
,
outlineIndex
,
(
*
bo
)
->
startPoint
.
x
,
(
*
bo
)
->
startPoint
.
y
);
fprintf
(
layoutFile
,
"%d %.5f %.5f 0
\n
"
,
outlineIndex
,
(
*
bo
)
->
endPoint
.
x
,
(
*
bo
)
->
endPoint
.
y
);
}
else
{
fprintf
(
layoutFile
,
"%d %.5f %.5f 0
\n
"
,
outlineIndex
,
(
*
bo
)
->
startPoint
.
x
,
(
*
bo
)
->
startPoint
.
y
);
fprintf
(
layoutFile
,
"%d %.5f %.5f %.5f
\n
"
,
outlineIndex
,
(
*
bo
)
->
endPoint
.
x
,
(
*
bo
)
->
endPoint
.
y
,
(
*
bo
)
->
angle
);
}
++
bo
;
// for all other segments we only write out the last point
while
(
bo
!=
eo
)
{
if
(
(
*
bo
)
->
angle
<
MIN_ANG
&&
(
*
bo
)
->
angle
>
-
MIN_ANG
)
{
fprintf
(
layoutFile
,
"%d %.5f %.5f 0
\n
"
,
outlineIndex
,
(
*
bo
)
->
endPoint
.
x
,
(
*
bo
)
->
endPoint
.
y
);
}
else
{
fprintf
(
layoutFile
,
"%d %.5f %.5f %.5f
\n
"
,
outlineIndex
,
(
*
bo
)
->
endPoint
.
x
,
(
*
bo
)
->
endPoint
.
y
,
(
*
bo
)
->
angle
);
}
++
bo
;
}
}
++
outlineIndex
;
return
true
;
}
bool
IDF_BOARD
::
AddDrill
(
double
dia
,
double
x
,
double
y
,
IDF3
::
KEY_PLATING
plating
,
const
std
::
string
refdes
,
const
std
::
string
holeType
,
IDF3
::
KEY_OWNER
owner
)
{
if
(
dia
<
IDF_MIN_DIA
*
scale
)
return
false
;
IDF_DRILL_DATA
*
dp
=
new
IDF_DRILL_DATA
(
dia
,
x
,
y
,
plating
,
refdes
,
holeType
,
owner
);
drills
.
push_back
(
dp
);
return
true
;
}
bool
IDF_BOARD
::
AddSlot
(
double
aWidth
,
double
aLength
,
double
aOrientation
,
double
aX
,
double
aY
)
{
if
(
aWidth
<
IDF_MIN_DIA
*
scale
)
return
false
;
if
(
aLength
<
IDF_MIN_DIA
*
scale
)
return
false
;
IDF_POINT
c
[
2
];
// centers
IDF_POINT
pt
[
4
];
double
a1
=
aOrientation
/
180.0
*
M_PI
;
double
a2
=
a1
+
M_PI2
;
double
d1
=
aLength
/
2.0
;
double
d2
=
aWidth
/
2.0
;
double
sa1
=
sin
(
a1
);
double
ca1
=
cos
(
a1
);
double
dsa2
=
d2
*
sin
(
a2
);
double
dca2
=
d2
*
cos
(
a2
);
c
[
0
].
x
=
aX
+
d1
*
ca1
;
c
[
0
].
y
=
aY
+
d1
*
sa1
;
c
[
1
].
x
=
aX
-
d1
*
ca1
;
c
[
1
].
y
=
aY
-
d1
*
sa1
;
pt
[
0
].
x
=
c
[
0
].
x
-
dca2
;
pt
[
0
].
y
=
c
[
0
].
y
-
dsa2
;
pt
[
1
].
x
=
c
[
1
].
x
-
dca2
;
pt
[
1
].
y
=
c
[
1
].
y
-
dsa2
;
pt
[
2
].
x
=
c
[
1
].
x
+
dca2
;
pt
[
2
].
y
=
c
[
1
].
y
+
dsa2
;
pt
[
3
].
x
=
c
[
0
].
x
+
dca2
;
pt
[
3
].
y
=
c
[
0
].
y
+
dsa2
;
IDF_OUTLINE
outline
;
// first straight run
IDF_SEGMENT
*
seg
=
new
IDF_SEGMENT
(
pt
[
0
],
pt
[
1
]
);
outline
.
push
(
seg
);
// first 180 degree cap
seg
=
new
IDF_SEGMENT
(
c
[
1
],
pt
[
1
],
-
180.0
,
true
);
outline
.
push
(
seg
);
// final straight run
seg
=
new
IDF_SEGMENT
(
pt
[
2
],
pt
[
3
]
);
outline
.
push
(
seg
);
// final 180 degree cap
seg
=
new
IDF_SEGMENT
(
c
[
0
],
pt
[
3
],
-
180.0
,
true
);
outline
.
push
(
seg
);
return
AddOutline
(
outline
);
}
bool
IDF_BOARD
::
PlaceComponent
(
const
wxString
aComponentFile
,
const
std
::
string
aRefDes
,
double
aXLoc
,
double
aYLoc
,
double
aZLoc
,
double
aRotation
,
bool
isOnTop
)
{
return
IDFLib
.
PlaceComponent
(
aComponentFile
,
aRefDes
,
aXLoc
,
aYLoc
,
aZLoc
,
aRotation
,
isOnTop
);
}
std
::
string
IDF_BOARD
::
GetRefDes
(
void
)
{
std
::
ostringstream
ostr
;
ostr
<<
"NOREFDES_"
<<
refdesIndex
++
;
return
ostr
.
str
();
}
bool
IDF_BOARD
::
WriteDrills
(
void
)
{
if
(
!
layoutFile
)
return
false
;
// TODO: check the stream integrity and return false as appropriate
if
(
drills
.
empty
()
)
return
true
;
fprintf
(
layoutFile
,
".DRILLED_HOLES
\n
"
);
std
::
list
<
class
IDF_DRILL_DATA
*>::
iterator
ds
=
drills
.
begin
();
std
::
list
<
class
IDF_DRILL_DATA
*>::
iterator
de
=
drills
.
end
();
while
(
ds
!=
de
)
{
if
(
!
(
*
ds
)
->
Write
(
layoutFile
)
)
return
false
;
++
ds
;
}
fprintf
(
layoutFile
,
".END_DRILLED_HOLES
\n
"
);
return
true
;
}
double
IDF_BOARD
::
GetScale
(
void
)
{
return
scale
;
}
void
IDF_BOARD
::
SetOffset
(
double
x
,
double
y
)
{
offsetX
=
x
;
offsetY
=
y
;
}
void
IDF_BOARD
::
GetOffset
(
double
&
x
,
double
&
y
)
{
x
=
offsetX
;
y
=
offsetY
;
}
IDF_LIB
::~
IDF_LIB
()
{
while
(
!
components
.
empty
()
)
{
delete
components
.
back
();
components
.
pop_back
();
}
}
bool
IDF_LIB
::
writeLib
(
FILE
*
aLibFile
)
{
if
(
!
aLibFile
)
return
false
;
// TODO: check stream integrity and return false as appropriate
// export models
std
::
list
<
IDF_COMP
*
>::
const_iterator
mbeg
=
components
.
begin
();
std
::
list
<
IDF_COMP
*
>::
const_iterator
mend
=
components
.
end
();
while
(
mbeg
!=
mend
)
{
if
(
!
(
*
mbeg
)
->
WriteLib
(
aLibFile
)
)
return
false
;
++
mbeg
;
}
libWritten
=
true
;
return
true
;
}
bool
IDF_LIB
::
writeBrd
(
FILE
*
aLayoutFile
)
{
if
(
!
aLayoutFile
||
!
libWritten
)
return
false
;
if
(
components
.
empty
()
)
return
true
;
// TODO: check stream integrity and return false as appropriate
// write out the board placement information
std
::
list
<
IDF_COMP
*
>::
const_iterator
mbeg
=
components
.
begin
();
std
::
list
<
IDF_COMP
*
>::
const_iterator
mend
=
components
.
end
();
fprintf
(
aLayoutFile
,
"
\n
.PLACEMENT
\n
"
);
while
(
mbeg
!=
mend
)
{
if
(
!
(
*
mbeg
)
->
WritePlacement
(
aLayoutFile
)
)
return
false
;
++
mbeg
;
}
fprintf
(
aLayoutFile
,
".END_PLACEMENT
\n
"
);
return
true
;
}
bool
IDF_LIB
::
WriteFiles
(
FILE
*
aLayoutFile
,
FILE
*
aLibFile
)
{
if
(
!
aLayoutFile
||
!
aLibFile
)
return
false
;
libWritten
=
false
;
regOutlines
.
clear
();
if
(
!
writeLib
(
aLibFile
)
)
return
false
;
return
writeBrd
(
aLayoutFile
);
}
bool
IDF_LIB
::
RegisterOutline
(
const
std
::
string
aGeomPartString
)
{
std
::
set
<
std
::
string
>::
const_iterator
it
=
regOutlines
.
find
(
aGeomPartString
);
if
(
it
!=
regOutlines
.
end
()
)
return
true
;
regOutlines
.
insert
(
aGeomPartString
);
return
false
;
}
bool
IDF_LIB
::
PlaceComponent
(
const
wxString
aComponentFile
,
const
std
::
string
aRefDes
,
double
aXLoc
,
double
aYLoc
,
double
aZLoc
,
double
aRotation
,
bool
isOnTop
)
{
IDF_COMP
*
comp
=
new
IDF_COMP
(
this
);
if
(
comp
==
NULL
)
{
std
::
cerr
<<
"IDF_LIB: *ERROR* could not allocate memory for a component
\n
"
;
return
false
;
}
components
.
push_back
(
comp
);
if
(
!
comp
->
PlaceComponent
(
aComponentFile
,
aRefDes
,
aXLoc
,
aYLoc
,
aZLoc
,
aRotation
,
isOnTop
)
)
{
std
::
cerr
<<
"IDF_LIB: file does not exist (or is symlink):
\n
"
;
std
::
cerr
<<
" FILE: "
<<
TO_UTF8
(
aComponentFile
)
<<
"
\n
"
;
return
false
;
}
return
true
;
}
IDF_COMP
::
IDF_COMP
(
IDF_LIB
*
aParent
)
{
parent
=
aParent
;
}
bool
IDF_COMP
::
PlaceComponent
(
const
wxString
aComponentFile
,
const
std
::
string
aRefDes
,
double
aXLoc
,
double
aYLoc
,
double
aZLoc
,
double
aRotation
,
bool
isOnTop
)
{
componentFile
=
aComponentFile
;
refdes
=
aRefDes
;
if
(
refdes
.
empty
()
||
!
refdes
.
compare
(
"~"
)
||
!
refdes
.
compare
(
"0"
)
)
refdes
=
"NOREFDES"
;
loc_x
=
aXLoc
;
loc_y
=
aYLoc
;
loc_z
=
aZLoc
;
rotation
=
aRotation
;
top
=
isOnTop
;
wxString
fname
=
wxExpandEnvVars
(
aComponentFile
);
if
(
!
wxFileName
::
FileExists
(
fname
)
)
return
false
;
componentFile
=
fname
;
return
true
;
}
bool
IDF_COMP
::
WritePlacement
(
FILE
*
aLayoutFile
)
{
if
(
aLayoutFile
==
NULL
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* WritePlacement() invoked with aLayoutFile = NULL
\n
"
;
return
false
;
}
if
(
parent
==
NULL
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* no valid pointer
\n
"
;
return
false
;
}
if
(
componentFile
.
empty
()
)
{
std
::
cerr
<<
"IDF_COMP: *BUG* empty componentFile name in WritePlacement()
\n
"
;
return
false
;
}
if
(
geometry
.
empty
()
&&
partno
.
empty
()
)
{
std
::
cerr
<<
"IDF_COMP: *BUG* geometry and partno strings are empty in WritePlacement()
\n
"
;
return
false
;
}
// TODO: monitor stream integrity and respond accordingly
// PLACEMENT, RECORD 2:
fprintf
(
aLayoutFile
,
"
\"
%s
\"
\"
%s
\"
\"
%s
\"\n
"
,
geometry
.
c_str
(),
partno
.
c_str
(),
refdes
.
c_str
()
);
// PLACEMENT, RECORD 3:
if
(
rotation
>=
-
MIN_ANG
&&
rotation
<=
-
MIN_ANG
)
{
fprintf
(
aLayoutFile
,
"%.6f %.6f %.6f 0 %s ECAD
\n
"
,
loc_x
,
loc_y
,
loc_z
,
top
?
"TOP"
:
"BOTTOM"
);
}
else
{
fprintf
(
aLayoutFile
,
"%.6f %.6f %.6f %.3f %s ECAD
\n
"
,
loc_x
,
loc_y
,
loc_z
,
rotation
,
top
?
"TOP"
:
"BOTTOM"
);
}
return
true
;
}
bool
IDF_COMP
::
WriteLib
(
FILE
*
aLibFile
)
{
// 1. parse the file for the .ELECTRICAL or .MECHANICAL section
// and extract the Geometry and PartNumber strings
// 2. Register the name; check if it already exists
// 3. parse the rest of the file until .END_ELECTRICAL or
// .END_MECHANICAL; validate that each entry conforms
// to a valid outline
// 4. write lines to library file
//
// NOTE on parsing (the order matters):
// + store each line which begins with '#'
// + strip blanks from both ends of the line
// + drop each blank line
// + the first non-blank non-comment line must be
// .ELECTRICAL or .MECHANICAL (as per spec, case does not matter)
// + the first non-blank line after RECORD 1 must be RECORD 2
// + following RECORD 2, only blank lines, valid outline entries,
// and .END_{MECHANICAL,ELECTRICAL} are allowed
// + only a single outline may be specified; the order may be
// CW or CCW.
// + all valid lines are stored and written to the library file
//
// return: false if we do could not write model data; we may return
// true even if we could not read an IDF file for some reason, provided
// that the default model was written. In such a case, warnings will be
// written to stderr.
if
(
aLibFile
==
NULL
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* WriteLib() invoked with aLibFile = NULL
\n
"
;
return
false
;
}
if
(
parent
==
NULL
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* no valid pointer
\n
"
;
return
false
;
}
if
(
componentFile
.
empty
()
)
{
std
::
cerr
<<
"IDF_COMP: *BUG* empty componentFile name in WriteLib()
\n
"
;
return
false
;
}
std
::
list
<
std
::
string
>
records
;
std
::
ifstream
model
;
std
::
string
fname
=
TO_UTF8
(
componentFile
);
model
.
open
(
fname
.
c_str
(),
std
::
ios_base
::
in
);
if
(
!
model
.
is_open
()
)
{
std
::
cerr
<<
"* IDF EXPORT: could not open file "
<<
fname
<<
"
\n
"
;
return
substituteComponent
(
aLibFile
);
}
std
::
string
entryType
;
// will be one of ELECTRICAL or MECHANICAL
std
::
string
endMark
;
// will be one of .END_ELECTRICAL or .END_MECHANICAL
std
::
string
iline
;
// the input line
int
state
=
1
;
bool
isComment
;
// true if a line just read in is a comment line
bool
isNewItem
=
false
;
// true if the outline is a previously unsaved IDF item
// some vars for parsing record 3
int
loopIdx
=
-
1
;
// direction of points in outline (0=CW, 1=CCW, -1=no points yet)
double
firstX
;
double
firstY
;
bool
lineClosed
=
false
;
// true when outline has been closed; only one outline is permitted
while
(
state
)
{
while
(
!
FetchIDFLine
(
model
,
iline
,
isComment
)
&&
model
.
good
()
);
if
(
!
model
.
good
()
)
{
// this should not happen; we should at least
// have encountered the .END_ statement;
// however, we shall make a concession if the
// last line is an .END_ statement which had
// not been correctly terminated
if
(
!
endMark
.
empty
()
&&
!
strncasecmp
(
iline
.
c_str
(),
endMark
.
c_str
(),
15
)
)
{
std
::
cerr
<<
"IDF EXPORT: *WARNING* IDF file is not properly terminated
\n
"
;
std
::
cerr
<<
"* FILE: "
<<
fname
<<
"
\n
"
;
records
.
push_back
(
endMark
);
break
;
}
std
::
cerr
<<
"IDF EXPORT: *ERROR* faulty IDF file
\n
"
;
std
::
cerr
<<
"* FILE: "
<<
fname
<<
"
\n
"
;
return
substituteComponent
(
aLibFile
);
}
switch
(
state
)
{
case
1
:
// accept comment lines, .ELECTRICAL, or .MECHANICAL;
// all others are simply ignored
if
(
isComment
)
{
records
.
push_back
(
iline
);
break
;
}
if
(
!
strncasecmp
(
iline
.
c_str
(),
".electrical"
,
11
)
)
{
entryType
=
".ELECTRICAL"
;
endMark
=
".END_ELECTRICAL"
;
records
.
push_back
(
entryType
);
state
=
2
;
break
;
}
if
(
!
strncasecmp
(
iline
.
c_str
(),
".mechanical"
,
11
)
)
{
entryType
=
".MECHANICAL"
;
endMark
=
".END_MECHANICAL"
;
records
.
push_back
(
entryType
);
state
=
2
;
break
;
}
break
;
case
2
:
// accept only a RECORD 2 compliant line;
// anything else constitutes a malformed IDF file
if
(
isComment
)
{
std
::
cerr
<<
"IDF EXPORT: bad IDF file
\n
"
;
std
::
cerr
<<
"* LINE: "
<<
iline
<<
"
\n
"
;
std
::
cerr
<<
"* FILE: "
<<
fname
<<
"
\n
"
;
std
::
cerr
<<
"* REASON: comment within "
<<
entryType
<<
" section
\n
"
;
model
.
close
();
return
substituteComponent
(
aLibFile
);
}
if
(
!
parseRec2
(
iline
,
isNewItem
)
)
{
std
::
cerr
<<
"IDF EXPORT: bad IDF file
\n
"
;
std
::
cerr
<<
"* LINE: "
<<
iline
<<
"
\n
"
;
std
::
cerr
<<
"* FILE: "
<<
fname
<<
"
\n
"
;
std
::
cerr
<<
"* REASON: expecting RECORD 2 of "
<<
entryType
<<
" section
\n
"
;
model
.
close
();
return
substituteComponent
(
aLibFile
);
}
if
(
isNewItem
)
{
records
.
push_back
(
iline
);
state
=
3
;
}
else
{
model
.
close
();
return
true
;
}
break
;
case
3
:
// accept outline entries or end of section
if
(
isComment
)
{
std
::
cerr
<<
"IDF EXPORT: bad IDF file
\n
"
;
std
::
cerr
<<
"* LINE: "
<<
iline
<<
"
\n
"
;
std
::
cerr
<<
"* FILE: "
<<
fname
<<
"
\n
"
;
std
::
cerr
<<
"* REASON: comment within "
<<
entryType
<<
" section
\n
"
;
model
.
close
();
return
substituteComponent
(
aLibFile
);
}
if
(
!
strncasecmp
(
iline
.
c_str
(),
endMark
.
c_str
(),
15
)
)
{
records
.
push_back
(
endMark
);
state
=
0
;
break
;
}
if
(
lineClosed
)
{
// there should be no further points
std
::
cerr
<<
"IDF EXPORT: faulty IDF file
\n
"
;
std
::
cerr
<<
"* LINE: "
<<
iline
<<
"
\n
"
;
std
::
cerr
<<
"* FILE: "
<<
fname
<<
"
\n
"
;
std
::
cerr
<<
"* REASON: more than 1 outline in "
<<
entryType
<<
" section
\n
"
;
model
.
close
();
return
substituteComponent
(
aLibFile
);
}
if
(
!
parseRec3
(
iline
,
loopIdx
,
firstX
,
firstY
,
lineClosed
)
)
{
std
::
cerr
<<
"IDF EXPORT: unexpected line in IDF file
\n
"
;
std
::
cerr
<<
"* LINE: "
<<
iline
<<
"
\n
"
;
std
::
cerr
<<
"* FILE: "
<<
fname
<<
"
\n
"
;
model
.
close
();
return
substituteComponent
(
aLibFile
);
}
records
.
push_back
(
iline
);
break
;
default
:
std
::
cerr
<<
"IDF EXPORT: BUG in "
<<
__FUNCTION__
<<
": unexpected state
\n
"
;
model
.
close
();
return
substituteComponent
(
aLibFile
);
break
;
}
// switch( state )
}
// while( state )
model
.
close
();
if
(
!
lineClosed
)
{
std
::
cerr
<<
"IDF EXPORT: component outline not closed
\n
"
;
std
::
cerr
<<
"* FILE: "
<<
fname
<<
"
\n
"
;
return
substituteComponent
(
aLibFile
);
}
std
::
list
<
std
::
string
>::
iterator
lbeg
=
records
.
begin
();
std
::
list
<
std
::
string
>::
iterator
lend
=
records
.
end
();
// TODO: check stream integrity
while
(
lbeg
!=
lend
)
{
fprintf
(
aLibFile
,
"%s
\n
"
,
lbeg
->
c_str
()
);
++
lbeg
;
}
fprintf
(
aLibFile
,
"
\n
"
);
return
true
;
}
bool
IDF_COMP
::
substituteComponent
(
FILE
*
aLibFile
)
{
// the component outline does not exist or could not be
// read; substitute a placeholder
// TODO: check the stream integrity
geometry
=
"NOGEOM"
;
partno
=
"NOPART"
;
if
(
parent
->
RegisterOutline
(
"NOGEOM_NOPART"
)
)
return
true
;
// Create a star shape 5mm high with points on 5 and 2.5 mm circles
fprintf
(
aLibFile
,
".ELECTRICAL
\n
"
);
fprintf
(
aLibFile
,
"
\"
NOGEOM
\"
\"
NOPART
\"
MM 5
\n
"
);
double
a
,
da
,
x
,
y
;
da
=
M_PI
/
5.0
;
a
=
da
/
2.0
;
for
(
int
i
=
0
;
i
<
10
;
++
i
)
{
if
(
i
&
1
)
{
x
=
2.5
*
cos
(
a
);
y
=
2.5
*
sin
(
a
);
}
else
{
x
=
1.5
*
cos
(
a
);
y
=
1.5
*
sin
(
a
);
}
a
+=
da
;
fprintf
(
aLibFile
,
"0 %.3f %.3f 0
\n
"
,
x
,
y
);
}
a
=
da
/
2.0
;
x
=
1.5
*
cos
(
a
);
y
=
1.5
*
sin
(
a
);
fprintf
(
aLibFile
,
"0 %.3f %.3f 0
\n
"
,
x
,
y
);
fprintf
(
aLibFile
,
".END_ELECTRICAL
\n\n
"
);
return
true
;
}
bool
IDF_COMP
::
parseRec2
(
const
std
::
string
aLine
,
bool
&
isNewItem
)
{
// RECORD 2:
// + "Geometry Name"
// + "Part Number"
// + MM or THOU
// + height (float)
isNewItem
=
false
;
int
idx
=
0
;
bool
quoted
=
false
;
std
::
string
entry
;
if
(
!
GetIDFString
(
aLine
,
entry
,
quoted
,
idx
)
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 2 in model file (no Geometry Name entry)
\n
"
;
return
false
;
}
geometry
=
entry
;
if
(
!
GetIDFString
(
aLine
,
entry
,
quoted
,
idx
)
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 2 in model file (no Part No. entry)
\n
"
;
return
false
;
}
partno
=
entry
;
if
(
geometry
.
empty
()
&&
partno
.
empty
()
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 2 in model file
\n
"
;
std
::
cerr
<<
" Geometry Name and Part Number are both empty.
\n
"
;
return
false
;
}
if
(
!
GetIDFString
(
aLine
,
entry
,
quoted
,
idx
)
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 2, missing FIELD 3
\n
"
;
return
false
;
}
if
(
strcasecmp
(
"MM"
,
entry
.
c_str
()
)
&&
strcasecmp
(
"THOU"
,
entry
.
c_str
()
)
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 2, invalid FIELD 3
\"
"
<<
entry
<<
"
\"\n
"
;
return
false
;
}
if
(
!
GetIDFString
(
aLine
,
entry
,
quoted
,
idx
)
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 2, missing FIELD 4
\n
"
;
return
false
;
}
if
(
quoted
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 2, invalid FIELD 4 (quoted)
\n
"
;
std
::
cerr
<<
" LINE: "
<<
aLine
<<
"
\n
"
;
return
false
;
}
// ensure that we have a valid value
double
val
;
std
::
stringstream
teststr
;
teststr
<<
entry
;
if
(
!
(
teststr
>>
val
)
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 2, invalid FIELD 4 (must be numeric)
\n
"
;
std
::
cerr
<<
" LINE: "
<<
aLine
<<
"
\n
"
;
return
false
;
}
teststr
.
clear
();
teststr
<<
geometry
<<
"_"
<<
partno
;
if
(
!
parent
->
RegisterOutline
(
teststr
.
str
()
)
)
isNewItem
=
true
;
return
true
;
}
bool
IDF_COMP
::
parseRec3
(
const
std
::
string
aLine
,
int
&
aLoopIndex
,
double
&
aX
,
double
&
aY
,
bool
&
aClosed
)
{
// RECORD 3:
// + 0,1 (loop label)
// + X coord (float)
// + Y coord (float)
// + included angle (0 for line, +ang for CCW, -ang for CW, +360 for circle)
//
// notes:
// 1. first entry may not be a circle or arc
// 2. it would be nice, but not essential, to ensure that the
// winding is indeed as specified by the loop label
//
double
x
,
y
,
ang
;
bool
ccw
=
false
;
bool
quoted
=
false
;
int
idx
=
0
;
std
::
string
entry
;
if
(
!
GetIDFString
(
aLine
,
entry
,
quoted
,
idx
)
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 3, no data
\n
"
;
return
false
;
}
if
(
quoted
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 3, FIELD 1 is quoted
\n
"
;
std
::
cerr
<<
" LINE: "
<<
aLine
<<
"
\n
"
;
return
false
;
}
if
(
entry
.
compare
(
"0"
)
&&
entry
.
compare
(
"1"
)
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 3, FIELD 1 is invalid (must be 0 or 1)
\n
"
;
std
::
cerr
<<
" LINE: "
<<
aLine
<<
"
\n
"
;
return
false
;
}
if
(
!
entry
.
compare
(
"0"
)
)
ccw
=
true
;
if
(
aLoopIndex
==
0
&&
!
ccw
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 3, LOOP INDEX changed from 0 to 1
\n
"
;
std
::
cerr
<<
" LINE: "
<<
aLine
<<
"
\n
"
;
return
false
;
}
if
(
aLoopIndex
==
1
&&
ccw
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 3, LOOP INDEX changed from 1 to 0
\n
"
;
std
::
cerr
<<
" LINE: "
<<
aLine
<<
"
\n
"
;
return
false
;
}
if
(
!
GetIDFString
(
aLine
,
entry
,
quoted
,
idx
)
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 3, FIELD 2 does not exist
\n
"
;
std
::
cerr
<<
" LINE: "
<<
aLine
<<
"
\n
"
;
return
false
;
}
if
(
quoted
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 3, FIELD 2 is quoted
\n
"
;
std
::
cerr
<<
" LINE: "
<<
aLine
<<
"
\n
"
;
return
false
;
}
std
::
stringstream
tstr
;
tstr
.
str
(
entry
);
if
(
!
(
tstr
>>
x
)
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 3, invalid X value in FIELD 2
\n
"
;
std
::
cerr
<<
" LINE: "
<<
aLine
<<
"
\n
"
;
return
false
;
}
if
(
!
GetIDFString
(
aLine
,
entry
,
quoted
,
idx
)
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 3, FIELD 3 does not exist
\n
"
;
std
::
cerr
<<
" LINE: "
<<
aLine
<<
"
\n
"
;
return
false
;
}
if
(
quoted
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 3, FIELD 3 is quoted
\n
"
;
std
::
cerr
<<
" LINE: "
<<
aLine
<<
"
\n
"
;
return
false
;
}
tstr
.
clear
();
tstr
.
str
(
entry
);
if
(
!
(
tstr
>>
y
)
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 3, invalid Y value in FIELD 3
\n
"
;
std
::
cerr
<<
" LINE: "
<<
aLine
<<
"
\n
"
;
return
false
;
}
if
(
!
GetIDFString
(
aLine
,
entry
,
quoted
,
idx
)
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 3, FIELD 4 does not exist
\n
"
;
std
::
cerr
<<
" LINE: "
<<
aLine
<<
"
\n
"
;
return
false
;
}
if
(
quoted
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 3, FIELD 4 is quoted
\n
"
;
std
::
cerr
<<
" LINE: "
<<
aLine
<<
"
\n
"
;
return
false
;
}
tstr
.
clear
();
tstr
.
str
(
entry
);
if
(
!
(
tstr
>>
ang
)
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 3, invalid ANGLE value in FIELD 3
\n
"
;
std
::
cerr
<<
" LINE: "
<<
aLine
<<
"
\n
"
;
return
false
;
}
if
(
aLoopIndex
==
-
1
)
{
// this is the first point; there are some special checks
aLoopIndex
=
ccw
?
0
:
1
;
aX
=
x
;
aY
=
y
;
aClosed
=
false
;
// ensure that the first point is not an arc specification
if
(
ang
<
-
MIN_ANG
||
ang
>
MIN_ANG
)
{
std
::
cerr
<<
"IDF_COMP: *ERROR* invalid RECORD 3, first point has non-zero angle
\n
"
;
std
::
cerr
<<
" LINE: "
<<
aLine
<<
"
\n
"
;
return
false
;
}
}
else
{
// does this close the outline?
if
(
ang
<
0.0
)
ang
=
-
ang
;
ang
-=
360.0
;
if
(
ang
>
-
MIN_ANG
&&
ang
<
MIN_ANG
)
{
// this is a circle; the loop is closed
aClosed
=
true
;
}
else
{
x
=
(
aX
-
x
)
*
(
aX
-
x
);
y
=
(
aY
-
y
)
*
(
aY
-
y
)
+
x
;
if
(
y
<=
1e-6
)
{
// the points are close enough; the loop is closed
aClosed
=
true
;
}
}
}
// NOTE:
// 1. ideally we would ensure that there are no arcs with a radius of 0; this entails
// actively calculating the last point as the previous entry could have been an instruction
// to create an arc. This check is sacrificed in the interest of speed.
// 2. a bad outline can be crafted by giving at least one valid segment and then introducing
// a circle; such a condition is not checked for here in the interest of speed.
// 3. a circle specified with an angle of -360 is invalid, but that condition is not
// tested here.
return
true
;
}
// fetch a line from the given input file and trim the ends
static
bool
FetchIDFLine
(
std
::
ifstream
&
aModel
,
std
::
string
&
aLine
,
bool
&
isComment
)
{
aLine
=
""
;
std
::
getline
(
aModel
,
aLine
);
isComment
=
false
;
// A comment begins with a '#' and must be the first character on the line
if
(
aLine
[
0
]
==
'#'
)
isComment
=
true
;
while
(
!
aLine
.
empty
()
&&
isspace
(
*
aLine
.
begin
()
)
)
aLine
.
erase
(
aLine
.
begin
()
);
while
(
!
aLine
.
empty
()
&&
isspace
(
*
aLine
.
rbegin
()
)
)
aLine
.
erase
(
--
aLine
.
end
()
);
if
(
aLine
.
empty
()
)
return
false
;
return
true
;
}
// extract an IDF string and move the index to point to the character after the substring
static
bool
GetIDFString
(
const
std
::
string
&
aLine
,
std
::
string
&
aIDFString
,
bool
&
hasQuotes
,
int
&
aIndex
)
{
// 1. drop all leading spaces
// 2. if the first character is '"', read until the next '"',
// otherwise read until the next space or EOL.
std
::
ostringstream
ostr
;
int
len
=
aLine
.
length
();
int
idx
=
aIndex
;
if
(
idx
<
0
||
idx
>=
len
)
return
false
;
while
(
isspace
(
aLine
[
idx
]
)
&&
idx
<
len
)
++
idx
;
if
(
idx
==
len
)
{
aIndex
=
idx
;
return
false
;
}
if
(
aLine
[
idx
]
==
'"'
)
{
hasQuotes
=
true
;
++
idx
;
while
(
aLine
[
idx
]
!=
'"'
&&
idx
<
len
)
ostr
<<
aLine
[
idx
++
];
if
(
idx
==
len
)
{
std
::
cerr
<<
"GetIDFString(): *ERROR*: unterminated quote mark in line:
\n
"
;
std
::
cerr
<<
"LINE: "
<<
aLine
<<
"
\n
"
;
aIndex
=
idx
;
return
false
;
}
++
idx
;
}
else
{
hasQuotes
=
false
;
while
(
!
isspace
(
aLine
[
idx
]
)
&&
idx
<
len
)
ostr
<<
aLine
[
idx
++
];
}
aIDFString
=
ostr
.
str
();
aIndex
=
idx
;
return
true
;
}
pcbnew/exporters/idf.h
deleted
100644 → 0
View file @
60a86853
/**
* @file idf.h
*/
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2014 Cirilo Bernardo
*
* 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_H
#define IDF_H
#include <wx/string.h>
#include <set>
#include <string>
#include <idf_common.h>
/**
* @Class IDF_COMP
* is responsible for parsing individual component files and rewriting relevant
* data to a library file.
*/
class
IDF_COMP
{
private
:
/// filename (full path) of the IDF component footprint
wxString
componentFile
;
/// reference designator; a valid designator or NOREFDES
std
::
string
refdes
;
/// overall translation of the part (component location + 3D offset)
double
loc_x
;
double
loc_y
;
double
loc_z
;
/// overall rotation of the part (3D Z rotation + component rotation)
double
rotation
;
/// true if the component is on the top of the board
bool
top
;
/// geometry of the package; for example, HORIZ, VERT, "HORIZ 0.2 inch"
std
::
string
geometry
;
/// package name or part number; for example "TO92" or "BC107"
std
::
string
partno
;
/// the owning IDF_LIB instance
IDF_LIB
*
parent
;
/**
* Function substituteComponent
* places a substitute component footprint into the library file
* and creates an appropriate entry for the PLACEMENT section
* @param aLibFile is the library file to write to
* @return bool: true if data was successfully written
*/
bool
substituteComponent
(
FILE
*
aLibFile
);
// parse RECORD 2; return TRUE if all is OK, otherwise FALSE
bool
parseRec2
(
const
std
::
string
aLine
,
bool
&
isNewItem
);
// parse RECORD 3; return TRUE if all is OK, otherwise FALSE
bool
parseRec3
(
const
std
::
string
aLine
,
int
&
aLoopIndex
,
double
&
aX
,
double
&
aY
,
bool
&
aClosed
);
public
:
IDF_COMP
(
IDF_LIB
*
aParent
);
/**
* Function PlaceComponent
* specifies the parameters of an IDF component outline placed on the board
* @param aComponentFile is the IDF component file to include
* @param aRefDes is the component reference designator; an empty string,
* '~' or '0' all default to "NOREFDES".
* @param aLocation is the overall translation of the part (board location + 3D offset)
* @param aRotation is the overall rotation of the part (component rotation + 3D Z rotation)
* @return bool: true if the specified component file exists
*/
bool
PlaceComponent
(
const
wxString
aComponentFile
,
const
std
::
string
aRefDes
,
double
aXLoc
,
double
aYLoc
,
double
aZLoc
,
double
aRotation
,
bool
isOnTop
);
/**
* Function WriteLib
* parses the model file to extract information needed by the
* PLACEMENT section and writes data (if necessary) to the
* library file
* @param aLibFile is the library file to write to
* @return bool: true if data was successfully written
*/
bool
WriteLib
(
FILE
*
aLibFile
);
/**
* Function WritePlacement
* write the .PLACEMENT data of the component to the IDF board @param aLayoutFile
* @return bool: true if data was successfully written
*/
bool
WritePlacement
(
FILE
*
aLayoutFile
);
};
/**
* @Class IDF_LIB
* stores information on IDF models ( also has an inbuilt NOMODEL model )
* and is responsible for writing the ELECTRICAL sections of the library file
* (*.emp) and the PLACEMENT section of the board file.
*/
class
IDF_LIB
{
/// a list of component outline names and a flag to indicate their save state
std
::
set
<
std
::
string
>
regOutlines
;
std
::
list
<
IDF_COMP
*
>
components
;
bool
libWritten
;
/**
* Function writeLib
* writes all current library information to the output file
*/
bool
writeLib
(
FILE
*
aLibFile
);
/**
* Function writeBrd
* write placement information to the board file
*/
bool
writeBrd
(
FILE
*
aLayoutFile
);
public
:
virtual
~
IDF_LIB
();
/**
* Function WriteFiles
* writes the library entries to the *.emp file (aLibFile) and the
* .PLACEMENT section to the *.emn file (aLayoutFile)
* @param aLayoutFile IDF board file
* @param aLibFile IDF library file
* @return bool: true if all data was written successfully
*/
bool
WriteFiles
(
FILE
*
aLayoutFile
,
FILE
*
aLibFile
);
/**
* Function RegisterOutline
* adds the given string to a list of current outline entities.
* @param aGeomPartString is a concatenation of the IDF component's
* geometry name and part name; this is used as a unique identifier
* to prevent redundant entries in the library output.
* @return bool: true if the string was already registered,
* false if it is a new registration.
*/
bool
RegisterOutline
(
const
std
::
string
aGeomPartString
);
bool
PlaceComponent
(
const
wxString
aComponentFile
,
const
std
::
string
aRefDes
,
double
aXLoc
,
double
aYLoc
,
double
aZLoc
,
double
aRotation
,
bool
isOnTop
);
};
/**
* @Class IDF_BOARD
* contains objects necessary for the maintenance of the IDF board and library files.
*/
class
IDF_BOARD
{
private
:
IDF_LIB
IDFLib
;
///< IDF library manager
std
::
list
<
IDF_DRILL_DATA
*>
drills
;
///< IDF drill data
int
outlineIndex
;
///< next outline index to use
bool
useThou
;
///< true if output is THOU
double
scale
;
///< scale from KiCad IU to IDF output units
double
boardThickness
;
///< total thickness of the PCB
bool
hasBrdOutlineHdr
;
///< true when a board outline header has been written
int
refdesIndex
;
///< index to generate REFDES for modules which have none
double
offsetX
;
///< offset to roughly center the board on the world origin
double
offsetY
;
FILE
*
layoutFile
;
///< IDF board file (*.emn)
FILE
*
libFile
;
///< IDF library file (*.emp)
/**
* Function Write
* outputs a .DRILLED_HOLES section compliant with the
* IDFv3 specification.
* @param aLayoutFile : open file (*.emn) for output
*/
bool
WriteDrills
(
void
);
public
:
IDF_BOARD
();
~
IDF_BOARD
();
// Set up the output files and scale factor;
// return TRUE if everything is OK
bool
Setup
(
wxString
aBoardName
,
wxString
aFullFileName
,
bool
aUseThou
,
int
aBoardThickness
);
// Finish a board
// Write out all current data and close files.
// Return true for success
bool
Finish
(
void
);
/**
* Function GetScale
* returns the output scaling factor
*/
double
GetScale
(
void
);
/**
* Function SetOffset
* sets the global coordinate offsets
*/
void
SetOffset
(
double
x
,
double
y
);
/**
* Function GetOffset
* returns the global coordinate offsets
*/
void
GetOffset
(
double
&
x
,
double
&
y
);
// Add an outline; the very first outline is the board perimeter;
// all additional outlines are cutouts.
bool
AddOutline
(
IDF_OUTLINE
&
aOutline
);
/**
* Function AddDrill
* creates a drill entry and adds it to the list of PCB holes
* @param dia : drill diameter
* @param x : X coordinate of the drill center
* @param y : Y coordinate of the drill center
* @param plating : flag, PTH or NPTH
* @param refdes : component Reference Designator
* @param holetype : purpose of hole
* @param owner : one of MCAD, ECAD, UNOWNED
*/
bool
AddDrill
(
double
dia
,
double
x
,
double
y
,
IDF3
::
KEY_PLATING
plating
,
const
std
::
string
refdes
,
const
std
::
string
holeType
,
IDF3
::
KEY_OWNER
owner
);
/**
* Function AddSlot
* creates a slot cutout within the IDF BOARD section; this is a deficient representation
* of a KiCad 'oval' drill; IDF is unable to represent a plated slot and unable to
* represent the Reference Designator association with a slot.
*/
bool
AddSlot
(
double
aWidth
,
double
aLength
,
double
aOrientation
,
double
aX
,
double
aY
);
bool
PlaceComponent
(
const
wxString
aComponentFile
,
const
std
::
string
aRefDes
,
double
aXLoc
,
double
aYLoc
,
double
aZLoc
,
double
aRotation
,
bool
isOnTop
);
std
::
string
GetRefDes
(
void
);
};
/**
* @Class IDF_DRILL_DATA
* contains information describing a drilled hole and is responsible for
* writing this information to a file in compliance with the IDFv3 specification.
*/
class
IDF_DRILL_DATA
{
private
:
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
;
public
:
/**
* Constructor IDF_DRILL_DATA
* creates a drill entry with information compliant with the
* IDFv3 specifications.
* @param aDrillDia : drill diameter
* @param aPosX : X coordinate of the drill center
* @param aPosY : Y coordinate of the drill center
* @param aPlating : flag, PTH or NPTH
* @param aRefDes : component Reference Designator
* @param aHoleType : purpose of hole
* @param aOwner : 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
);
/**
* Function Write
* writes a single line representing the hole within a .DRILLED_HOLES section
*/
bool
Write
(
FILE
*
aLayoutFile
);
};
#endif // IDF_H
pcbnew/exporters/idf_common.cpp
deleted
100644 → 0
View file @
60a86853
/**
* file: idf_common.cpp
*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2014 Cirilo Bernardo
*
* 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
*/
#include <list>
#include <string>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <richio.h>
#include <idf_common.h>
#include <build_version.h>
#ifdef DEBUG_IDF
void
IDF3
::
PrintSeg
(
IDF_SEGMENT
*
aSegment
)
{
if
(
aSegment
->
IsCircle
()
)
{
fprintf
(
stdout
,
"printSeg(): CIRCLE: C(%.3f, %.3f) P(%.3f, %.3f) rad. %.3f
\n
"
,
aSegment
->
startPoint
.
x
,
aSegment
->
startPoint
.
y
,
aSegment
->
endPoint
.
x
,
aSegment
->
endPoint
.
y
,
aSegment
->
radius
);
return
;
}
if
(
aSegment
->
angle
<
-
MIN_ANG
||
aSegment
->
angle
>
MIN_ANG
)
{
fprintf
(
stdout
,
"printSeg(): ARC: p1(%.3f, %.3f) p2(%.3f, %.3f) ang. %.3f
\n
"
,
aSegment
->
startPoint
.
x
,
aSegment
->
startPoint
.
y
,
aSegment
->
endPoint
.
x
,
aSegment
->
endPoint
.
y
,
aSegment
->
angle
);
return
;
}
fprintf
(
stdout
,
"printSeg(): LINE: p1(%.3f, %.3f) p2(%.3f, %.3f)
\n
"
,
aSegment
->
startPoint
.
x
,
aSegment
->
startPoint
.
y
,
aSegment
->
endPoint
.
x
,
aSegment
->
endPoint
.
y
);
return
;
}
#endif
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
;
}
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
);
#ifdef DEBUG_IDF
PrintSeg
(
*
idx
);
#endif
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
;
#ifdef DEBUG_IDF
PrintSeg
(
*
bl
);
#endif
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
();
#ifdef DEBUG_IDF
printSeg
(
*
bl
);
#endif
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
;
#ifdef DEBUG_IDF
printSeg
(
seg
);
#endif
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_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
);
}
pcbnew/exporters/idf_common.h
deleted
100644 → 0
View file @
60a86853
/**
* @file idf_common.h
*/
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2014 Cirilo Bernardo
*
* 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>
#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
// 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
;
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
);
#ifdef DEBUG_IDF
// prints out segment information for debug purposes
void
PrintSeg
(
IDF_SEGMENT
*
aSegment
);
#endif
}
/**
* @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
);
};
#endif // IDF_COMMON_H
utils/idftools/idf_examples/test_donut.emn
0 → 100644
View file @
7a110d0c
.HEADER
BOARD_FILE 3.0 "Created by some software" 2014/02/01.15:09:15 1
"test_donut" MM
.END_HEADER
# The board outline is a simple square with a small hole in it
.BOARD_OUTLINE ECAD
1.60000
0 -100 100 0
0 -100 -100 0
0 100 -100 0
0 100 100 0
0 -100 100 0
1 0 0 0
1 5 0 360
.END_BOARD_OUTLINE
# This OTHER OUTLINE is a square toroid
.OTHER_OUTLINE UNOWNED
MY_DONUT 30 TOP
0 0 0 0
0 75 0 360
1 0 0 0
1 30 0 360
.END_OTHER_OUTLINE
# This OTHER OUTLINE is a square with a hole
.OTHER_OUTLINE UNOWNED
MY_NOT_DONUT 2 BOTTOM
0 -50 50 0
0 -50 -50 0
0 50 -50 0
0 50 50 0
0 -50 50 0
1 0 0 0
1 10 0 360
2 0 50 0
2 0 75 360
3 50 0 0
3 75 0 360
4 0 -50 0
4 0 -75 360
5 -50 0 0
5 -75 0 360
.END_OTHER_OUTLINE
utils/idftools/idf_examples/test_donut.emp
0 → 100644
View file @
7a110d0c
.HEADER
LIBRARY_FILE 3.0 "Created by some software" 2014/02/01.15:09:15 1
.END_HEADER
# This file contains no component outlines
\ No newline at end of file
utils/idftools/idf_outlines.cpp
View file @
7a110d0c
...
...
@@ -693,6 +693,10 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli
std
::
list
<
IDF_SEGMENT
*>::
iterator
bo
;
std
::
list
<
IDF_SEGMENT
*>::
iterator
eo
;
if
(
!
aOutline
)
throw
(
IDF_ERROR
(
__FILE__
,
__FUNCTION__
,
__LINE__
,
"
\n
* BUG: NULL outline pointer"
)
);
if
(
aOutline
->
size
()
==
1
)
{
if
(
!
aOutline
->
front
()
->
IsCircle
()
)
...
...
utils/idftools/idf_parser.cpp
View file @
7a110d0c
...
...
@@ -36,6 +36,48 @@
using
namespace
std
;
using
namespace
IDF3
;
static
bool
MatchCompOutline
(
IDF3_COMP_OUTLINE
*
aOutlineA
,
IDF3_COMP_OUTLINE
*
aOutlineB
)
{
if
(
aOutlineA
->
GetComponentClass
()
!=
aOutlineB
->
GetComponentClass
()
)
return
false
;
if
(
aOutlineA
->
OutlinesSize
()
!=
aOutlineB
->
OutlinesSize
()
)
return
false
;
// are both outlines empty?
if
(
aOutlineA
->
OutlinesSize
()
==
0
)
return
true
;
IDF_OUTLINE
*
opA
=
aOutlineA
->
GetOutline
(
0
);
IDF_OUTLINE
*
opB
=
aOutlineB
->
GetOutline
(
0
);
if
(
opA
->
size
()
!=
opB
->
size
()
)
return
false
;
if
(
opA
->
size
()
==
0
)
return
true
;
std
::
list
<
IDF_SEGMENT
*>::
iterator
olAs
=
opA
->
begin
();
std
::
list
<
IDF_SEGMENT
*>::
iterator
olAe
=
opA
->
end
();
std
::
list
<
IDF_SEGMENT
*>::
iterator
olBs
=
opB
->
begin
();
while
(
olAs
!=
olAe
)
{
if
(
!
(
*
olAs
)
->
MatchesStart
(
(
*
olBs
)
->
startPoint
)
)
return
false
;
if
(
!
(
*
olAs
)
->
MatchesEnd
(
(
*
olBs
)
->
endPoint
)
)
return
false
;
++
olAs
;
++
olBs
;
}
return
true
;
}
/*
* CLASS: IDF3_COMP_OUTLINE_DATA
* This represents the outline placement
...
...
@@ -285,7 +327,7 @@ bool IDF3_COMP_OUTLINE_DATA::readPlaceData( std::ifstream &aBoardFile,
// component is given a unique RefDes. This class of defect
// is one reason IDF does not work well in faithfully
// conveying information between ECAD and MCAD.
refdes
=
token
;
refdes
=
aBoard
->
GetNewRefDes
()
;
}
else
if
(
CompareToken
(
"BOARD"
,
token
)
)
{
...
...
@@ -1297,6 +1339,7 @@ IDF3_BOARD::IDF3_BOARD( IDF3::CAD_TYPE aCadType )
userYoff
=
0.0
;
brdFileVersion
=
0
;
libFileVersion
=
0
;
iRefDes
=
0
;
// unlike other outlines which are created as necessary,
// the board outline always exists and its parent must
...
...
@@ -1314,6 +1357,18 @@ IDF3_BOARD::~IDF3_BOARD()
return
;
}
const
std
::
string
&
IDF3_BOARD
::
GetNewRefDes
(
void
)
{
ostringstream
ostr
;
ostr
<<
"NOREFDESn"
<<
iRefDes
++
;
sRefDes
=
ostr
.
str
();
return
sRefDes
;
}
#ifndef DISABLE_IDF_OWNERSHIP
bool
IDF3_BOARD
::
checkComponentOwnership
(
int
aSourceLine
,
const
char
*
aSourceFunc
,
IDF3_COMPONENT
*
aComponent
)
...
...
@@ -2422,7 +2477,12 @@ void IDF3_BOARD::readLibSection( std::ifstream& aLibFile, IDF3::FILE_STATE& aLib
}
else
{
delete
pout
;
if
(
MatchCompOutline
(
pout
,
cop
)
)
{
delete
pout
;
// everything is fine; the outlines are genuine duplicates
return
;
}
ostringstream
ostr
;
ostr
<<
"invalid IDF library
\n
"
;
...
...
@@ -3131,7 +3191,7 @@ bool IDF3_BOARD::SetBoardVersion( int aVersion )
{
ostringstream
ostr
;
ostr
<<
__FILE__
<<
":"
<<
__LINE__
<<
":"
<<
__FUNCTION__
<<
"():
\n
"
;
ostr
<<
"* board version ("
<<
aVersion
<<
") must be > 0"
;
ostr
<<
"* board version ("
<<
aVersion
<<
") must be >
=
0"
;
errormsg
=
ostr
.
str
();
return
false
;
...
...
@@ -3155,7 +3215,7 @@ bool IDF3_BOARD::SetLibraryVersion( int aVersion )
{
ostringstream
ostr
;
ostr
<<
__FILE__
<<
":"
<<
__LINE__
<<
":"
<<
__FUNCTION__
<<
"():
\n
"
;
ostr
<<
"* library version ("
<<
aVersion
<<
") must be > 0"
;
ostr
<<
"* library version ("
<<
aVersion
<<
") must be >
=
0"
;
errormsg
=
ostr
.
str
();
return
false
;
...
...
@@ -3741,64 +3801,54 @@ IDF3_COMPONENT* IDF3_BOARD::FindComponent( std::string aRefDes )
// returns a pointer to a component outline object or NULL
// if the object doesn't exist
IDF3_COMP_OUTLINE
*
IDF3_BOARD
::
GetComponentOutline
(
const
std
::
string
aGeomName
,
const
std
::
string
aPartName
,
wxString
aFullFileName
)
IDF3_COMP_OUTLINE
*
IDF3_BOARD
::
GetComponentOutline
(
wxString
aFullFileName
)
{
std
::
ostringstream
ostr
;
ostr
<<
aGeomName
<<
"_"
<<
aPartName
;
IDF3_COMP_OUTLINE
*
cp
=
GetComponentOutline
(
ostr
.
str
()
);
if
(
cp
!=
NULL
)
return
cp
;
std
::
string
fname
=
TO_UTF8
(
aFullFileName
);
wxFileName
idflib
(
aFullFileName
);
cp
=
new
IDF3_COMP_OUTLINE
(
this
);
if
(
cp
==
NULL
)
if
(
!
idflib
.
IsOk
()
)
{
ostringstream
ostr
;
ostr
<<
__FILE__
<<
":"
<<
__LINE__
<<
":"
<<
__FUNCTION__
<<
"():
\n
"
;
cerr
<<
"* failed to create outline with UID '"
<<
aGeomName
<<
"_"
;
cerr
<<
aPartName
<<
"'
\n
"
;
cerr
<<
"* filename: '"
<<
fname
<<
"'"
;
cerr
<<
"* invalid file name: '"
<<
fname
<<
"'"
;
errormsg
=
ostr
.
str
();
return
NULL
;
}
wxFileName
idflib
(
aFullFileName
);
if
(
!
idflib
.
IsOk
()
)
if
(
!
idflib
.
FileExists
()
)
{
delete
cp
;
ostringstream
ostr
;
ostr
<<
__FILE__
<<
":"
<<
__LINE__
<<
":"
<<
__FUNCTION__
<<
"():
\n
"
;
cerr
<<
"*
invalid file name: '"
<<
fname
<<
"'"
;
cerr
<<
"*
no such file: '"
<<
fname
<<
"'"
;
errormsg
=
ostr
.
str
();
return
NULL
;
}
if
(
!
idflib
.
FileExists
()
)
if
(
!
idflib
.
IsFileReadable
()
)
{
delete
cp
;
ostringstream
ostr
;
ostr
<<
__FILE__
<<
":"
<<
__LINE__
<<
":"
<<
__FUNCTION__
<<
"():
\n
"
;
cerr
<<
"*
no such file: '"
<<
fname
<<
"'"
;
cerr
<<
"*
cannot read file: '"
<<
fname
<<
"'"
;
errormsg
=
ostr
.
str
();
return
NULL
;
}
if
(
!
idflib
.
IsFileReadable
()
)
std
::
map
<
std
::
string
,
std
::
string
>::
iterator
itm
=
uidFileList
.
find
(
fname
);
if
(
itm
!=
uidFileList
.
end
()
)
return
GetComponentOutline
(
itm
->
second
);
IDF3_COMP_OUTLINE
*
cp
=
new
IDF3_COMP_OUTLINE
(
this
);
if
(
cp
==
NULL
)
{
delete
cp
;
ostringstream
ostr
;
ostr
<<
__FILE__
<<
":"
<<
__LINE__
<<
":"
<<
__FUNCTION__
<<
"():
\n
"
;
cerr
<<
"* cannot read file: '"
<<
fname
<<
"'"
;
cerr
<<
"* failed to create outline
\n
"
;
cerr
<<
"* filename: '"
<<
fname
<<
"'"
;
errormsg
=
ostr
.
str
();
return
NULL
;
...
...
@@ -3867,7 +3917,92 @@ IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( const std::string aGeomName,
model
.
close
();
return
cp
;
// check the unique ID against the list from library components
std
::
list
<
std
::
string
>::
iterator
lsts
=
uidLibList
.
begin
();
std
::
list
<
std
::
string
>::
iterator
lste
=
uidLibList
.
end
();
std
::
string
uid
=
cp
->
GetUID
();
IDF3_COMP_OUTLINE
*
oldp
=
NULL
;
while
(
lsts
!=
lste
)
{
if
(
!
lsts
->
compare
(
uid
)
)
{
oldp
=
GetComponentOutline
(
uid
);
if
(
MatchCompOutline
(
cp
,
oldp
)
)
{
// everything is fine; the outlines are genuine duplicates; delete the copy
delete
cp
;
// make sure we can find the item via its filename
uidFileList
.
insert
(
std
::
pair
<
std
::
string
,
std
::
string
>
(
fname
,
uid
)
);
// return the pointer to the original
return
oldp
;
}
else
{
delete
cp
;
ostringstream
ostr
;
ostr
<<
__FILE__
<<
":"
<<
__LINE__
<<
":"
<<
__FUNCTION__
<<
"():
\n
"
;
ostr
<<
"* duplicate UID for different Component Outlines: '"
<<
uid
<<
"'
\n
"
;
ostr
<<
"* original loaded from library, duplicate in current file
\n
"
;
ostr
<<
"* file: '"
<<
fname
<<
"'"
;
errormsg
=
ostr
.
str
();
return
NULL
;
}
}
++
lsts
;
}
// if we got this far then any duplicates are from files previously read
oldp
=
GetComponentOutline
(
uid
);
if
(
oldp
==
NULL
)
{
// everything is fine, there are no existing entries
uidFileList
.
insert
(
std
::
pair
<
std
::
string
,
std
::
string
>
(
fname
,
uid
)
);
compOutlines
.
insert
(
pair
<
const
std
::
string
,
IDF3_COMP_OUTLINE
*>
(
uid
,
cp
)
);
return
cp
;
}
if
(
MatchCompOutline
(
cp
,
oldp
)
)
{
// everything is fine; the outlines are genuine duplicates; delete the copy
delete
cp
;
// make sure we can find the item via its other filename
uidFileList
.
insert
(
std
::
pair
<
std
::
string
,
std
::
string
>
(
fname
,
uid
)
);
// return the pointer to the original
return
oldp
;
}
delete
cp
;
// determine the file name of the first instance
std
::
map
<
std
::
string
,
std
::
string
>::
iterator
ufls
=
uidFileList
.
begin
();
std
::
map
<
std
::
string
,
std
::
string
>::
iterator
ufle
=
uidFileList
.
end
();
std
::
string
oldfname
;
while
(
ufls
!=
ufle
)
{
if
(
!
ufls
->
second
.
compare
(
uid
)
)
{
oldfname
=
ufls
->
first
;
break
;
}
++
ufls
;
}
ostringstream
ostr
;
ostr
<<
__FILE__
<<
":"
<<
__LINE__
<<
":"
<<
__FUNCTION__
<<
"():
\n
"
;
ostr
<<
"* duplicate UID for different Component Outlines: '"
<<
uid
<<
"'
\n
"
;
ostr
<<
"* original file: '"
<<
oldfname
<<
"'
\n
"
;
ostr
<<
"* this file: '"
<<
fname
<<
"'"
;
errormsg
=
ostr
.
str
();
return
NULL
;
}
...
...
@@ -3940,8 +4075,12 @@ void IDF3_BOARD::Clear( void )
libSource
.
clear
();
brdDate
.
clear
();
libDate
.
clear
();
uidFileList
.
clear
();
uidLibList
.
clear
();
brdFileVersion
=
0
;
libFileVersion
=
0
;
iRefDes
=
0
;
sRefDes
.
clear
();
// delete comment lists
noteComments
.
clear
();
...
...
utils/idftools/idf_parser.h
View file @
7a110d0c
...
...
@@ -463,6 +463,8 @@ public:
class
IDF3_BOARD
{
private
:
std
::
map
<
std
::
string
,
std
::
string
>
uidFileList
;
// map of files opened and UIDs
std
::
list
<
std
::
string
>
uidLibList
;
// list of UIDs read from a library file
std
::
string
errormsg
;
// string for passing error messages to user
std
::
list
<
IDF_NOTE
*
>
notes
;
// IDF notes
std
::
list
<
std
::
string
>
noteComments
;
// comment list for NOTES section
...
...
@@ -476,6 +478,8 @@ private:
IDF3
::
CAD_TYPE
cadType
;
IDF3
::
IDF_UNIT
unit
;
IDF3
::
IDF_VERSION
idfVer
;
// IDF version of Board or Library
int
iRefDes
;
// counter for automatically numbered NOREFDES items
std
::
string
sRefDes
;
std
::
string
idfSource
;
// SOURCE string to use when writing BOARD and LIBRARY headers
std
::
string
brdSource
;
// SOURCE string as retrieved from a BOARD file
...
...
@@ -569,6 +573,8 @@ public:
// end user must use only mm in the API.
IDF3
::
IDF_UNIT
GetUnit
(
void
);
const
std
::
string
&
GetNewRefDes
(
void
);
void
SetBoardName
(
std
::
string
aBoardName
);
const
std
::
string
&
GetBoardName
(
void
);
...
...
@@ -692,9 +698,7 @@ public:
// returns a pointer to a component outline object or NULL
// if the object doesn't exist
IDF3_COMP_OUTLINE
*
GetComponentOutline
(
const
std
::
string
aGeomName
,
const
std
::
string
aPartName
,
wxString
aFullFileName
);
IDF3_COMP_OUTLINE
*
GetComponentOutline
(
wxString
aFullFileName
);
// returns a pointer to the component outline object with the
// unique ID aComponentID
...
...
utils/idftools/vrml_layer.cpp
View file @
7a110d0c
...
...
@@ -23,17 +23,6 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/*
* NOTES ON OUTPUT PRECISION:
*
* If we use %.6f then we have no need for special unit dependent formatting:
*
* inch: .0254 microns
* mm: 0.001 microns
* m: 1 micron
*
*/
#include <sstream>
#include <string>
#include <iomanip>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment