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, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <list>
23#include <unordered_set>
24
25#include <wx/arrstr.h>
26#include <wx/dir.h>
27#include <wx/filename.h>
28
29#include <filename_resolver.h>
30#include <gestfich.h>
31#include <pgm_base.h>
33
34
36{
37
38namespace
39{
40
43const std::vector<wxString>& stepExtensions()
44{
45 static const std::vector<wxString> exts = {
46 wxS( "step" ), wxS( "stp" ), wxS( "stpz" ),
47 wxS( "step.gz" ), wxS( "stp.gz" ),
48 wxS( "iges" ), wxS( "igs" )
49 };
50
51 return exts;
52}
53
54
57const std::vector<wxString>& dottedStepExtensions()
58{
59 static const std::vector<wxString> dotted = []
60 {
61 std::vector<wxString> result;
62 result.reserve( stepExtensions().size() );
63
64 for( const wxString& ext : stepExtensions() )
65 result.push_back( wxS( "." ) + ext );
66
67 return result;
68 }();
69
70 return dotted;
71}
72
73
74bool hasStepExtension( const wxString& aPath )
75{
76 const wxString lower = aPath.Lower();
77
78 for( const wxString& dotted : dottedStepExtensions() )
79 {
80 if( lower.length() > dotted.length()
81 && lower.Right( dotted.length() ) == dotted )
82 {
83 return true;
84 }
85 }
86
87 return false;
88}
89
90
94wxString normalizeStem( const wxString& aName )
95{
96 wxString stem = aName;
97
98 static const wxString stripList[] = {
99 wxS( ".step.gz" ), wxS( ".stp.gz" ),
100 wxS( ".wrl" ), wxS( ".wrz" ),
101 wxS( ".step" ), wxS( ".stp" ), wxS( ".stpz" ),
102 wxS( ".iges" ), wxS( ".igs" )
103 };
104
105 for( const wxString& ext : stripList )
106 {
107 if( stem.length() > ext.length()
108 && stem.Right( ext.length() ).Lower() == ext )
109 {
110 stem = stem.Left( stem.length() - ext.length() );
111 break;
112 }
113 }
114
115 stem.MakeLower();
116
117 stem.Replace( wxS( "-" ), wxS( "_" ) );
118 stem.Replace( wxS( " " ), wxS( "_" ) );
119
120 return stem;
121}
122
123
124wxString parentDirName( const wxString& aPath )
125{
126 wxFileName fn( aPath );
127 const wxArrayString& dirs = fn.GetDirs();
128
129 return dirs.empty() ? wxString() : dirs.Last();
130}
131
132
136std::vector<wxString> gatherScanDirs( const wxString& aProjectPath,
137 const FILENAME_RESOLVER* aResolver )
138{
139 std::vector<wxString> result;
140 std::unordered_set<wxString> seen;
141
142 auto addDir = [&]( const wxString& aDir )
143 {
144 if( aDir.IsEmpty() )
145 return;
146
147 wxFileName norm( aDir, wxEmptyString );
148 norm.Normalize( wxPATH_NORM_ABSOLUTE | wxPATH_NORM_DOTS );
149 const wxString key = norm.GetPath().Lower();
150
151 if( !seen.insert( key ).second )
152 return;
153
154 if( wxDir::Exists( norm.GetPath() ) )
155 result.push_back( norm.GetPath() );
156 };
157
158 if( aResolver )
159 {
160 if( const std::list<SEARCH_PATH>* paths = aResolver->GetPaths() )
161 {
162 for( const SEARCH_PATH& sp : *paths )
163 addDir( sp.m_Pathexp );
164 }
165 }
166
167 if( !aProjectPath.IsEmpty() )
168 {
169 wxFileName prj3D( aProjectPath, wxEmptyString );
170 prj3D.AppendDir( wxS( "3dshapes" ) );
171 addDir( prj3D.GetPath() );
172 }
173
174 if( COMMON_SETTINGS* common = Pgm().GetCommonSettings() )
175 {
176 for( const wxString& dir : common->m_Extra3DSearchDirs )
177 addDir( dir );
178 }
179
180 return result;
181}
182
183} // namespace
184
185
186bool IsWrlExtension( const wxString& aFilename )
187{
188 const wxString ext = aFilename.AfterLast( '.' ).Lower();
189
190 return ext == wxS( "wrl" ) || ext == wxS( "wrz" );
191}
192
193
194void STEP_CATALOG::Build( const wxString& aProjectPath, const FILENAME_RESOLVER* aResolver )
195{
196 m_byStem.clear();
197
198 for( const wxString& dir : gatherScanDirs( aProjectPath, aResolver ) )
199 {
200 wxArrayString files;
201 ::CollectFilesLoopSafe( dir, files, wxEmptyString, wxDIR_FILES | wxDIR_DIRS );
202
203 for( const wxString& file : files )
204 {
205 if( !hasStepExtension( file ) )
206 continue;
207
208 const wxString stem = normalizeStem( wxFileName( file ).GetFullName() );
209
210 if( stem.IsEmpty() )
211 continue;
212
213 m_byStem[stem].push_back( file );
214 }
215 }
216}
217
218
219wxString STEP_CATALOG::FindMatchFor( const wxString& aMissingWrl ) const
220{
221 if( !IsWrlExtension( aMissingWrl ) )
222 return wxEmptyString;
223
224 const wxString stem = normalizeStem( wxFileName( aMissingWrl ).GetFullName() );
225
226 if( stem.IsEmpty() )
227 return wxEmptyString;
228
229 auto it = m_byStem.find( stem );
230
231 if( it == m_byStem.end() || it->second.empty() )
232 return wxEmptyString;
233
234 const wxString wrlParent = parentDirName( aMissingWrl );
235
236 if( !wrlParent.IsEmpty() )
237 {
238 for( const wxString& candidate : it->second )
239 {
240 if( parentDirName( candidate ).CmpNoCase( wrlParent ) == 0 )
241 return candidate;
242 }
243 }
244
245 return it->second.front();
246}
247
248} // 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.
void CollectFilesLoopSafe(const wxString &aRoot, wxArrayString &aFiles, const wxString &aFileSpec, int aFlags)
Recursively collect every file under aRoot, deduplicating subdirectories by their resolved path.
Definition gestfich.cpp:817
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.