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