KiCad PCB EDA Suite
Loading...
Searching...
No Matches
odb_eda_data.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 * Author: SYSUEric <[email protected]>.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include "odb_eda_data.h"
22
23#include <base_units.h>
24#include <build_version.h>
25#include <hash_eda.h>
26#include <footprint.h>
27#include <pad.h>
28#include <string_utils.h>
29
30#include <netinfo.h>
31#include <odb_feature.h>
32#include <pcb_io_odbpp.h>
33
34
36{
37 auto& x = nets_map.emplace( std::piecewise_construct, std::forward_as_tuple( 0 ),
38 std::forward_as_tuple( nets.size(), "$NONE$" ) )
39 .first->second;
40
41 nets.push_back( &x );
42}
43
44
45void EDA_DATA::NET::Write( std::ostream& ost ) const
46{
47 ost << "NET " << m_name;
48
49 WriteAttributes( ost );
50
51 ost << std::endl;
52
53 for( const auto& subnet : subnets )
54 {
55 subnet->Write( ost );
56 }
57}
58
59
60void EDA_DATA::AddNET( const NETINFO_ITEM* aNet )
61{
62 if( nets_map.end() == nets_map.find( aNet->GetNetCode() ) )
63 {
64 wxString netName = aNet->GetNetname();
65 ODB::RemoveWhitespace( netName );
66
67 auto& net = nets_map.emplace( std::piecewise_construct,
68 std::forward_as_tuple( aNet->GetNetCode() ),
69 std::forward_as_tuple( nets.size(), netName ) )
70 .first->second;
71
72 nets.push_back( &net );
73
74 //TODO: netname check
75 }
76}
77
78
79void EDA_DATA::SUB_NET::Write( std::ostream& ost ) const
80{
81 ost << "SNT ";
82
83 WriteSubnet( ost );
84
85 ost << std::endl;
86
87 for( const auto& fid : feature_ids )
88 {
89 fid.Write( ost );
90 }
91}
92
93
94void EDA_DATA::FEATURE_ID::Write( std::ostream& ost ) const
95{
96 static const std::map<TYPE, std::string> type_map = {
97 { TYPE::COPPER, "C" },
98 { TYPE::HOLE, "H" },
99 };
100
101 ost << "FID " << type_map.at( type ) << " " << layer << " " << feature_id << std::endl;
102}
103
104
105void EDA_DATA::SUB_NET_VIA::WriteSubnet( std::ostream& ost ) const
106{
107 ost << "VIA";
108}
109
110
111void EDA_DATA::SUB_NET_TRACE::WriteSubnet( std::ostream& ost ) const
112{
113 ost << "TRC";
114}
115
116
117void EDA_DATA::SUB_NET_PLANE::WriteSubnet( std::ostream& ost ) const
118{
119 static const std::map<FILL_TYPE, std::string> fill_type_map = { { FILL_TYPE::SOLID, "S" },
120 { FILL_TYPE::OUTLINE, "O" } };
121
122 static const std::map<CUTOUT_TYPE, std::string> cutout_type_map = {
123 { CUTOUT_TYPE::CIRCLE, "C" },
124 { CUTOUT_TYPE::RECT, "R" },
125 { CUTOUT_TYPE::OCTAGON, "O" },
126 { CUTOUT_TYPE::EXACT, "E" }
127 };
128
129 ost << "PLN " << fill_type_map.at( fill_type ) << " " << cutout_type_map.at( cutout_type )
130 << " " << fill_size;
131}
132
133
134void EDA_DATA::SUB_NET_TOEPRINT::WriteSubnet( std::ostream& ost ) const
135{
136 static const std::map<SIDE, std::string> side_map = {
137 { SIDE::BOTTOM, "B" },
138 { SIDE::TOP, "T" },
139 };
140 ost << "TOP " << side_map.at( side ) << " " << comp_num << " " << toep_num;
141}
142
143
144void EDA_DATA::SUB_NET::AddFeatureID( FEATURE_ID::TYPE type, const wxString& layer,
145 size_t feature_id )
146{
147 feature_ids.emplace_back( type, m_edadata->GetLyrIdx( layer ), feature_id );
148}
149
150
151size_t EDA_DATA::GetLyrIdx( const wxString& aLayer )
152{
153 if( layers_map.count( aLayer ) )
154 {
155 return layers_map.at( aLayer );
156 }
157 else
158 {
159 auto idx = layers_map.size();
160 layers_map.emplace( aLayer, idx );
161 layers.push_back( aLayer );
162 return idx;
163 }
164}
165
166
167void OUTLINE_SQUARE::Write( std::ostream& ost ) const
168{
169 ost << "SQ " << ODB::Data2String( m_center.x ) << " " << ODB::Data2String( m_center.y ) << " "
170 << ODB::Data2String( m_halfSide ) << std::endl;
171}
172
173
174void OUTLINE_CIRCLE::Write( std::ostream& ost ) const
175{
176 ost << "CR " << ODB::Data2String( m_center.x ) << " " << ODB::Data2String( m_center.y ) << " "
177 << ODB::Data2String( m_radius ) << std::endl;
178}
179
180
181void OUTLINE_RECT::Write( std::ostream& ost ) const
182{
183 ost << "RC " << ODB::Data2String( m_lower_left.x ) << " " << ODB::Data2String( m_lower_left.y )
184 << " " << ODB::Data2String( m_width ) << " " << ODB::Data2String( m_height ) << std::endl;
185}
186
187
188void OUTLINE_CONTOUR::Write( std::ostream& ost ) const
189{
190 if( !m_surfaces )
191 return;
192
193 ost << "CT" << std::endl;
194 m_surfaces->WriteData( ost );
195 ost << "CE" << std::endl;
196}
197
198
200{
201 // ODBPP only need unique PACKAGE in PKG record in eda/data file.
202 // the PKG index can repeat to be ref in CMP record in component file.
203
204 std::shared_ptr<FOOTPRINT> fp( static_cast<FOOTPRINT*>( aFp->Clone() ) );
205 m_eda_footprints.emplace_back( fp );
206 fp->SetParentGroup( nullptr );
207 fp->SetPosition( { 0, 0 } );
208
209 if( aFp->IsFlipped() )
210 {
211 // ODB++ needs both flips
212 fp->Flip( fp->GetPosition(), FLIP_DIRECTION::LEFT_RIGHT );
213 fp->Flip( fp->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
214 }
215
216 fp->SetOrientation( ANGLE_0 );
217
218 size_t hash = hash_fp_item( fp.get(), HASH_POS | REL_COORD );
219 size_t pkg_index = packages_map.size();
220 wxString fp_name = fp->GetFPID().GetLibItemName().wx_str();
221 ODB::RemoveWhitespace( fp_name );
222
223 if( fp_name.IsEmpty() )
224 fp_name = wxS( "__" );
225
226 auto [iter, success] = packages_map.emplace( hash, PACKAGE( pkg_index, fp_name ) );
227
228 if( !success )
229 {
230 return;
231 }
232
233 PACKAGE* pkg = &( iter->second );
234
235 packages.push_back( pkg );
236
237 BOX2I bbox = fp->GetBoundingBox();
238 pkg->m_xmin = bbox.GetPosition().x;
239 pkg->m_ymin = bbox.GetPosition().y;
240 pkg->m_xmax = bbox.GetEnd().x;
241 pkg->m_ymax = bbox.GetEnd().y;
242 pkg->m_pitch = UINT64_MAX;
243
244 if( fp->Pads().size() < 2 )
245 pkg->m_pitch = pcbIUScale.mmToIU( 1.0 ); // placeholder value
246
247 for( size_t i = 0; i < fp->Pads().size(); ++i )
248 {
249 const PAD* pad1 = fp->Pads()[i];
250
251 for( size_t j = i + 1; j < fp->Pads().size(); ++j )
252 {
253 const PAD* pad2 = fp->Pads()[j];
254 const uint64_t pin_dist = ( pad1->GetCenter() - pad2->GetCenter() ).EuclideanNorm();
255 pkg->m_pitch = std::min( pkg->m_pitch, pin_dist );
256 }
257 }
258
259 const SHAPE_POLY_SET& courtyard = fp->GetCourtyard( F_CrtYd );
260 const SHAPE_POLY_SET& courtyard_back = fp->GetCourtyard( B_CrtYd );
261 SHAPE_POLY_SET pkg_outline;
262
263 if( courtyard.OutlineCount() > 0 )
264 pkg_outline = courtyard;
265
266 if( courtyard_back.OutlineCount() > 0 )
267 {
268 pkg_outline = courtyard_back;
269 }
270
271 if( !courtyard.OutlineCount() && !courtyard_back.OutlineCount() )
272 {
273 pkg_outline = fp->GetBoundingHull();
274 }
275
276 // TODO: Here we put rect, square, and circle, all as polygon
277
278 if( pkg_outline.OutlineCount() > 0 )
279 {
280 for( int ii = 0; ii < pkg_outline.OutlineCount(); ++ii )
281 {
282 pkg->m_pkgOutlines.push_back(
283 std::make_unique<OUTLINE_CONTOUR>( pkg_outline.Polygon( ii ) ) );
284 }
285 }
286
287 for( size_t i = 0; i < fp->Pads().size(); ++i )
288 {
289 const PAD* pad = fp->Pads()[i];
290 pkg->AddPin( pad, i );
291 }
292
293 return;
294}
295
296
297void EDA_DATA::PACKAGE::AddPin( const PAD* aPad, size_t aPinNum )
298{
299 wxString name = aPad->GetNumber();
300
301 // ODB is unhappy with whitespace in most places
303
304 // Pins are required to have names, so if our pad doesn't have a name, we need to
305 // generate one that is unique
306
307 if( aPad->GetAttribute() == PAD_ATTRIB::NPTH )
308 name = wxString::Format( "NPTH%zu", aPinNum );
309 else if( name.empty() )
310 name = wxString::Format( "PAD%zu", aPinNum );
311
312 // // for SNT record, pad, net, pin
313 std::shared_ptr<PIN> pin = std::make_shared<PIN>( m_pinsVec.size(), name );
314 m_pinsVec.push_back( pin );
315
316 VECTOR2D relpos = aPad->GetFPRelativePosition();
317
318 pin->m_center = ODB::AddXY( relpos );
319
320 if( aPad->HasHole() )
321 {
322 pin->type = PIN::TYPE::THROUGH_HOLE;
323 }
324 else
325 {
326 pin->type = PIN::TYPE::SURFACE;
327 }
328
329 if( aPad->GetAttribute() == PAD_ATTRIB::NPTH )
331 else if( aPad->IsOnCopperLayer() )
333 else
335
336
337 if( ( aPad->HasHole() && aPad->IsOnCopperLayer() ) || aPad->GetAttribute() == PAD_ATTRIB::PTH )
338 {
340 }
341 else if( aPad->HasHole() && aPad->GetAttribute() == PAD_ATTRIB::NPTH )
342 {
343 pin->mtype = PIN::MOUNT_TYPE::HOLE;
344 }
345 else if( aPad->GetAttribute() == PAD_ATTRIB::SMD )
346 {
347 pin->mtype = PIN::MOUNT_TYPE::SMT;
348 }
349 else
350 {
352 }
353
354 const std::shared_ptr<SHAPE_POLY_SET>& polygons =
356
357 // TODO: Here we put all pad shapes as polygonl, we should switch by pad shape
358 // Note:pad only use polygons->Polygon(0),
359 if( polygons->OutlineCount() > 0 )
360 {
361 pin->m_pinOutlines.push_back( std::make_unique<OUTLINE_CONTOUR>( polygons->Polygon( 0 ) ) );
362 }
363}
364
365
366void EDA_DATA::PIN::Write( std::ostream& ost ) const
367{
368 static const std::map<TYPE, std::string> type_map = { { TYPE::SURFACE, "S" },
369 { TYPE::THROUGH_HOLE, "T" },
370 { TYPE::BLIND, "B" } };
371
372 static const std::map<ELECTRICAL_TYPE, std::string> etype_map = {
376 };
377 static const std::map<MOUNT_TYPE, std::string> mtype_map = { { MOUNT_TYPE::THROUGH_HOLE, "T" },
378 { MOUNT_TYPE::HOLE, "H" },
379 { MOUNT_TYPE::SMT, "S" },
380 { MOUNT_TYPE::UNDEFINED, "U" } };
381
382 ost << "PIN " << m_name << " " << type_map.at( type ) << " " << m_center.first << " "
383 << m_center.second << " 0 " << etype_map.at( etype ) << " " << mtype_map.at( mtype )
384 << std::endl;
385
386 for( const auto& outline : m_pinOutlines )
387 {
388 outline->Write( ost );
389 }
390}
391
392
393void EDA_DATA::PACKAGE::Write( std::ostream& ost ) const
394{
395 ost << "PKG " << m_name << " " << ODB::Data2String( m_pitch ) << " "
396 << ODB::Data2String( m_xmin ) << " " << ODB::Data2String( m_ymin ) << " "
397 << ODB::Data2String( m_xmax ) << " " << ODB::Data2String( m_ymax ) << ";" << std::endl;
398
399 for( const auto& outline : m_pkgOutlines )
400 {
401 outline->Write( ost );
402 }
403
404 for( const auto& pin : m_pinsVec )
405 {
406 pin->Write( ost );
407 }
408}
409
410
411void EDA_DATA::Write( std::ostream& ost ) const
412{
413 ost << "# " << wxDateTime::Now().FormatISOCombined() << std::endl;
414 ost << "HDR KiCad EDA " << TO_UTF8( GetBuildVersion() ) << std::endl;
415 ost << "UNITS=" << PCB_IO_ODBPP::m_unitsStr << std::endl;
416 ost << "LYR";
417
418 for( const auto& layer : layers )
419 {
420 ost << " " << layer;
421 }
422
423 ost << std::endl;
424
425 WriteAttributes( ost, "#" );
426
427 for( const auto& net : nets )
428 {
429 ost << "#NET " << net->m_index << std::endl;
430 net->Write( ost );
431 }
432
433 size_t i = 0;
434 for( const auto* pkg : packages )
435 {
436 ost << "# PKG " << i << std::endl;
437 i++;
438 pkg->Write( ost );
439 ost << "#" << std::endl;
440 }
441}
const char * name
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
wxString GetBuildVersion()
Get the full KiCad version string.
void WriteAttributes(std::ostream &ost, const std::string &prefix="") const
void WriteAttributes(std::ostream &ost) const
VECTOR2I GetFPRelativePosition() const
constexpr const Vec & GetPosition() const
Definition box2.h:211
constexpr const Vec GetEnd() const
Definition box2.h:212
void Write(std::ostream &ost) const
void Write(std::ostream &ost) const
std::list< std::unique_ptr< SUB_NET > > subnets
wxString m_name
void AddPin(const PAD *aPad, size_t aPinNum)
std::vector< std::shared_ptr< PIN > > m_pinsVec
wxString m_name
<! Reference number of the package to be used in CMP.
void Write(std::ostream &ost) const
std::list< std::unique_ptr< PKG_OUTLINE > > m_pkgOutlines
std::list< std::unique_ptr< PKG_OUTLINE > > m_pinOutlines
std::pair< wxString, wxString > m_center
void Write(std::ostream &ost) const
wxString m_name
ELECTRICAL_TYPE etype
MOUNT_TYPE mtype
void WriteSubnet(std::ostream &ost) const override
void WriteSubnet(std::ostream &ost) const override
void WriteSubnet(std::ostream &ost) const override
void WriteSubnet(std::ostream &ost) const override
std::list< FEATURE_ID > feature_ids
EDA_DATA * m_edadata
void Write(std::ostream &ost) const
void AddFeatureID(FEATURE_ID::TYPE type, const wxString &layer, size_t feature_id)
virtual void WriteSubnet(std::ostream &ost) const =0
std::list< const PACKAGE * > packages
std::map< wxString, size_t > layers_map
std::list< const NET * > nets
std::map< size_t, NET > nets_map
std::vector< wxString > layers
std::map< size_t, PACKAGE > packages_map
void AddNET(const NETINFO_ITEM *aNet)
std::vector< std::shared_ptr< FOOTPRINT > > m_eda_footprints
size_t GetLyrIdx(const wxString &aLayerName)
void AddPackage(const FOOTPRINT *aFp)
void Write(std::ostream &ost) const
EDA_ITEM * Clone() const override
Invoke a function on all children.
bool IsFlipped() const
Definition footprint.h:524
Handle the data for a net.
Definition netinfo.h:54
const wxString & GetNetname() const
Definition netinfo.h:112
int GetNetCode() const
Definition netinfo.h:106
void Write(std::ostream &ost) const override
VECTOR2I m_center
void Write(std::ostream &ost) const override
std::unique_ptr< ODB_SURFACE_DATA > m_surfaces
VECTOR2I m_lower_left
void Write(std::ostream &ost) const override
VECTOR2I m_center
void Write(std::ostream &ost) const override
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
Definition pad.h:55
PAD_ATTRIB GetAttribute() const
Definition pad.h:563
const wxString & GetNumber() const
Definition pad.h:137
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pad.h:331
bool IsOnCopperLayer() const override
Definition pad.cpp:1551
const std::shared_ptr< SHAPE_POLY_SET > & GetEffectivePolygon(PCB_LAYER_ID aLayer, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Definition pad.cpp:949
bool HasHole() const override
Definition pad.h:107
static std::string m_unitsStr
Represent a set of closed polygons.
POLYGON & Polygon(int aIndex)
Return the aIndex-th subpolygon in the set.
int OutlineCount() const
Return the number of outlines in the set.
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
size_t hash_fp_item(const EDA_ITEM *aItem, int aFlags)
Calculate hash of an EDA_ITEM.
Definition hash_eda.cpp:58
Hashing functions for EDA_ITEMs.
@ HASH_POS
Definition hash_eda.h:47
@ REL_COORD
Use coordinates relative to the parent object.
Definition hash_eda.h:50
@ F_CrtYd
Definition layer_ids.h:116
@ B_CrtYd
Definition layer_ids.h:115
@ LEFT_RIGHT
Flip left to right (around the Y axis)
Definition mirror.h:28
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:29
wxString Data2String(double aVal)
Definition odb_util.cpp:185
void RemoveWhitespace(wxString &aStr)
Definition odb_util.cpp:143
std::pair< wxString, wxString > AddXY(const VECTOR2I &aVec)
Definition odb_util.cpp:191
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:99
@ PTH
Plated through hole pad.
Definition padstack.h:98
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
KIBIS_PIN * pin
VECTOR2< double > VECTOR2D
Definition vector2d.h:694