KiCad PCB EDA Suite
Loading...
Searching...
No Matches
odb_util.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 <string>
22#include <algorithm>
23#include <filesystem>
24#include <locale>
25#include <map>
26#include <set>
27#include "odb_util.h"
28#include <string_utils.h>
29#include <wx/chartype.h>
30#include <wx/dir.h>
31#include <wx/regex.h>
32#include <board.h>
34#include <footprint.h>
35#include <pad.h>
36#include <pcb_track.h>
37#include "odb_defines.h"
38#include "pcb_io_odbpp.h"
39
40namespace ODB
41{
42
43int IpcViaType( const DRILL_OPERATION& aOperation, bool aTop )
44{
45 bool covered = aTop ? aOperation.m_TopCovered : aOperation.m_BottomCovered;
46 bool plugged = aTop ? aOperation.m_TopPlugged : aOperation.m_BottomPlugged;
47 bool tented = aTop ? aOperation.m_TopTented : aOperation.m_BottomTented;
48
49 // IPC-4761 type VII requires filling, so capping alone cannot select it
50 if( aOperation.m_Filled )
51 {
52 if( aOperation.m_Capped )
53 return 7;
54
55 return tented || covered ? 6 : 5;
56 }
57
58 if( plugged )
59 return tented || covered ? 4 : 3;
60
61 if( covered )
62 return 2;
63
64 return tented ? 1 : 0;
65}
66
67
68bool ViaInPad( const PCB_VIA& aVia )
69{
70 const BOARD* board = aVia.GetBoard();
71
72 if( !board )
73 return false;
74
75 std::shared_ptr<SHAPE_SEGMENT> hole = aVia.GetEffectiveHoleShape();
76
77 if( !hole )
78 return false;
79
80 BOX2I holeBox = hole->BBox();
81
82 // Copper pours do not make ordinary stitching vias via-in-pad
83 for( FOOTPRINT* footprint : board->Footprints() )
84 {
85 if( !footprint->GetBoundingBox( false ).Intersects( holeBox ) )
86 continue;
87
88 for( PAD* pad : footprint->Pads() )
89 {
90 if( pad->GetAttribute() == PAD_ATTRIB::NPTH || !pad->GetBoundingBox().Intersects( holeBox ) )
91 continue;
92
93 for( PCB_LAYER_ID layer : { F_Cu, B_Cu } )
94 {
95 if( !aVia.IsOnLayer( layer ) || !pad->IsOnLayer( layer ) )
96 continue;
97
98 std::shared_ptr<SHAPE> copper = pad->GetEffectiveShape( layer );
99
100 if( copper && copper->Collide( hole.get(), 0 ) )
101 return true;
102 }
103 }
104 }
105
106 return false;
107}
108
109
110std::vector<ODB_TYPE> OverridableLayerTypes( PCB_LAYER_ID aLayer )
111{
112 if( IsCopperLayer( aLayer ) )
114
115 // Component, drill, rout and dielectric rows carry structure a drawing layer cannot supply
118}
119
120
121wxString GenODBString( const wxString& aStr )
122{
123 wxString str;
124
125 for( size_t ii = 0; ii < aStr.Len(); ++ii )
126 {
127 // Rule: we can only use the standard ASCII, control excluded
128 wxUniChar ch = aStr[ii];
129
130 if( ch > 126 || !std::isgraph( static_cast<unsigned char>( ch ) ) )
131 ch = '?';
132
133 str += ch;
134 }
135
136 // Rule: only uppercase
137 str.MakeUpper();
138
139 return str;
140}
141
142
143static wxUniChar NextCharacter( const wxString& aStr, size_t& aIndex )
144{
145 wxUniChar character = aStr[aIndex];
146 unsigned int code = character.GetValue();
147
148 // Windows wxString stores supplementary characters as surrogate pairs
149 if( code >= 0xD800 && code <= 0xDBFF && aIndex + 1 < aStr.size() )
150 {
151 unsigned int next = aStr[aIndex + 1].GetValue();
152
153 if( next >= 0xDC00 && next <= 0xDFFF )
154 ++aIndex;
155 }
156
157 return character;
158}
159
160
161wxString GenLegalNetName( const wxString& aStr )
162{
163 wxString out;
164 out.reserve( aStr.size() );
165
166 for( size_t ii = 0; ii < aStr.size(); ++ii )
167 {
168 wxUniChar c = NextCharacter( aStr, ii );
169 unsigned int code = c.GetValue();
170
171 if( ( code >= 33 && code <= 126 ) && code != ';' )
172 {
173 out += c;
174 }
175 else
176 {
177 out += '_';
178 }
179 }
180
181 return out;
182}
183
184
185wxString GenLegalComponentName( const wxString& aStr )
186{
187 wxString out;
188 out.reserve( aStr.length() );
189
190 for( size_t ii = 0; ii < aStr.Len(); ++ii )
191 {
192 unsigned int code = NextCharacter( aStr, ii ).GetValue();
193
194 // ODB++ component names must be printable ASCII (33-126), no spaces or semicolons
195 if( code >= 33 && code <= 126 && code != ';' )
196 {
197 out.append( 1, static_cast<char>( code ) );
198 }
199 else
200 {
201 out.append( 1, '_' ); // Replace invalid characters with underscore
202 }
203 }
204
205 return out;
206}
207
208
209// The names of these ODB++ entities must comply with
210// the rules for legal entity names:
211// product, model, step, layer, symbol, and attribute.
212wxString GenLegalEntityName( const wxString& aStr )
213{
214 wxString out;
215 out.reserve( aStr.size() );
216
217 for( size_t ii = 0; ii < aStr.size(); ++ii )
218 {
219 unsigned int code = NextCharacter( aStr, ii ).GetValue();
220 char character;
221
222 if( code >= 'A' && code <= 'Z' )
223 character = static_cast<char>( code - 'A' + 'a' );
224 else if( ( code >= 'a' && code <= 'z' ) || ( code >= '0' && code <= '9' ) || code == '-' || code == '_'
225 || code == '+' || code == '.' )
226 character = static_cast<char>( code );
227 else
228 character = '_';
229
230 out.append( 1, character );
231 }
232
233 if( out.length() > 64 )
234 {
235 out.Truncate( 64 );
236 }
237
238 while( !out.IsEmpty() && ( out[0] == '.' || out[0] == '-' || out[0] == '+' ) )
239 {
240 out.erase( 0, 1 );
241 }
242
243 while( !out.IsEmpty() && out.Last() == '.' )
244 {
245 out.RemoveLast();
246 }
247
248 return out;
249}
250
251
252std::vector<wxString> UniqueNames( const std::vector<wxString>& aBases, const std::set<wxString>& aReserved,
253 size_t aMaxLength )
254{
255 std::vector<wxString> bases;
256 bases.reserve( aBases.size() );
257
258 for( const wxString& base : aBases )
259 {
260 wxCHECK_MSG( !base.IsEmpty(), aBases, wxS( "ODB++ name base must not be empty" ) );
261 bases.push_back( base.Left( aMaxLength ) );
262 }
263
264 const std::set<wxString> originalBases( bases.begin(), bases.end() );
265 std::set<wxString> used = aReserved;
266 std::vector<wxString> names;
267 names.reserve( bases.size() );
268
269 // Names are never freed, so a base resumes after the last suffix it took
270 std::map<wxString, size_t> nextSuffix;
271
272 for( const wxString& base : bases )
273 {
274 wxString legal = base;
275
276 if( used.contains( legal ) )
277 {
278 size_t& suffixNumber = nextSuffix.try_emplace( base, 2 ).first->second;
279
280 for( ; ; ++suffixNumber )
281 {
282 wxString suffix = wxString::Format( wxS( "_%zu" ), suffixNumber );
283 legal = base.Left( aMaxLength - suffix.length() ) + suffix;
284
285 if( !originalBases.contains( legal ) && !used.contains( legal ) )
286 break;
287 }
288 }
289
290 names.push_back( legal );
291 used.insert( legal );
292 }
293
294 return names;
295}
296
297
298VARIANT_NAMES VARIANT_NAMES::Build( const std::vector<wxString>& aNames )
299{
301 std::vector<wxString> bases;
302 std::vector<bool> unnamed;
303 bases.reserve( aNames.size() );
304
305 for( const wxString& name : aNames )
306 {
307 wxString base = ODB::GenLegalEntityName( name );
308 unnamed.push_back( base.IsEmpty() );
309 bases.push_back( base.IsEmpty() ? wxString( wxS( "variant" ) ) : base );
310 }
311
312 std::vector<wxString> legalNames = UniqueNames( bases, {}, 64 );
313
314 for( size_t index = 0; index < aNames.size(); ++index )
315 {
316 const wxString& name = aNames[index];
317 const wxString& base = bases[index];
318 const wxString& legal = legalNames[index];
319
320 if( unnamed[index] || legal != base )
321 result.m_renamed.push_back( name );
322
323 result.m_names.emplace_back( name, legal );
324 }
325
326 result.m_listsFit = result.Join( aNames ).length() <= 1000;
327 return result;
328}
329
330
331wxString VARIANT_NAMES::LegalName( const wxString& aName ) const
332{
333 for( const auto& [source, legal] : m_names )
334 {
335 if( source.CmpNoCase( aName ) == 0 )
336 return legal;
337 }
338
339 return ODB::GenLegalEntityName( aName );
340}
341
342
343wxString VARIANT_NAMES::SelectedName( const wxString& aName ) const
344{
345 if( !m_listsFit )
346 return wxString();
347
348 for( const auto& [source, legal] : m_names )
349 {
350 if( source.CmpNoCase( aName ) == 0 )
351 return legal;
352 }
353
354 return wxString();
355}
356
357
358wxString VARIANT_NAMES::Join( const std::vector<wxString>& aNames ) const
359{
360 wxString result;
361
362 for( const wxString& name : aNames )
363 {
364 if( !result.IsEmpty() )
365 result += ':';
366
367 result += LegalName( name );
368 }
369
370 return result;
371}
372
373
374void RemoveWhitespace( wxString& aStr )
375{
376 aStr.Trim().Trim( false );
377 wxRegEx spaces( "\\s" );
378 spaces.Replace( &aStr, "_" );
379}
380
381
382wxString Double2String( const ODB_FORMAT& aFormat, double aVal )
383{
384 return FormatTrimmedDecimal( aVal, aFormat.m_sigfig );
385}
386
387
388std::string Double2String( double aVal, int32_t aDigits )
389{
390 // We don't want to output -0.0 as this value is just 0 for fabs
391 if( aVal == -0.0 )
392 aVal = 0.0;
393
394 wxString str = wxString::FromCDouble( aVal, aDigits );
395
396 return str.ToStdString();
397}
398
399
400wxString SymDouble2String( const ODB_FORMAT& aFormat, double aVal )
401{
402 return Double2String( aFormat, aFormat.m_symbolScale * aVal );
403}
404
405
406wxString Data2String( const ODB_FORMAT& aFormat, double aVal )
407{
408 return Double2String( aFormat, aFormat.m_scale * aVal );
409}
410
411
412std::pair<wxString, wxString> AddXY( const ODB_FORMAT& aFormat, const VECTOR2I& aVec )
413{
414 return { Double2String( aFormat, aFormat.m_scale * ( aVec.x - aFormat.m_originOffset.x ) ),
415 Double2String( aFormat, -aFormat.m_scale * ( aVec.y - aFormat.m_originOffset.y ) ) };
416}
417
418
419std::pair<wxString, wxString> AddRelativeXY( const ODB_FORMAT& aFormat, const VECTOR2I& aVec )
420{
421 return { Double2String( aFormat, aFormat.m_scale * aVec.x ),
422 Double2String( aFormat, -aFormat.m_scale * aVec.y ) };
423}
424
425
427{
428 VECTOR2D pos{};
429
430 switch( aShape.GetShape() )
431 {
432 // Rectangles in KiCad are mapped by their corner while ODBPP uses the center
434 pos = aShape.GetPosition()
435 + VECTOR2I( aShape.GetRectangleWidth() / 2.0, aShape.GetRectangleHeight() / 2.0 );
436 break;
437 // Both KiCad and ODBPP use the center of the circle
438 case SHAPE_T::CIRCLE:
439 // KiCad uses the exact points on the board
440 case SHAPE_T::POLY:
441 case SHAPE_T::BEZIER:
442 case SHAPE_T::SEGMENT:
443 case SHAPE_T::ARC:
444 case SHAPE_T::ELLIPSE:
447 pos = aShape.GetPosition();
448 break;
449 }
450
451 return pos;
452}
453} // namespace ODB
454
455
456void ODB_TREE_WRITER::CreateEntityDirectory( const wxString& aPareDir,
457 const wxString& aSubDir /*= wxEmptyString*/ )
458{
459 wxFileName path = wxFileName::DirName( aPareDir );
460
461 wxArrayString subDirs = wxFileName::DirName( aSubDir.Lower() ).GetDirs();
462
463 for( size_t i = 0; i < subDirs.GetCount(); i++ )
464 path.AppendDir( subDirs[i] );
465
466 if( !path.DirExists() )
467 {
468 if( !path.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
469 {
470 throw( std::runtime_error( "Could not create directory" + path.GetPath() ) );
471 }
472 }
473
474 m_currentPath = path.GetPath();
475}
476
477
478ODB_FILE_WRITER::ODB_FILE_WRITER( ODB_TREE_WRITER& aTreeWriter, const wxString& aFileName ) :
479 m_treeWriter( aTreeWriter )
480{
481 CreateODBFile( aFileName );
482}
483
484
485void ODB_FILE_WRITER::CreateODBFile( const wxString& aFileName )
486{
487 if( aFileName.IsEmpty() || m_treeWriter.GetCurrentPath().IsEmpty() )
488 return;
489
490 wxFileName fn;
491 fn.SetPath( m_treeWriter.GetCurrentPath() );
492 fn.SetFullName( aFileName );
493
494 wxString dirPath = fn.GetPath();
495
496 if( !wxDir::Exists( dirPath ) )
497 {
498 if( !wxDir::Make( dirPath ) )
499 throw( std::runtime_error( "Could not create directory" + dirPath ) );
500 }
501
502 if( !fn.IsDirWritable() || ( fn.Exists() && !fn.IsFileWritable() ) )
503 return;
504
505 if( m_ostream.is_open() )
506 m_ostream.close();
507
508 // Windows narrow stream paths use the process code page, which may not be UTF-8
509#ifdef __WXMSW__
510 m_ostream.open( std::filesystem::path( std::wstring( fn.GetFullPath().wc_str() ) ),
511 std::ios_base::out | std::ios_base::trunc | std::ios_base::binary );
512#else
513 m_ostream.open( TO_UTF8( fn.GetFullPath() ),
514 std::ios_base::out | std::ios_base::trunc | std::ios_base::binary );
515#endif
516
517 m_ostream.imbue( std::locale::classic() );
518
519 if( !m_ostream.is_open() || !m_ostream.good() )
520 throw std::runtime_error( "Failed to open file: " + fn.GetFullPath() );
521}
522
523
525{
526 if( m_ostream.is_open() )
527 {
528 m_ostream.close();
529
530 if( !m_ostream.good() )
531 throw std::runtime_error( "close file failed" );
532 }
533
534 return true;
535}
536
537
538void ODB_TEXT_WRITER::WriteEquationLine( const std::string& var, int value )
539{
540 WriteIndent();
541 m_ostream << var << "=" << value << std::endl;
542}
543
544
545void ODB_TEXT_WRITER::WriteEquationLine( const wxString& var, const wxString& value )
546{
547 WriteIndent();
548 m_ostream << var << "=" << value << std::endl;
549}
550
551
553{
554 if( in_array )
555 m_ostream << " ";
556}
557
558
559void ODB_TEXT_WRITER::BeginArray( const std::string& a )
560{
561 if( in_array )
562 throw std::runtime_error( "already in array" );
563 in_array = true;
564 m_ostream << a << " {" << std::endl;
565}
566
567
569{
570 if( !in_array )
571 throw std::runtime_error( "not in array" );
572 in_array = false;
573 m_ostream << "}" << std::endl << std::endl;
574}
575
576
577ODB_TEXT_WRITER::ARRAY_PROXY::ARRAY_PROXY( ODB_TEXT_WRITER& aWriter, const std::string& aStr ) :
578 m_writer( aWriter )
579{
580 m_writer.BeginArray( aStr );
581}
582
583
588
589
590ODB_DRILL_TOOLS::ODB_DRILL_TOOLS( const wxString& aUnits, const wxString& aThickness,
591 const wxString& aUserParams ) :
592 m_units( aUnits ), m_thickness( aThickness ), m_userParams( aUserParams )
593{
594}
595
596
597void ODB_DRILL_TOOLS::AddDrillTool( const ODB_FORMAT& aFormat, const wxString& aType, int aDiameter,
598 const wxString& aType2 )
599{
600 // ODB++ tools use microns or mils, the same scale as symbol sizes
601 wxString size = ODB::SymDouble2String( aFormat, aDiameter );
602
603 // NUM names a physical drill bit, so a size that recurs across holes reuses its tool
604 for( const TOOLS& existing : m_tools )
605 {
606 if( existing.m_type == aType && existing.m_type2 == aType2
607 && existing.m_drillSize == size )
608 {
609 return;
610 }
611 }
612
613 TOOLS tool;
614 tool.m_num = m_tools.size() + 1;
615 tool.m_type = aType;
616 tool.m_type2 = aType2;
617 tool.m_finishSize = size;
618 tool.m_drillSize = size;
619
620 m_tools.push_back( tool );
621}
622
623
624void ODB_DRILL_TOOLS::GenerateFile( std::ostream& aStream )
625{
626 ODB_TEXT_WRITER twriter( aStream );
627
628 twriter.WriteEquationLine( "UNITS", m_units );
629 twriter.WriteEquationLine( "THICKNESS", m_thickness );
630 twriter.WriteEquationLine( "USER_PARAMS", m_userParams );
631
632 for( const auto& tool : m_tools )
633 {
634 const auto array_proxy = twriter.MakeArrayProxy( "TOOLS" );
635 twriter.WriteEquationLine( "NUM", tool.m_num );
636 twriter.WriteEquationLine( "TYPE", tool.m_type );
637 twriter.WriteEquationLine( "TYPE2", tool.m_type2 );
638 twriter.WriteEquationLine( "MIN_TOL", tool.m_minTol );
639 twriter.WriteEquationLine( "MAX_TOL", tool.m_maxTol );
640 twriter.WriteEquationLine( "BIT", tool.m_bit );
641 twriter.WriteEquationLine( "FINISH_SIZE", tool.m_finishSize );
642 twriter.WriteEquationLine( "DRILL_SIZE", tool.m_drillSize );
643 }
644}
int index
const char * name
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
const FOOTPRINTS & Footprints() const
Definition board.h:464
int GetRectangleWidth() const
SHAPE_T GetShape() const
Definition eda_shape.h:175
int GetRectangleHeight() const
wxString m_units
Definition odb_util.h:455
void GenerateFile(std::ostream &aStream)
Definition odb_util.cpp:624
ODB_DRILL_TOOLS(const wxString &aUnits, const wxString &aThickness="0", const wxString &aUserParams=wxEmptyString)
Definition odb_util.cpp:590
wxString m_thickness
Definition odb_util.h:456
wxString m_userParams
Definition odb_util.h:457
void AddDrillTool(const ODB_FORMAT &aFormat, const wxString &aType, int aDiameter, const wxString &aType2=wxT("STANDARD"))
Record the drill bit used for a hole.
Definition odb_util.cpp:597
std::vector< ODB_DRILL_TOOLS::TOOLS > m_tools
Definition odb_util.h:458
ODB_TREE_WRITER & m_treeWriter
Definition odb_util.h:331
ODB_FILE_WRITER(ODB_TREE_WRITER &aTreeWriter, const wxString &aFileName)
Definition odb_util.cpp:478
void CreateODBFile(const wxString &aFileName)
Definition odb_util.cpp:485
std::ofstream m_ostream
Definition odb_util.h:332
ARRAY_PROXY(ODB_TEXT_WRITER &aWriter, const std::string &aStr)
Definition odb_util.cpp:577
ODB_TEXT_WRITER & m_writer
Definition odb_util.h:395
void WriteEquationLine(const std::string &var, int value)
Definition odb_util.cpp:538
ARRAY_PROXY MakeArrayProxy(const std::string &aStr)
Definition odb_util.h:406
std::ostream & m_ostream
Definition odb_util.h:418
void BeginArray(const std::string &a)
Definition odb_util.cpp:559
void CreateEntityDirectory(const wxString &aPareDir, const wxString &aSubDir=wxEmptyString)
Definition odb_util.cpp:456
wxString m_currentPath
Definition odb_util.h:365
Definition pad.h:61
VECTOR2I GetPosition() const override
Definition pcb_shape.h:76
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, DRC_CONSTRAINT_T aUsage=NULL_CONSTRAINT) const override
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
@ UNDEFINED
Definition eda_shape.h:55
@ ELLIPSE
Definition eda_shape.h:62
@ SEGMENT
Definition eda_shape.h:56
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
@ ELLIPSE_ARC
Definition eda_shape.h:63
std::string source
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition idf_helpers.h:37
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:703
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ B_Cu
Definition layer_ids.h:61
@ F_Cu
Definition layer_ids.h:60
wxString GenLegalComponentName(const wxString &aStr)
Definition odb_util.cpp:185
std::vector< ODB_TYPE > OverridableLayerTypes(PCB_LAYER_ID aLayer)
Matrix types a layer override may assign to aLayer.
Definition odb_util.cpp:110
std::pair< wxString, wxString > AddXY(const ODB_FORMAT &aFormat, const VECTOR2I &aVec)
Definition odb_util.cpp:412
void RemoveWhitespace(wxString &aStr)
Definition odb_util.cpp:374
std::vector< wxString > UniqueNames(const std::vector< wxString > &aBases, const std::set< wxString > &aReserved, size_t aMaxLength)
Definition odb_util.cpp:252
wxString GenODBString(const wxString &aStr)
Definition odb_util.cpp:121
wxString Data2String(const ODB_FORMAT &aFormat, double aVal)
Definition odb_util.cpp:406
int IpcViaType(const DRILL_OPERATION &aOperation, bool aTop)
Definition odb_util.cpp:43
std::pair< wxString, wxString > AddRelativeXY(const ODB_FORMAT &aFormat, const VECTOR2I &aVec)
Definition odb_util.cpp:419
wxString GenLegalEntityName(const wxString &aStr)
Definition odb_util.cpp:212
wxString GenLegalNetName(const wxString &aStr)
Definition odb_util.cpp:161
bool ViaInPad(const PCB_VIA &aVia)
Definition odb_util.cpp:68
static wxUniChar NextCharacter(const wxString &aStr, size_t &aIndex)
Definition odb_util.cpp:143
VECTOR2I GetShapePosition(const PCB_SHAPE &aShape)
Definition odb_util.cpp:426
wxString SymDouble2String(const ODB_FORMAT &aFormat, double aVal)
Definition odb_util.cpp:400
wxString Double2String(const ODB_FORMAT &aFormat, double aVal)
Definition odb_util.cpp:382
@ CONDUCTIVE_PASTE
Definition odb_util.h:120
@ POWER_GROUND
Definition odb_util.h:109
@ SOLDER_PASTE
Definition odb_util.h:113
@ SILK_SCREEN
Definition odb_util.h:114
@ SOLDER_MASK
Definition odb_util.h:112
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
CITER next(CITER it)
Definition ptree.cpp:120
wxString FormatTrimmedDecimal(double aValue, int aPrecision)
Format a value to aPrecision decimal digits and strip trailing zeros, keeping one digit after the dec...
One machining action, which is also one NC hit and one tool assignment.
std::vector< std::pair< wxString, wxString > > m_names
Definition odb_util.h:42
wxString Join(const std::vector< wxString > &aNames) const
Definition odb_util.cpp:358
wxString LegalName(const wxString &aName) const
Definition odb_util.cpp:331
wxString SelectedName(const wxString &aName) const
Definition odb_util.cpp:343
static VARIANT_NAMES Build(const std::vector< wxString > &aNames)
Definition odb_util.cpp:298
double m_symbolScale
Definition odb_util.h:71
VECTOR2I m_originOffset
Definition odb_util.h:77
double m_scale
Definition odb_util.h:68
int m_sigfig
Definition odb_util.h:72
std::string path
wxString result
Test unit parsing edge cases and error handling.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707