KiCad PCB EDA Suite
Loading...
Searching...
No Matches
altium_parser_utils.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 (C) 2021 Thomas Pointhuber <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU 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 "altium_parser_utils.h"
22
23#include <string_utils.h>
24#include <lib_id.h>
25#include <trigo.h>
26#include <math/util.h>
27#include <wx/arrstr.h>
28
29LIB_ID AltiumToKiCadLibID( const wxString& aLibName, const wxString& aLibReference )
30{
31 wxString libName = LIB_ID::FixIllegalChars( aLibName, true );
32 wxString libReference = EscapeString( aLibReference, CTX_LIBID );
33
34 wxString key = !libName.empty() ? ( libName + ":" + libReference ) : libReference;
35
36 LIB_ID libId;
37 libId.Parse( key, true );
38
39 return libId;
40}
41
42
43wxString AltiumPropertyToKiCadString( const wxString& aString )
44{
45 wxString output;
46 wxString tempString;
47 bool hasPrev = false;
48 wxUniChar prev;
49
50 for( wxString::const_iterator it = aString.begin(); it != aString.end(); ++it )
51 {
52 char ch = 0;
53
54 if( (*it).GetAsChar( &ch ) )
55 {
56 if( ch == '\\' )
57 {
58 if( hasPrev )
59 {
60 tempString += prev;
61 hasPrev = false;
62 }
63
64 continue; // Backslash is ignored and not added to the output
65 }
66 }
67
68 if( hasPrev ) // Two letters in a row with no backslash
69 {
70 if( !tempString.empty() )
71 {
72 output += "~{" + tempString + "}";
73 tempString.Clear();
74 }
75
76 output += prev;
77 }
78
79 prev = *it;
80 hasPrev = true;
81 }
82
83 // Append any leftover escaped string
84 if( !tempString.IsEmpty() )
85 output += "~{" + tempString + "}";
86
87 if( hasPrev )
88 output += prev;
89
90 return output;
91}
92
93
94// https://www.altium.com/documentation/altium-designer/sch-obj-textstringtext-string-ad#!special-strings
95wxString AltiumSchSpecialStringsToKiCadVariables( const wxString& aString,
96 const std::map<wxString, wxString>& aOverrides )
97{
98 if( aString.IsEmpty() || aString.at( 0 ) != '=' )
99 {
100 return aString;
101 }
102
103 wxString result;
104
105 size_t start = 1;
106 size_t delimiter = 0;
107 size_t escaping_start = 0;
108 do
109 {
110 delimiter = aString.find( "+", start );
111 escaping_start = aString.find( "'", start );
112
113 if( escaping_start < delimiter )
114 {
115 size_t text_start = escaping_start + 1;
116 size_t escaping_end = aString.find( "'", text_start );
117
118 if( escaping_end == wxString::npos )
119 {
120 escaping_end = aString.size();
121 }
122
123 result += aString.substr( text_start, escaping_end - text_start );
124
125 start = escaping_end + 1;
126 }
127 else
128 {
129 wxString specialString =
130 aString.substr( start, delimiter - start ).Trim().Trim( false );
131
132 if( specialString.StartsWith( "\"" ) && specialString.EndsWith( "\"" ) )
133 specialString = specialString.Mid( 1, specialString.Length() - 2 );
134
135 if( !specialString.IsEmpty() )
136 {
137 // Note: Altium variable references are case-insensitive. KiCad matches
138 // case-sensitive OR to all-upper-case, so make the references all-upper-case.
139 specialString.UpperCase();
140
141 auto overrideIt = aOverrides.find( specialString );
142
143 if( overrideIt != aOverrides.end() )
144 specialString = overrideIt->second;
145
146 result += wxString::Format( wxT( "${%s}" ), specialString );
147 }
148
149 start = delimiter + 1;
150 }
151 } while( delimiter != wxString::npos );
152
153 return result;
154}
155
156
157// https://www.altium.com/documentation/altium-designer/text-objects-pcb
158wxString AltiumPcbSpecialStringsToKiCadStrings( const wxString& aString,
159 const std::map<wxString, wxString>& aOverrides )
160{
161 if( aString.IsEmpty() )
162 return aString;
163
164 // Convert a 'special string' to a KiCad variable, substituting any override.
165 const auto getVariable = [&]( const wxString& aSpecialString )
166 {
167 wxString str = aSpecialString;
168 str.UpperCase(); // matching is implemented using upper case strings
169
170 auto it = aOverrides.find( str );
171 if( it != aOverrides.end() )
172 str = it->second;
173
174 return wxString::Format( wxT( "${%s}" ), str );
175 };
176
177 // special case: string starts with dot -> whole string is special string
178 if( aString.at( 0 ) == '.' )
179 {
180 wxString specialString = aString.substr( 1 );
181 return getVariable( specialString );
182 }
183
184 // Strings can also have one or more special strings using apostrophes to
185 // delineate them, e.g. "foo '.bar' '.baz' = '.qux' quux"
186
187 // In the common case, the string is a simple string with no special strings,
188 // so bail out early.
189 if( !aString.Contains( "'." ) )
190 {
191 return aString;
192 }
193
194 wxString stringCopy = aString;
195
196 // Given a position of a dot, check if it is a special string variable
197 // and replace it with a variable name if defined.
198 const auto tryReplacement = [&]( wxString& aStr, unsigned aDotPos )
199 {
200 // Check that the dot has an apostrophe before it, if not, it's just a dot
201 if( aDotPos == 0 || aStr.at( aDotPos - 1 ) != '\'' )
202 return;
203
204 // Scan forward for the next apostrophe
205 size_t apostrophePos = aStr.find( '\'', aDotPos + 1 );
206
207 // Didn't find it
208 if( apostrophePos == wxString::npos )
209 return;
210
211 // Extract the special string
212 wxString specialString = aStr.substr( aDotPos + 1, apostrophePos - aDotPos - 1 );
213 wxString replacement = getVariable( specialString );
214
215 aStr.replace( aDotPos - 1, apostrophePos - aDotPos + 2, replacement );
216 };
217
218 // Work backwards through the string checking any dots
219 // (so we don't mess up the positions of the dots as we replace them)
220 for( size_t pos = stringCopy.size() - 1; pos > 0; --pos )
221 {
222 if( stringCopy[pos] == '.' )
223 {
224 tryReplacement( stringCopy, pos );
225 }
226 }
227
228 return stringCopy;
229}
230
231
232wxString AltiumPinNamesToKiCad( wxString& aString )
233{
234 if( aString.IsEmpty() )
235 return wxEmptyString;
236
237 wxString rest;
238
239 if( aString.StartsWith( '\\', &rest ) && !rest.Contains( '\\' ) )
240 return "~{" + rest + "}";
241
242 return AltiumPropertyToKiCadString( aString );
243}
244
245
246wxString AltiumPinDesignatorToKiCad( const wxString& aDesignator )
247{
248 wxString designator = aDesignator;
249 designator.Trim( true ).Trim( false );
250
251 if( !designator.Contains( wxT( "," ) ) )
252 return designator;
253
254 // Rebuild as KiCad stacked notation "[a,b,c]". If trimming collapses the list to a
255 // single token, emit the bare token so well-formed single designators never get wrapped.
256 wxArrayString cleaned;
257
258 for( wxString token : wxSplit( designator, ',', '\0' ) )
259 {
260 token.Trim( true ).Trim( false );
261
262 if( !token.IsEmpty() )
263 cleaned.Add( token );
264 }
265
266 if( cleaned.IsEmpty() )
267 return designator;
268
269 if( cleaned.GetCount() == 1 )
270 return cleaned[0];
271
272 return wxT( "[" ) + wxJoin( cleaned, ',', '\0' ) + wxT( "]" );
273}
274
275
276VECTOR2I AltiumGetEllipticalPos( double aMajor, double aMinor, double aAngleRadians )
277{
278 if( aMajor == 0 || aMinor == 0 )
279 return VECTOR2I( 0, 0 );
280
281 double numerator = aMajor * aMinor;
282 double majorTerm = aMajor * sin( aAngleRadians );
283 double minorTerm = aMinor * cos( aAngleRadians );
284 double denominator = sqrt( majorTerm * majorTerm + minorTerm * minorTerm );
285
286 double radius = numerator / denominator;
287
288 VECTOR2I retval( KiROUND( radius * cos( aAngleRadians ) ),
289 KiROUND( radius * sin( aAngleRadians ) ) );
290
291 return retval;
292
293}
wxString AltiumPinDesignatorToKiCad(const wxString &aDesignator)
Convert an Altium pin designator string to the equivalent KiCad pin number.
wxString AltiumSchSpecialStringsToKiCadVariables(const wxString &aString, const std::map< wxString, wxString > &aOverrides)
wxString AltiumPropertyToKiCadString(const wxString &aString)
wxString AltiumPinNamesToKiCad(wxString &aString)
VECTOR2I AltiumGetEllipticalPos(double aMajor, double aMinor, double aAngleRadians)
LIB_ID AltiumToKiCadLibID(const wxString &aLibName, const wxString &aLibReference)
wxString AltiumPcbSpecialStringsToKiCadStrings(const wxString &aString, const std::map< wxString, wxString > &aOverrides)
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:48
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib)
Replace illegal LIB_ID item name characters with underscores '_'.
Definition lib_id.cpp:188
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_LIBID
nlohmann::json output
int radius
wxString result
Test unit parsing edge cases and error handling.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683