KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_odbpp_attributes.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 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
22
23#include <algorithm>
24#include <cmath>
25#include <cctype>
26#include <fstream>
27#include <memory>
28#include <map>
29#include <sstream>
30#include <string>
31#include <vector>
32
33#include <board.h>
34#include <base_units.h>
37#include <footprint.h>
38#include <netinfo.h>
39#include <pad.h>
40#include <pcb_track.h>
46
47#include "odb_test_utils.h"
48
49namespace fs = std::filesystem;
50
51
52BOOST_AUTO_TEST_CASE( FabComponentRowUsesSavedVariantFields )
53{
55 + "variant_test/variant_test.kicad_pcb" );
56 BOOST_REQUIRE( board );
57 const FOOTPRINT* resistor = nullptr;
58
59 for( const FOOTPRINT* footprint : board->Footprints() )
60 {
61 if( footprint->GetReference() == wxS( "R1" ) )
62 resistor = footprint;
63 }
64
65 BOOST_REQUIRE( resistor );
66 FAB_COMPONENT_ROW normal = MakeFabComponentRow( *resistor, wxString() );
67 FAB_COMPONENT_ROW variant = MakeFabComponentRow( *resistor, wxS( "Variant A" ) );
68 BOOST_CHECK( normal.Field( wxS( "Datasheet" ) ).IsEmpty() );
69 BOOST_CHECK_EQUAL( variant.Field( wxS( "Datasheet" ) ), wxString( wxS( "test" ) ) );
70 BOOST_CHECK( variant.m_mount == FAB_MOUNT::SMT );
71}
72
73
74BOOST_AUTO_TEST_CASE( FabTestPointsKeepSavedBoardViaOrder )
75{
77 + "connect/connect.kicad_pcb" );
78 BOOST_REQUIRE( board );
79 BOOST_REQUIRE( board->BuildConnectivity() );
80 std::vector<FAB_TEST_POINT> points = FabTestPoints( *board );
81 BOOST_REQUIRE( !points.empty() );
82 bool sawPad = false;
83 size_t vias = 0;
84
85 for( const FAB_TEST_POINT& point : points )
86 {
87 if( point.m_pad )
88 {
89 sawPad = true;
90 BOOST_CHECK( !point.m_via );
91 }
92 else
93 {
94 BOOST_CHECK( !sawPad );
95 BOOST_REQUIRE( point.m_via );
96 BOOST_CHECK( point.m_position == point.m_via->GetPosition() );
97 ++vias;
98 }
99 }
100
101 BOOST_CHECK_GT( vias, 0u );
102 BOOST_CHECK( sawPad );
103}
104
105
106BOOST_AUTO_TEST_CASE( FabAnonymousNetNamesFollowSavedNetCodes )
107{
109 + "odbpp/net_name_collision.kicad_pcb" );
110 BOOST_REQUIRE( board );
111 std::map<int, wxString> names = FabAnonymousNetNames( *board );
112 BOOST_REQUIRE_GT( names.size(), 1u );
113 size_t ordinal = 0;
114
115 for( const auto& [code, name] : names )
116 {
117 BOOST_CHECK_GT( code, 0 );
118 BOOST_CHECK_EQUAL( name, wxString::Format( wxS( "NET_%zu" ), ++ordinal ) );
119 }
120
121 const NETINFO_ITEM* supply = board->FindNet( wxS( "+12V" ) );
122 const NETINFO_ITEM* q2 = board->FindNet( wxS( "Net-(Q2-C)" ) );
123 const NETINFO_ITEM* q1 = board->FindNet( wxS( "Net-(Q1-C)" ) );
124 BOOST_REQUIRE( supply );
125 BOOST_REQUIRE( q2 );
126 BOOST_REQUIRE( q1 );
127 BOOST_REQUIRE_LT( q2->GetNetCode(), q1->GetNetCode() );
128 BOOST_CHECK_EQUAL( names.at( supply->GetNetCode() ), wxString( wxS( "NET_1" ) ) );
129 BOOST_CHECK_EQUAL( names.at( q2->GetNetCode() ), wxString( wxS( "NET_2" ) ) );
130 BOOST_CHECK_EQUAL( names.at( q1->GetNetCode() ), wxString( wxS( "NET_3" ) ) );
131}
132
133
134BOOST_AUTO_TEST_CASE( OdbPadUsageOptionOrderMatchesSpec )
135{
136 BOOST_CHECK_EQUAL( static_cast<int>( ODB_ATTR::PAD_USAGE::TOEPRINT ), 0 );
137 BOOST_CHECK_EQUAL( static_cast<int>( ODB_ATTR::PAD_USAGE::VIA ), 1 );
138 BOOST_CHECK_EQUAL( static_cast<int>( ODB_ATTR::PAD_USAGE::G_FIDUCIAL ), 2 );
139 BOOST_CHECK_EQUAL( static_cast<int>( ODB_ATTR::PAD_USAGE::L_FIDUCIAL ), 3 );
141 BOOST_CHECK_EQUAL( static_cast<int>( ODB_ATTR::PAD_USAGE::BOND_FINGER ), 5 );
142}
143
144
145BOOST_AUTO_TEST_CASE( OdbFiducialPadUsage )
146{
148 + "odbpp/pad_roles.kicad_pcb" );
149 BOOST_REQUIRE( board );
150
151 size_t globals = CountPads( *board, PAD_PROP::FIDUCIAL_GLBL, F_Cu );
152 size_t locals = CountPads( *board, PAD_PROP::FIDUCIAL_LOCAL, F_Cu );
153 BOOST_REQUIRE_GT( globals, 0 );
154 BOOST_REQUIRE_EQUAL( locals, 1 );
155
156 ODB_PRODUCT product( ExportOdb( *board, TempDir().Path() ) );
157 OdbFeatureFile top( fs::path( product.LayerFile( ODB::GenLegalEntityName( board->GetLayerName( F_Cu ) ),
158 wxS( "features" ) ).ToStdWstring() ) );
159
160 BOOST_CHECK_EQUAL( top.CountRecordsWithOption( ".pad_usage", "g_fiducial" ), globals );
161 BOOST_CHECK_EQUAL( top.CountRecordsWithOption( ".pad_usage", "l_fiducial" ), locals );
162}
163
164
165BOOST_AUTO_TEST_CASE( OdbTestPointFeatureAndCadnetFlag )
166{
168 + "odbpp/pad_roles.kicad_pcb" );
169 BOOST_REQUIRE( board );
170 BOOST_REQUIRE_EQUAL( CountPads( *board, PAD_PROP::TESTPOINT, F_Cu ), 1 );
171
172 const PAD* testPoint = nullptr;
173
174 for( const FOOTPRINT* footprint : board->Footprints() )
175 {
176 for( const PAD* pad : footprint->Pads() )
177 {
178 if( pad->GetProperty() == PAD_PROP::TESTPOINT )
179 testPoint = pad;
180 }
181 }
182
183 BOOST_REQUIRE( testPoint );
184
185 fs::path root = ExportOdb( *board, TempDir().Path() );
186 ODB_PRODUCT product( root );
187 OdbFeatureFile top( fs::path( product.LayerFile( ODB::GenLegalEntityName( board->GetLayerName( F_Cu ) ),
188 wxS( "features" ) ).ToStdWstring() ) );
189 BOOST_CHECK_EQUAL( top.CountRecordsWithFlag( ".test_point" ), 1 );
190
191 size_t taggedPoints = 0;
192
193 for( const std::string& line : ReadLines( root / "steps" / "pcb" / "netlists" / "cadnet" / "netlist" ) )
194 {
195 std::istringstream stream( line );
196 std::vector<std::string> fields;
197 std::string token;
198
199 while( stream >> token )
200 fields.push_back( token );
201
202 if( fields.size() < 9 || fields[0].empty() || !std::isdigit( fields[0][0] ) )
203 continue;
204
205 double x = std::stod( fields[2] );
206 double y = std::stod( fields[3] );
207 double padX = pcbIUScale.IUTomm( testPoint->GetPosition().x );
208 double padY = -pcbIUScale.IUTomm( testPoint->GetPosition().y );
209
210 if( std::abs( x - padX ) < 0.00001 && std::abs( y - padY ) < 0.00001 )
211 {
212 BOOST_CHECK_EQUAL( fields[7], "e" );
213 BOOST_CHECK_EQUAL( fields.back(), "t" );
214 ++taggedPoints;
215 }
216 }
217
218 BOOST_CHECK_EQUAL( taggedPoints, 1 );
219}
220
221
222BOOST_AUTO_TEST_CASE( OdbConnectedViaIsCadnetMidpoint )
223{
225 + "connect/connect.kicad_pcb" );
226 BOOST_REQUIRE( board );
227 BOOST_REQUIRE( board->BuildConnectivity() );
228 const FAB_TEST_POINT* midpoint = nullptr;
229 std::vector<FAB_TEST_POINT> points = FabTestPoints( *board );
230
231 for( const FAB_TEST_POINT& point : points )
232 {
233 if( point.m_via && point.m_netCode > 0 && ( point.m_front || point.m_back ) && !point.m_netEnd )
234 {
235 midpoint = &point;
236 break;
237 }
238 }
239
240 BOOST_REQUIRE_MESSAGE( midpoint, "connect board has no exposed midpoint via" );
241 fs::path root = ExportOdb( *board, TempDir().Path() );
242 bool found = false;
243
244 for( const std::string& line : ReadLines( root / "steps" / "pcb" / "netlists" / "cadnet" / "netlist" ) )
245 {
246 std::istringstream stream( line );
247 std::vector<std::string> fields;
248 std::string token;
249
250 while( stream >> token )
251 fields.push_back( token );
252
253 if( fields.size() < 8 || fields.back() != "v" || fields[0] != std::to_string( midpoint->m_netCode ) )
254 continue;
255
256 double x = pcbIUScale.IUTomm( midpoint->m_position.x );
257 double y = -pcbIUScale.IUTomm( midpoint->m_position.y );
258
259 if( std::abs( std::stod( fields[2] ) - x ) < 0.00001
260 && std::abs( std::stod( fields[3] ) - y ) < 0.00001 )
261 {
262 BOOST_CHECK_EQUAL( fields[5], "m" );
263 found = true;
264 }
265 }
266
267 BOOST_CHECK( found );
268}
269
270
271BOOST_AUTO_TEST_CASE( OdbPressFitUsesDrillToolComponentAndPinRoles )
272{
274 + "odbpp/pad_roles.kicad_pcb" );
275 BOOST_REQUIRE( board );
276 BOOST_REQUIRE_EQUAL( CountPads( *board, PAD_PROP::PRESSFIT, F_Cu ), 1 );
277
278 fs::path root = ExportOdb( *board, TempDir().Path() );
279 ODB_PRODUCT product( root );
280 wxString drillLayer;
281
282 for( const ODB_MATRIX_ROW& row : product.Matrix() )
283 {
284 if( row.m_name.StartsWith( wxS( "drill_plated_" ) ) )
285 drillLayer = row.m_name;
286 }
287
288 BOOST_REQUIRE( !drillLayer.IsEmpty() );
289 OdbFeatureFile drill( fs::path( product.LayerFile( drillLayer, wxS( "features" ) ).ToStdWstring() ) );
290 BOOST_CHECK_EQUAL( drill.CountRecordsWithOption( ".plated_type", "press_fit" ), 1 );
291
292 size_t pressFitTools = 0;
293
294 for( const std::string& line : ReadLines( fs::path( product.LayerFile( drillLayer,
295 wxS( "tools" ) ).ToStdWstring() ) ) )
296 {
297 if( line.find( "TYPE2=PRESS_FIT" ) != std::string::npos )
298 ++pressFitTools;
299 }
300
301 BOOST_CHECK_GT( pressFitTools, 0 );
302
303 size_t pressFitPins = 0;
304
305 for( const std::string& line : ReadLines( root / "steps" / "pcb" / "eda" / "data" ) )
306 {
307 if( line.rfind( "PIN ", 0 ) == 0 && line.size() >= 2
308 && line.compare( line.size() - 2, 2, " P" ) == 0 )
309 ++pressFitPins;
310 }
311
312 BOOST_CHECK_EQUAL( pressFitPins, 1 );
313
314 size_t pressFitComponents = 0;
315
316 for( const ODB_MATRIX_ROW& row : product.Matrix() )
317 {
318 if( row.m_type != wxS( "COMPONENT" ) )
319 continue;
320
321 OdbFeatureFile components( fs::path( product.LayerFile( row.m_name, wxS( "components" ) ).ToStdWstring() ) );
322 pressFitComponents += components.CountRecordsWithOption( ".comp_mount_type", "pressfit" );
323 }
324
325 BOOST_CHECK_EQUAL( pressFitComponents, 1 );
326}
327
328
329BOOST_AUTO_TEST_CASE( OdbUnnumberedNpthUsesToolingHoleRole )
330{
332 + "odbpp/pad_roles.kicad_pcb" );
333 BOOST_REQUIRE( board );
334
335 ODB_PRODUCT product( ExportOdb( *board, TempDir().Path() ) );
336 wxString drillLayer;
337
338 for( const ODB_MATRIX_ROW& row : product.Matrix() )
339 {
340 if( row.m_name.StartsWith( wxS( "drill_non-plated_" ) ) )
341 drillLayer = row.m_name;
342 }
343
344 BOOST_REQUIRE( !drillLayer.IsEmpty() );
345 OdbFeatureFile drill( fs::path( product.LayerFile( drillLayer, wxS( "features" ) ).ToStdWstring() ) );
346 size_t nonPlated = drill.CountRecordsWithOption( ".drill", "non_plated" );
347 BOOST_REQUIRE_GT( nonPlated, 0 );
348 BOOST_CHECK_EQUAL( drill.CountRecordsWithOption( ".pad_usage", "tooling_hole" ), nonPlated );
349}
350
351
352BOOST_AUTO_TEST_CASE( OdbMicroviasUseLaserAttributesAndTools )
353{
355 + "padstacks.kicad_pcb" );
356 BOOST_REQUIRE( board );
357 size_t microvias = 0;
358
359 for( PCB_TRACK* track : board->Tracks() )
360 {
361 if( track->Type() == PCB_VIA_T && static_cast<PCB_VIA*>( track )->GetViaType() == VIATYPE::MICROVIA )
362 ++microvias;
363 }
364
365 BOOST_REQUIRE_GT( microvias, 0 );
366 ODB_PRODUCT product( ExportOdb( *board, TempDir().Path() ) );
367 size_t laserFeatures = 0;
368 size_t laserTools = 0;
369
370 for( const ODB_MATRIX_ROW& row : product.Matrix() )
371 {
372 if( row.m_type != wxS( "DRILL" ) )
373 continue;
374
375 OdbFeatureFile features( fs::path( product.LayerFile( row.m_name, wxS( "features" ) ).ToStdWstring() ) );
376 laserFeatures += features.CountRecordsWithOption( ".via_type", "laser" );
377
378 for( const std::string& line : ReadLines( fs::path( product.LayerFile( row.m_name,
379 wxS( "tools" ) ).ToStdWstring() ) ) )
380 {
381 if( line.find( "TYPE2=LASER" ) != std::string::npos )
382 ++laserTools;
383 }
384 }
385
386 BOOST_CHECK_EQUAL( laserFeatures, microvias );
387 BOOST_CHECK_GT( laserTools, 0 );
388}
389
390
391BOOST_AUTO_TEST_CASE( OdbNpthPressFitNeverUsesPressFitTool )
392{
393 const std::vector<std::pair<std::string, bool>> fixtures = {
394 { "odbpp/npth_pressfit.kicad_pcb", false }, { "odbpp/npth_pressfit_slot.kicad_pcb", true }
395 };
396
397 for( const auto& [name, isSlot] : fixtures )
398 {
399 std::unique_ptr<BOARD> board = KI_TEST::ReadBoardFromFileOrStream( KI_TEST::GetPcbnewTestDataDir() + name );
400 BOOST_REQUIRE( board );
401 size_t invalidPads = 0;
402
403 for( const FOOTPRINT* footprint : board->Footprints() )
404 {
405 for( const PAD* pad : footprint->Pads() )
406 {
407 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && pad->GetProperty() == PAD_PROP::PRESSFIT )
408 {
409 ++invalidPads;
410 BOOST_CHECK_EQUAL( pad->GetDrillSizeX() != pad->GetDrillSizeY(), isSlot );
411 }
412 }
413 }
414
415 BOOST_REQUIRE_EQUAL( invalidPads, 1 );
416 ODB_PRODUCT product( ExportOdb( *board, TempDir().Path() ) );
417 size_t nonPlatedTools = 0;
418 size_t pressFitTools = 0;
419
420 for( const ODB_MATRIX_ROW& row : product.Matrix() )
421 {
422 if( row.m_type != wxS( "DRILL" ) )
423 continue;
424
425 fs::path toolsPath( product.LayerFile( row.m_name, wxS( "tools" ) ).ToStdWstring() );
426
427 for( const std::string& line : ReadLines( toolsPath ) )
428 {
429 if( line.find( "TYPE=NON_PLATED" ) != std::string::npos )
430 ++nonPlatedTools;
431
432 if( line.find( "TYPE2=PRESS_FIT" ) != std::string::npos )
433 ++pressFitTools;
434 }
435 }
436
437 BOOST_CHECK_GT( nonPlatedTools, 0 );
438 BOOST_CHECK_EQUAL( pressFitTools, 0 );
439 }
440}
441
442
443BOOST_AUTO_TEST_CASE( OdbStepBoardThicknessMatchesDefaultStackup )
444{
446 + "custom_pads.kicad_pcb" );
447 BOOST_REQUIRE( board );
448 BOOST_REQUIRE( !board->GetDesignSettings().m_HasStackup );
449
450 fs::path root = ExportOdb( *board, TempDir().Path() );
451 std::vector<std::string> lines = ReadLines( root / "steps" / "pcb" / "attrlist" );
452 BOOST_REQUIRE( !lines.empty() );
453 BOOST_CHECK_EQUAL( lines.front(), "UNITS=MM" );
454 BOOST_CHECK( std::find( lines.begin(), lines.end(), ".board_thickness=1600.0" ) != lines.end() );
455
456 fs::path inchRoot = ExportOdb( *board, TempDir().Path(), "inch" );
457 std::vector<std::string> inchLines = ReadLines( inchRoot / "steps" / "pcb" / "attrlist" );
458 BOOST_REQUIRE( inchLines.size() >= 2 );
459 BOOST_CHECK_EQUAL( inchLines.front(), "UNITS=INCH" );
460 BOOST_CHECK_EQUAL( inchLines[1].substr( 0, 17 ), ".board_thickness=" );
461 BOOST_CHECK_CLOSE( std::stod( inchLines[1].substr( 17 ) ), 62.992126, 0.01 );
462}
463
464
465BOOST_AUTO_TEST_CASE( OdbLayerMaterialHasUserAttributeDeclaration )
466{
468 + "ipc2581/dielectric-sublayer.kicad_pcb" );
469 BOOST_REQUIRE( board );
470
471 fs::path root = ExportOdb( *board, TempDir().Path() );
472 ODB_PRODUCT product( root );
473 size_t materials = 0;
474
475 for( const ODB_MATRIX_ROW& row : product.Matrix() )
476 {
477 if( row.m_type != wxS( "DIELECTRIC" ) )
478 continue;
479
480 fs::path attrlistPath( product.LayerFile( row.m_name, wxS( "attrlist" ) ).ToStdWstring() );
481 std::vector<std::string> attrlist = ReadLines( attrlistPath );
482 BOOST_REQUIRE( !attrlist.empty() );
483 BOOST_CHECK_EQUAL( attrlist.front(), "UNITS=MM" );
484
485 for( const std::string& line : attrlist )
486 {
487 if( line.rfind( "material=", 0 ) == 0 )
488 ++materials;
489
490 BOOST_CHECK( line.rfind( ".material=", 0 ) != 0 );
491 }
492 }
493
494 BOOST_CHECK_GT( materials, 0 );
495 std::vector<std::string> declaration = ReadLines( root / "misc" / "userattr" );
496
497 for( const std::string& field : { "UNITS=MM", "TEXT {", "NAME=material", "ENTITY=LAYER", "MAX_LEN=64" } )
498 BOOST_CHECK( std::find( declaration.begin(), declaration.end(), field ) != declaration.end() );
499}
500
501
502BOOST_AUTO_TEST_CASE( OdbBackdrillStopLayerFollowsMustCutCopper )
503{
505 + "issue25021/backdrill.kicad_pcb" );
506 BOOST_REQUIRE( board );
507
508 ODB_PRODUCT product( ExportOdb( *board, TempDir().Path() ) );
509 const std::map<std::pair<wxString, wxString>, wxString> stopLayers = {
510 { { wxS( "f.cu" ), wxS( "in3.cu" ) }, wxS( "in4.cu" ) },
511 { { wxS( "in6.cu" ), wxS( "b.cu" ) }, wxS( "in5.cu" ) },
512 { { wxS( "in3.cu" ), wxS( "b.cu" ) }, wxS( "in2.cu" ) }
513 };
514 size_t checked = 0;
515
516 for( const ODB_MATRIX_ROW& row : product.Matrix() )
517 {
518 if( row.m_addType != wxS( "BACKDRILL" ) )
519 continue;
520
521 auto it = stopLayers.find( { row.m_startName, row.m_endName } );
522 BOOST_REQUIRE( it != stopLayers.end() );
523 ++checked;
524 std::string expected = ".backdrill_penetrate_stop_layer=" + it->second.ToStdString();
525 fs::path attrlistPath( product.LayerFile( row.m_name, wxS( "attrlist" ) ).ToStdWstring() );
526 std::vector<std::string> lines = ReadLines( attrlistPath );
527 BOOST_CHECK( std::find( lines.begin(), lines.end(), expected ) != lines.end() );
528 }
529
530 BOOST_CHECK_EQUAL( checked, stopLayers.size() );
531}
532
533
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
General utilities for PCB file IO for QA programs.
Handle the data for a net.
Definition netinfo.h:50
int GetNetCode() const
Definition netinfo.h:104
The matrix and layer directories of an exported ODB++ product model tree.
wxString LayerFile(const wxString &aLayer, const wxString &aFile) const
Path of a file in a layer directory, matched to the directory name without regard to case.
const std::vector< ODB_MATRIX_ROW > & Matrix() const
size_t CountRecordsWithOption(const std::string &aName, const std::string &aOption) const
Definition pad.h:61
VECTOR2I GetPosition() const override
Definition pad.cpp:256
VIATYPE GetViaType() const
Definition pcb_track.h:418
FAB_COMPONENT_ROW MakeFabComponentRow(const FOOTPRINT &aFootprint, const wxString &aVariant)
std::map< int, wxString > FabAnonymousNetNames(const BOARD &aBoard)
Anonymous names NET_1 through NET_n keyed and numbered by net code.
std::vector< FAB_TEST_POINT > FabTestPoints(const BOARD &aBoard)
The caller must build board connectivity before enumerating net endpoints.
@ F_Cu
Definition layer_ids.h:60
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
std::unique_ptr< BOARD > ReadBoardFromFileOrStream(const std::string &aFilename, std::istream &aFallback)
Read a board from a file, or another stream, as appropriate.
wxString GenLegalEntityName(const wxString &aStr)
Definition odb_util.cpp:212
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
size_t CountPads(const BOARD &aBoard, PAD_PROP aProperty, PCB_LAYER_ID aLayer)
fs::path ExportOdb(const BOARD &aBoard, const fs::path &aDir, const std::string &aUnits, const std::string &aSigfig, REPORTER *aReporter)
std::vector< std::string > ReadLines(const fs::path &aFile)
const KI_TEST::SCOPED_TEMP_DIR & TempDir()
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
@ FIDUCIAL_LOCAL
a fiducial (usually a smd) local to the parent footprint
Definition padstack.h:117
@ FIDUCIAL_GLBL
a fiducial (usually a smd) for the full board
Definition padstack.h:116
@ PRESSFIT
a PTH with a hole diameter with tight tolerances for press fit pin
Definition padstack.h:122
@ TESTPOINT
a test point pad
Definition padstack.h:118
Footprint facts resolved for one assembly variant.
wxString Field(const wxString &aName) const
Variant-resolved shown text, empty when the field is missing.
One LAYER block of matrix/matrix.
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
KIBIS top(path, &reporter)
VECTOR3I expected(15, 30, 45)
BOOST_AUTO_TEST_CASE(FabComponentRowUsesSavedVariantFields)
BOOST_CHECK_EQUAL(result, "25.4")
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89