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
18 * along with this program. If not, see <https://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>
29#include <string_utils.h>
30
31#include <netinfo.h>
32#include <odb_feature.h>
33#include <pcb_io_odbpp.h>
34
35
37{
38 auto& x = nets_map.emplace( std::piecewise_construct, std::forward_as_tuple( 0 ),
39 std::forward_as_tuple( nets.size(), "$NONE$" ) )
40 .first->second;
41
42 nets.push_back( &x );
43}
44
45
46void EDA_DATA::NET::Write( std::ostream& ost, bool aIncludeComponents ) const
47{
48 ost << "NET " << m_name;
49
50 WriteAttributes( ost );
51
52 // Empty attributes still need their own field before the NET ID
53 if( m_uid != 0 )
54 ost << ( m_ODBattributes.empty() ? ";;ID=" : ";ID=" ) << m_uid;
55
56 ost << std::endl;
57
58 for( const auto& subnet : subnets )
59 {
60 if( !aIncludeComponents && dynamic_cast<const SUB_NET_TOEPRINT*>( subnet.get() ) )
61 continue;
62
63 subnet->Write( ost );
64 }
65}
66
67
68void EDA_DATA::AddNET( const NETINFO_ITEM* aNet, const wxString& aName )
69{
70 if( nets_map.end() == nets_map.find( aNet->GetNetCode() ) )
71 {
72 auto& net = nets_map.emplace( std::piecewise_construct,
73 std::forward_as_tuple( aNet->GetNetCode() ),
74 std::forward_as_tuple( nets.size(), aName ) )
75 .first->second;
76
77 nets.push_back( &net );
78
79 //TODO: netname check
80 }
81}
82
83
85{
86 for( NET* net : nets )
87 net->m_uid = aPlugin.NewUid();
88}
89
90
91bool EDA_DATA::AddShort( const std::set<int>& aNetCodes, const wxString& aLayer,
92 size_t aFeatureIndex, uint32_t aUid )
93{
94 std::set<uint32_t> uids;
95
96 for( int code : aNetCodes )
97 {
98 if( code > 0 && nets_map.count( code ) )
99 uids.insert( nets_map.at( code ).m_uid );
100 }
101
102 if( uids.size() < 2 )
103 return false;
104
105 m_shorts.push_back( { std::move( uids ), GetLyrIdx( aLayer ), aFeatureIndex, aUid } );
106 return true;
107}
108
109
110void EDA_DATA::SUB_NET::Write( std::ostream& ost ) const
111{
112 ost << "SNT ";
113
114 WriteSubnet( ost );
115
116 ost << std::endl;
117
118 for( const auto& fid : feature_ids )
119 {
120 fid.Write( ost );
121 }
122}
123
124
125void EDA_DATA::FEATURE_ID::Write( std::ostream& ost ) const
126{
127 static const std::map<TYPE, std::string> type_map = {
128 { TYPE::COPPER, "C" },
129 { TYPE::HOLE, "H" },
130 };
131
132 ost << "FID " << type_map.at( type ) << " " << layer << " " << feature_id << std::endl;
133}
134
135
136void EDA_DATA::SUB_NET_VIA::WriteSubnet( std::ostream& ost ) const
137{
138 ost << "VIA";
139}
140
141
142void EDA_DATA::SUB_NET_TRACE::WriteSubnet( std::ostream& ost ) const
143{
144 ost << "TRC";
145}
146
147
148void EDA_DATA::SUB_NET_PLANE::WriteSubnet( std::ostream& ost ) const
149{
150 static const std::map<FILL_TYPE, std::string> fill_type_map = { { FILL_TYPE::SOLID, "S" },
151 { FILL_TYPE::OUTLINE, "O" } };
152
153 static const std::map<CUTOUT_TYPE, std::string> cutout_type_map = {
154 { CUTOUT_TYPE::CIRCLE, "C" },
155 { CUTOUT_TYPE::RECTANGLE, "R" },
156 { CUTOUT_TYPE::OCTAGON, "O" },
157 { CUTOUT_TYPE::EXACT, "E" }
158 };
159
160 ost << "PLN " << fill_type_map.at( fill_type ) << " " << cutout_type_map.at( cutout_type )
161 << " " << fill_size;
162}
163
164
165void EDA_DATA::SUB_NET_TOEPRINT::WriteSubnet( std::ostream& ost ) const
166{
167 static const std::map<SIDE, std::string> side_map = {
168 { SIDE::BOTTOM, "B" },
169 { SIDE::TOP, "T" },
170 };
171 ost << "TOP " << side_map.at( side ) << " " << comp_num << " " << toep_num;
172}
173
174
175void EDA_DATA::SUB_NET::AddFeatureID( FEATURE_ID::TYPE type, const wxString& layer,
176 size_t feature_id )
177{
178 feature_ids.emplace_back( type, m_edadata->GetLyrIdx( layer ), feature_id );
179}
180
181
182size_t EDA_DATA::GetLyrIdx( const wxString& aLayer )
183{
184 if( layers_map.count( aLayer ) )
185 {
186 return layers_map.at( aLayer );
187 }
188 else
189 {
190 auto idx = layers_map.size();
191 layers_map.emplace( aLayer, idx );
192 layers.push_back( aLayer );
193 return idx;
194 }
195}
196
197
198void OUTLINE_SQUARE::Write( std::ostream& ost, const ODB_FORMAT& aFormat ) const
199{
200 ost << "SQ " << ODB::Data2String( aFormat, m_center.x ) << " " << ODB::Data2String( aFormat, m_center.y ) << " "
201 << ODB::Data2String( aFormat, m_halfSide ) << std::endl;
202}
203
204
205void OUTLINE_CIRCLE::Write( std::ostream& ost, const ODB_FORMAT& aFormat ) const
206{
207 ost << "CR " << ODB::Data2String( aFormat, m_center.x ) << " " << ODB::Data2String( aFormat, m_center.y ) << " "
208 << ODB::Data2String( aFormat, m_radius ) << std::endl;
209}
210
211
212void OUTLINE_RECT::Write( std::ostream& ost, const ODB_FORMAT& aFormat ) const
213{
214 ost << "RC " << ODB::Data2String( aFormat, m_lower_left.x ) << " " << ODB::Data2String( aFormat, m_lower_left.y )
215 << " " << ODB::Data2String( aFormat, m_width ) << " " << ODB::Data2String( aFormat, m_height ) << std::endl;
216}
217
218
219void OUTLINE_CONTOUR::Write( std::ostream& ost, const ODB_FORMAT& aFormat ) const
220{
221 if( !m_surfaces )
222 return;
223
224 ost << "CT" << std::endl;
225 m_surfaces->WriteData( ost, aFormat );
226 ost << "CE" << std::endl;
227}
228
229
230void EDA_DATA::AddPackage( const FOOTPRINT* aFp, const ODB_FORMAT& aFormat )
231{
232 // ODBPP only need unique PACKAGE in PKG record in eda/data file.
233 // the PKG index can repeat to be ref in CMP record in component file.
234
235 std::shared_ptr<FOOTPRINT> fp( static_cast<FOOTPRINT*>( aFp->Clone() ) );
236 m_eda_footprints.emplace_back( fp );
237 fp->SetParentGroup( nullptr );
238 fp->SetPosition( { 0, 0 } );
239
240 if( aFp->IsFlipped() )
241 {
242 // ODB++ needs both flips
243 fp->Flip( fp->GetPosition(), FLIP_DIRECTION::LEFT_RIGHT );
244 fp->Flip( fp->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
245 }
246
247 fp->SetOrientation( ANGLE_0 );
248
249 size_t hash = hash_fp_item( fp.get(), HASH_POS | REL_COORD );
250 size_t pkg_index = packages_map.size();
251 wxString fp_name = fp->GetFPID().GetLibItemName().wx_str();
252 ODB::RemoveWhitespace( fp_name );
253
254 if( fp_name.IsEmpty() )
255 fp_name = wxS( "__" );
256
257 auto [iter, success] = packages_map.emplace( hash, PACKAGE( pkg_index, fp_name ) );
258
259 if( !success )
260 {
261 return;
262 }
263
264 PACKAGE* pkg = &( iter->second );
265
266 packages.push_back( pkg );
267
268 BOX2I bbox = fp->GetBoundingBox();
269 pkg->m_xmin = bbox.GetPosition().x;
270 pkg->m_ymin = -bbox.GetEnd().y;
271 pkg->m_xmax = bbox.GetEnd().x;
272 pkg->m_ymax = -bbox.GetPosition().y;
273 pkg->m_pitch = UINT64_MAX;
274
275 if( fp->Pads().size() < 2 )
276 pkg->m_pitch = pcbIUScale.mmToIU( 1.0 ); // placeholder value
277
278 for( size_t i = 0; i < fp->Pads().size(); ++i )
279 {
280 const PAD* pad1 = fp->Pads()[i];
281
282 for( size_t j = i + 1; j < fp->Pads().size(); ++j )
283 {
284 const PAD* pad2 = fp->Pads()[j];
285 const uint64_t pin_dist = ( pad1->GetCenter() - pad2->GetCenter() ).EuclideanNorm();
286 pkg->m_pitch = std::min( pkg->m_pitch, pin_dist );
287 }
288 }
289
290 const SHAPE_POLY_SET& courtyard = fp->GetCourtyard( F_CrtYd );
291 const SHAPE_POLY_SET& courtyard_back = fp->GetCourtyard( B_CrtYd );
292 SHAPE_POLY_SET pkg_outline;
293
294 if( courtyard.OutlineCount() > 0 )
295 pkg_outline = courtyard;
296
297 if( courtyard_back.OutlineCount() > 0 )
298 {
299 pkg_outline = courtyard_back;
300 }
301
302 if( !courtyard.OutlineCount() && !courtyard_back.OutlineCount() )
303 {
304 pkg_outline = fp->GetBoundingHull();
305 }
306
307 // TODO: Here we put rect, square, and circle, all as polygon
308
309 if( pkg_outline.OutlineCount() > 0 )
310 {
311 for( int ii = 0; ii < pkg_outline.OutlineCount(); ++ii )
312 {
313 pkg->m_pkgOutlines.push_back(
314 std::make_unique<OUTLINE_CONTOUR>( pkg_outline.Polygon( ii ) ) );
315 }
316 }
317
318 for( size_t i = 0; i < fp->Pads().size(); ++i )
319 {
320 const PAD* pad = fp->Pads()[i];
321 pkg->AddPin( pad, i, aFormat );
322 }
323
324 return;
325}
326
327
328void EDA_DATA::PACKAGE::AddPin( const PAD* aPad, size_t aPinNum, const ODB_FORMAT& aFormat )
329{
330 FAB_PIN fabPin = MakeFabPin( *aPad, aPinNum );
331 wxString name = fabPin.m_name;
332
333 // ODB is unhappy with whitespace in most places
335
336 // Whitespace-only pad numbers can become empty after filtering
337 if( name.IsEmpty() )
338 {
339 name = wxString::Format( "PAD%zu", aPinNum );
340 }
341
342 // // for SNT record, pad, net, pin
343 std::shared_ptr<PIN> pin = std::make_shared<PIN>( m_pinsVec.size(), name );
344 m_pinsVec.push_back( pin );
345
346 VECTOR2D relpos = aPad->GetFPRelativePosition();
347
348 pin->m_center = ODB::AddRelativeXY( aFormat, relpos );
349
350 if( aPad->HasHole() )
351 {
352 pin->type = PIN::TYPE::THROUGH_HOLE;
353 }
354 else
355 {
356 pin->type = PIN::TYPE::SURFACE;
357 }
358
359 switch( fabPin.m_electrical )
360 {
364 }
365
366 switch( fabPin.m_mount )
367 {
369 case FAB_MOUNT::HOLE: pin->mtype = PIN::MOUNT_TYPE::HOLE; break;
370 case FAB_MOUNT::SMT: pin->mtype = PIN::MOUNT_TYPE::SMT; break;
371 case FAB_MOUNT::OTHER: pin->mtype = PIN::MOUNT_TYPE::UNDEFINED; break;
372 }
373
374 if( fabPin.m_role == FAB_PAD_ROLE::PRESSFIT )
376
377 // AddPackage() unflips the footprint, so this pin record always describes the component-side
378 // land
379 PCB_LAYER_ID mountLayer = aPad->Padstack().EffectiveLayerFor( F_Cu );
380
381 const std::shared_ptr<SHAPE_POLY_SET>& polygons = aPad->GetEffectivePolygon( mountLayer, ERROR_INSIDE );
382
383 // TODO: Here we put all pad shapes as polygonl, we should switch by pad shape
384 // Note:pad only use polygons->Polygon(0),
385 if( polygons->OutlineCount() > 0 )
386 {
387 pin->m_pinOutlines.push_back( std::make_unique<OUTLINE_CONTOUR>( polygons->Polygon( 0 ) ) );
388 }
389}
390
391
392void EDA_DATA::PIN::Write( std::ostream& ost, const ODB_FORMAT& aFormat ) const
393{
394 static const std::map<TYPE, std::string> type_map = { { TYPE::SURFACE, "S" },
395 { TYPE::THROUGH_HOLE, "T" },
396 { TYPE::BLIND, "B" } };
397
398 static const std::map<ELECTRICAL_TYPE, std::string> etype_map = {
402 };
403 static const std::map<MOUNT_TYPE, std::string> mtype_map = { { MOUNT_TYPE::THROUGH_HOLE, "T" },
404 { MOUNT_TYPE::HOLE, "H" },
405 { MOUNT_TYPE::SMT, "S" },
406 { MOUNT_TYPE::PRESSFIT, "P" },
407 { MOUNT_TYPE::UNDEFINED, "U" } };
408
409 ost << "PIN " << m_name.utf8_string() << " " << type_map.at( type ) << " " << m_center.first << " "
410 << m_center.second << " 0 " << etype_map.at( etype ) << " " << mtype_map.at( mtype ) << std::endl;
411
412 for( const auto& outline : m_pinOutlines )
413 {
414 outline->Write( ost, aFormat );
415 }
416}
417
418
419void EDA_DATA::PACKAGE::Write( std::ostream& ost, const ODB_FORMAT& aFormat ) const
420{
421 ODB_FORMAT localFormat = aFormat;
422 localFormat.m_originOffset = { 0, 0 };
423 ost << "PKG " << m_name << " " << ODB::Data2String( localFormat, m_pitch ) << " "
424 << ODB::Data2String( localFormat, m_xmin ) << " " << ODB::Data2String( localFormat, m_ymin ) << " "
425 << ODB::Data2String( localFormat, m_xmax ) << " " << ODB::Data2String( localFormat, m_ymax );
426
427 WriteAttributes( ost );
428
429 ost << std::endl;
430
431 for( const auto& outline : m_pkgOutlines )
432 {
433 outline->Write( ost, localFormat );
434 }
435
436 for( const auto& pin : m_pinsVec )
437 {
438 pin->Write( ost, localFormat );
439 }
440}
441
442
443void EDA_DATA::Write( std::ostream& ost, const ODB_FORMAT& aFormat ) const
444{
445 ost << "# " << wxDateTime::Now().FormatISOCombined() << std::endl;
446 ost << "HDR KiCad EDA " << TO_UTF8( GetBuildVersion() ) << std::endl;
447 ost << "UNITS=" << aFormat.m_unitsStr << std::endl;
448 ost << "LYR";
449
450 for( const auto& layer : layers )
451 {
452 ost << " " << layer;
453 }
454
455 ost << std::endl;
456
457 WriteAttributes( ost, "#" );
458
459 if( aFormat.m_writeEdaNets )
460 {
461 for( const auto& net : nets )
462 {
463 ost << "#NET " << net->m_index << std::endl;
464 net->Write( ost, aFormat.Includes( FAB::SECTION::COMPONENTS ) );
465 }
466 }
467
468 size_t i = 0;
469 for( const auto* pkg : packages )
470 {
471 if( !aFormat.Includes( FAB::SECTION::PACKAGES ) )
472 break;
473
474 ost << "# PKG " << i << std::endl;
475 i++;
476 pkg->Write( ost, aFormat );
477 ost << "#" << std::endl;
478 }
479}
480
481
482void EDA_DATA::WriteShortf( std::ostream& ost, const ODB_FORMAT& aFormat ) const
483{
484 ost << "UNITS=" << aFormat.m_unitsStr << std::endl;
485 ost << "LYR";
486
487 for( const wxString& layer : layers )
488 ost << " " << layer;
489
490 ost << std::endl;
491
492 for( const SHORT_NET& shortNet : m_shorts )
493 {
494 ost << "SRTF";
495
496 for( uint32_t uid : shortNet.m_netUids )
497 ost << " " << uid;
498
499 ost << ";ID=" << shortNet.m_uid << std::endl;
500 ost << "FID C " << shortNet.m_layerIndex << " " << shortNet.m_featureIndex << std::endl;
501 }
502}
const char * name
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
wxString GetBuildVersion()
Get the full KiCad version string.
void WriteAttributes(std::ostream &ost, const std::string &prefix="") const
void WriteAttributes(std::ostream &ost) const
std::map< unsigned int, std::string > m_ODBattributes
VECTOR2I GetFPRelativePosition() const
constexpr const Vec & GetPosition() const
Definition box2.h:208
constexpr const Vec GetEnd() const
Definition box2.h:209
void Write(std::ostream &ost) const
std::list< std::unique_ptr< SUB_NET > > subnets
void Write(std::ostream &ost, bool aIncludeComponents) const
wxString m_name
void AddPin(const PAD *aPad, size_t aPinNum, const ODB_FORMAT &aFormat)
std::vector< std::shared_ptr< PIN > > m_pinsVec
void Write(std::ostream &ost, const ODB_FORMAT &aFormat) const
wxString m_name
<! Reference number of the package to be used in CMP.
std::list< std::unique_ptr< PKG_OUTLINE > > m_pkgOutlines
std::list< std::unique_ptr< PKG_OUTLINE > > m_pinOutlines
void Write(std::ostream &ost, const ODB_FORMAT &aFormat) const
std::pair< wxString, wxString > m_center
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
bool AddShort(const std::set< int > &aNetCodes, const wxString &aLayer, size_t aFeatureIndex, uint32_t aUid)
std::list< const PACKAGE * > packages
std::list< NET * > nets
std::map< wxString, size_t > layers_map
std::map< size_t, NET > nets_map
void AssignNetUids(PCB_IO_ODBPP &aPlugin)
std::vector< wxString > layers
void WriteShortf(std::ostream &ost, const ODB_FORMAT &aFormat) const
void AddPackage(const FOOTPRINT *aFp, const ODB_FORMAT &aFormat)
void Write(std::ostream &ost, const ODB_FORMAT &aFormat) const
std::map< size_t, PACKAGE > packages_map
std::vector< SHORT_NET > m_shorts
void AddNET(const NETINFO_ITEM *aNet, const wxString &aName)
std::vector< std::shared_ptr< FOOTPRINT > > m_eda_footprints
size_t GetLyrIdx(const wxString &aLayerName)
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
bool IsFlipped() const
Definition footprint.h:662
Handle the data for a net.
Definition netinfo.h:50
int GetNetCode() const
Definition netinfo.h:104
void Write(std::ostream &ost, const ODB_FORMAT &aFormat) const override
VECTOR2I m_center
void Write(std::ostream &ost, const ODB_FORMAT &aFormat) const override
std::unique_ptr< ODB_SURFACE_DATA > m_surfaces
VECTOR2I m_lower_left
void Write(std::ostream &ost, const ODB_FORMAT &aFormat) const override
VECTOR2I m_center
void Write(std::ostream &ost, const ODB_FORMAT &aFormat) const override
PCB_LAYER_ID EffectiveLayerFor(PCB_LAYER_ID aLayer) const
Determines which geometry layer should be used for the given input layer.
Definition pad.h:61
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pad.h:329
const PADSTACK & Padstack() const
Definition pad.h:331
const std::shared_ptr< SHAPE_POLY_SET > & GetEffectivePolygon(PCB_LAYER_ID aLayer, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Definition pad.cpp:1240
bool HasHole() const override
Definition pad.h:113
uint32_t NewUid()
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:448
FAB_PIN MakeFabPin(const PAD &aPad, size_t aIndexInFootprint)
aIndexInFootprint is the pad's position in its footprint, used to name NPTH and unnumbered pads
Definition fab_pin.cpp:50
size_t hash_fp_item(const EDA_ITEM *aItem, int aFlags)
Calculate hash of an EDA_ITEM.
Definition hash_eda.cpp:54
Hashing functions for EDA_ITEMs.
@ HASH_POS
Definition hash_eda.h:43
@ REL_COORD
Use coordinates relative to the parent object.
Definition hash_eda.h:46
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition idf_helpers.h:37
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ B_CrtYd
Definition layer_ids.h:111
@ F_Cu
Definition layer_ids.h:60
@ LEFT_RIGHT
Flip left to right (around the Y axis)
Definition mirror.h:24
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:25
void RemoveWhitespace(wxString &aStr)
Definition odb_util.cpp:374
wxString Data2String(const ODB_FORMAT &aFormat, double aVal)
Definition odb_util.cpp:406
std::pair< wxString, wxString > AddRelativeXY(const ODB_FORMAT &aFormat, const VECTOR2I &aVec)
Definition odb_util.cpp:419
wxString m_name
Definition fab_pin.h:58
FAB_ELECTRICAL m_electrical
Definition fab_pin.h:61
FAB_MOUNT m_mount
Definition fab_pin.h:60
FAB_PAD_ROLE m_role
Definition fab_pin.h:59
bool Includes(FAB::SECTION aSection) const
Definition odb_util.h:85
VECTOR2I m_originOffset
Definition odb_util.h:77
std::string m_unitsStr
Definition odb_util.h:73
bool m_writeEdaNets
Definition odb_util.h:79
KIBIS_PIN * pin
VECTOR2< double > VECTOR2D
Definition vector2d.h:707