KiCad PCB EDA Suite
Loading...
Searching...
No Matches
model_substitution_helpers.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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
24
25#include <list>
26#include <unordered_set>
27
28#include <wx/arrstr.h>
29#include <wx/dir.h>
30#include <wx/filename.h>
31
32#include <filename_resolver.h>
33#include <pgm_base.h>
35
36
38{
39
40namespace
41{
42
45const std::vector<wxString>& stepExtensions()
46{
47 static const std::vector<wxString> exts = {
48 wxS( "step" ), wxS( "stp" ), wxS( "stpz" ),
49 wxS( "step.gz" ), wxS( "stp.gz" ),
50 wxS( "iges" ), wxS( "igs" )
51 };
52
53 return exts;
54}
55
56
59const std::vector<wxString>& dottedStepExtensions()
60{
61 static const std::vector<wxString> dotted = []
62 {
63 std::vector<wxString> result;
64 result.reserve( stepExtensions().size() );
65
66 for( const wxString& ext : stepExtensions() )
67 result.push_back( wxS( "." ) + ext );
68
69 return result;
70 }();
71
72 return dotted;
73}
74
75
76bool hasStepExtension( const wxString& aPath )
77{
78 const wxString lower = aPath.Lower();
79
80 for( const wxString& dotted : dottedStepExtensions() )
81 {
82 if( lower.length() > dotted.length()
83 && lower.Right( dotted.length() ) == dotted )
84 {
85 return true;
86 }
87 }
88
89 return false;
90}
91
92
96wxString normalizeStem( const wxString& aName )
97{
98 wxString stem = aName;
99
100 static const wxString stripList[] = {
101 wxS( ".step.gz" ), wxS( ".stp.gz" ),
102 wxS( ".wrl" ), wxS( ".wrz" ),
103 wxS( ".step" ), wxS( ".stp" ), wxS( ".stpz" ),
104 wxS( ".iges" ), wxS( ".igs" )
105 };
106
107 for( const wxString& ext : stripList )
108 {
109 if( stem.length() > ext.length()
110 && stem.Right( ext.length() ).Lower() == ext )
111 {
112 stem = stem.Left( stem.length() - ext.length() );
113 break;
114 }
115 }
116
117 stem.MakeLower();
118
119 stem.Replace( wxS( "-" ), wxS( "_" ) );
120 stem.Replace( wxS( " " ), wxS( "_" ) );
121
122 return stem;
123}
124
125
126wxString parentDirName( const wxString& aPath )
127{
128 wxFileName fn( aPath );
129 const wxArrayString& dirs = fn.GetDirs();
130
131 return dirs.empty() ? wxString() : dirs.Last();
132}
133
134
138std::vector<wxString> gatherScanDirs( const wxString& aProjectPath,
139 const FILENAME_RESOLVER* aResolver )
140{
141 std::vector<wxString> result;
142 std::unordered_set<wxString> seen;
143
144 auto addDir = [&]( const wxString& aDir )
145 {
146 if( aDir.IsEmpty() )
147 return;
148
149 wxFileName norm( aDir, wxEmptyString );
150 norm.Normalize( wxPATH_NORM_ABSOLUTE | wxPATH_NORM_DOTS );
151 const wxString key = norm.GetPath().Lower();
152
153 if( !seen.insert( key ).second )
154 return;
155
156 if( wxDir::Exists( norm.GetPath() ) )
157 result.push_back( norm.GetPath() );
158 };
159
160 if( aResolver )
161 {
162 if( const std::list<SEARCH_PATH>* paths = aResolver->GetPaths() )
163 {
164 for( const SEARCH_PATH& sp : *paths )
165 addDir( sp.m_Pathexp );
166 }
167 }
168
169 if( !aProjectPath.IsEmpty() )
170 {
171 wxFileName prj3D( aProjectPath, wxEmptyString );
172 prj3D.AppendDir( wxS( "3dshapes" ) );
173 addDir( prj3D.GetPath() );
174 }
175
176 if( COMMON_SETTINGS* common = Pgm().GetCommonSettings() )
177 {
178 for( const wxString& dir : common->m_Extra3DSearchDirs )
179 addDir( dir );
180 }
181
182 return result;
183}
184
185} // namespace
186
187
188bool IsWrlExtension( const wxString& aFilename )
189{
190 const wxString ext = aFilename.AfterLast( '.' ).Lower();
191
192 return ext == wxS( "wrl" ) || ext == wxS( "wrz" );
193}
194
195
196void STEP_CATALOG::Build( const wxString& aProjectPath, const FILENAME_RESOLVER* aResolver )
197{
198 m_byStem.clear();
199
200 for( const wxString& dir : gatherScanDirs( aProjectPath, aResolver ) )
201 {
202 wxArrayString files;
203 wxDir::GetAllFiles( dir, &files, wxEmptyString, wxDIR_FILES | wxDIR_DIRS );
204
205 for( const wxString& file : files )
206 {
207 if( !hasStepExtension( file ) )
208 continue;
209
210 const wxString stem = normalizeStem( wxFileName( file ).GetFullName() );
211
212 if( stem.IsEmpty() )
213 continue;
214
215 m_byStem[stem].push_back( file );
216 }
217 }
218}
219
220
221wxString STEP_CATALOG::FindMatchFor( const wxString& aMissingWrl ) const
222{
223 if( !IsWrlExtension( aMissingWrl ) )
224 return wxEmptyString;
225
226 const wxString stem = normalizeStem( wxFileName( aMissingWrl ).GetFullName() );
227
228 if( stem.IsEmpty() )
229 return wxEmptyString;
230
231 auto it = m_byStem.find( stem );
232
233 if( it == m_byStem.end() || it->second.empty() )
234 return wxEmptyString;
235
236 const wxString wrlParent = parentDirName( aMissingWrl );
237
238 if( !wrlParent.IsEmpty() )
239 {
240 for( const wxString& candidate : it->second )
241 {
242 if( parentDirName( candidate ).CmpNoCase( wrlParent ) == 0 )
243 return candidate;
244 }
245 }
246
247 return it->second.front();
248}
249
250} // namespace MODEL_SUBSTITUTION
Provide an extensible class to resolve 3D model paths.
const std::list< SEARCH_PATH > * GetPaths() const
Return a pointer to the internal path list; the items in:load.
wxString FindMatchFor(const wxString &aMissingWrl) const
Look up the best STEP-family replacement for a missing WRL reference.
void Build(const wxString &aProjectPath, const FILENAME_RESOLVER *aResolver)
Walk the resolver's search paths, the project's 3dshapes/ subdirectory, and the user's COMMON_SETTING...
std::unordered_map< wxString, std::vector< wxString > > m_byStem
Normalised stem → list of absolute STEP-family paths sharing that stem.
Shared helpers for matching obsolete .wrl/.wrz 3D model references against current STEP-family replac...
bool IsWrlExtension(const wxString &aFilename)
True iff aFilename ends in .wrl or .wrz (case-insensitive).
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
wxString result
Test unit parsing edge cases and error handling.