mirror of
https://github.com/leozide/leocad.git
synced 2026-10-27 06:20:55 +00:00
Fix decorated part classification and cache parent IDs
Detect decorations from filenames and description keywords so obsolete parts with numeric IDs and descriptions starting with ~ are hidden when Show Decorated Pieces is disabled. Keep decoration flags separate from the optional plain parent ID, preserving classification when no parent exists and leaving standalone stickers outside the current heuristic. Resolve single-reference aliases across official and unofficial sources, reject cycles, and use resolved parent IDs for category grouping. Cache classification with descriptions and rebuild dependent alias metadata when directory sources change. Keep local classification and cache restoration in lcPieceData; the library handles source reads and registry resolution. Validated with a warning-free Qt 6.10 Debug build, 58 lifecycle checks, and offscreen filter checks for 81 obsolete numeric patterned parts on cold and cached loads. Fixes #538
This commit is contained in:
1 parent
8a0b082f77
commit
e2cb91f6f4
6 files changed
+402
-112
No files matched your search
+118
-2
@@ -2,7 +2,6 @@
|
||||
#include "lc_math.h"
|
||||
#include "lc_mesh.h"
|
||||
#include "lc_colors.h"
|
||||
#include "lc_texture.h"
|
||||
#include "lc_piecedata.h"
|
||||
#include "lc_string.h"
|
||||
#include "lc_library.h"
|
||||
@@ -12,7 +11,6 @@
|
||||
#include "lc_scene.h"
|
||||
#include "lc_synth.h"
|
||||
#include "lc_traintrack.h"
|
||||
#include <locale.h>
|
||||
|
||||
lcPieceData::lcPieceData(bool IsLibraryPiece)
|
||||
: mIsLibraryPiece(IsLibraryPiece)
|
||||
@@ -27,6 +25,124 @@ lcPieceData::lcPieceData(bool IsLibraryPiece)
|
||||
m_strDescription[0] = 0;
|
||||
}
|
||||
|
||||
bool lcPieceData::HasDecorationDescription(const char* Description)
|
||||
{
|
||||
// Assemblies and intrinsically printed parts may have no pattern suffix.
|
||||
std::string PreviousWord;
|
||||
bool With = false;
|
||||
|
||||
for (const char* Position = Description; *Position;)
|
||||
{
|
||||
std::string Word;
|
||||
|
||||
while ((*Position >= 'A' && *Position <= 'Z') || (*Position >= 'a' && *Position <= 'z'))
|
||||
{
|
||||
char Character = *Position++;
|
||||
|
||||
if (Character >= 'a' && Character <= 'z')
|
||||
Character += 'A' - 'a';
|
||||
|
||||
Word.push_back(Character);
|
||||
}
|
||||
|
||||
if (Word.empty())
|
||||
{
|
||||
Position++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PreviousWord != "WITHOUT" && (Word == "PATTERN" || Word == "PATTERNS" || Word == "PATTERNED" || (With && (Word == "STICKER" || Word == "STICKERS"))))
|
||||
return true;
|
||||
|
||||
if (Word == "WITH")
|
||||
With = true;
|
||||
else if (Word == "WITHOUT")
|
||||
With = false;
|
||||
|
||||
PreviousWord = std::move(Word);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> lcPieceData::Classify(std::string_view Name)
|
||||
{
|
||||
mPatternParentId.clear();
|
||||
mFlags = Flags();
|
||||
|
||||
if (!IsLibraryPiece())
|
||||
return std::vector<std::string>();
|
||||
|
||||
if (m_strDescription[0] == '=')
|
||||
mFlags |= Flag::Alias;
|
||||
else if (m_strDescription[0] == '~')
|
||||
mFlags |= Flag::SubPiece;
|
||||
|
||||
if (HasDecorationDescription(m_strDescription))
|
||||
mFlags |= Flag::Decorated;
|
||||
|
||||
if (IsAlias())
|
||||
return std::vector<std::string>();
|
||||
|
||||
if (Name.size() <= 4 || Name.compare(Name.size() - 4, 4, ".DAT") != 0)
|
||||
return std::vector<std::string>();
|
||||
|
||||
const std::string_view Stem = Name.substr(0, Name.size() - 4);
|
||||
|
||||
const size_t NumberStart = Stem[0] == 'U' || Stem[0] == 'T' || Stem[0] == 'S' ? 1 : 0;
|
||||
const size_t NumberEnd = Stem.find_first_not_of("0123456789", NumberStart);
|
||||
|
||||
if (NumberStart == NumberEnd)
|
||||
return std::vector<std::string>();
|
||||
|
||||
std::vector<std::string> ParentIds;
|
||||
|
||||
// P denotes a printed pattern; D denotes a sticker shortcut. Preserve base variants.
|
||||
for (size_t Position = Stem.find_last_of("PD"); Position != std::string_view::npos; Position = Position ? Stem.find_last_of("PD", Position - 1) : std::string_view::npos)
|
||||
{
|
||||
if (Position == 0 || Position + 1 == Stem.size())
|
||||
continue;
|
||||
|
||||
if (Stem.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", Position + 1) != std::string_view::npos)
|
||||
continue;
|
||||
|
||||
mFlags |= Flag::Decorated;
|
||||
|
||||
ParentIds.emplace_back(Stem.substr(0, Position));
|
||||
ParentIds.back() += ".DAT";
|
||||
}
|
||||
|
||||
return ParentIds;
|
||||
}
|
||||
|
||||
bool lcPieceData::InitializeFromCache(const CacheData& CacheData)
|
||||
{
|
||||
const int KnownFlags = static_cast<int>(Flag::Decorated) | static_cast<int>(Flag::Alias) | static_cast<int>(Flag::SubPiece);
|
||||
|
||||
if (CacheData.Description.size() >= sizeof(m_strDescription) || CacheData.PatternParentId.size() >= LC_PIECE_NAME_LEN || (CacheData.Flags & ~KnownFlags) != 0)
|
||||
return false;
|
||||
|
||||
lcstrcpy(m_strDescription, CacheData.Description.c_str());
|
||||
mPatternParentId = CacheData.PatternParentId;
|
||||
mFlags = Flags(static_cast<Flag>(CacheData.Flags));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
lcPieceData::CacheData lcPieceData::GetCacheData() const
|
||||
{
|
||||
return { m_strDescription, mPatternParentId, static_cast<quint8>(static_cast<int>(mFlags)) };
|
||||
}
|
||||
|
||||
void lcPieceData::SetAliasTarget(const lcPieceData* Target)
|
||||
{
|
||||
if (!IsAlias())
|
||||
return;
|
||||
|
||||
mPatternParentId = Target ? Target->GetPatternParentId() : std::string();
|
||||
mFlags.setFlag(Flag::Decorated, HasDecorationDescription(m_strDescription) || (Target && Target->IsDecorated()));
|
||||
}
|
||||
|
||||
void lcPieceData::SetLoadingBoundingBox()
|
||||
{
|
||||
// Keep these bounds in sync with lcMesh::CreateBox().
|
||||
|
||||
Reference in new issue
Block a user