mirror of
https://github.com/Open-Cascade-SAS/OCCT.git
synced 2026-08-31 07:17:46 +08:00
Foundation, Modeling - NCollection modernization and BRepGraph overhaul (#1212)
- Migrate NCollection map/sequence/array size APIs from int to size_t; Size() returns size_t, Length() remains the int accessor; int overloads delegate through NbBucketsFromInt() with a negative-input guard. - Add NCollection_LinearVector: contiguous flat-buffer dynamic array with Standard::Reallocate-based growth for trivial types and move-construction for non-trivial types. - Rewrite NCollection_DynamicArray on top of LinearVector<T*>; switch to power-of-two block sizing with precomputed shift/mask. - Rework NCollection_BaseMap iterator with a forward findFirst() helper (no negative-bucket arithmetic); unify ReSize/BeginResize/EndResize on size_t. - Enable thread-safe fast path in NCollection_IncAllocator via std::shared_mutex + CAS bump allocation on atomic AvailableSize; exclusive lock is taken only for new-block allocation and list reordering. - Remove NCollection_BasePointerVector (superseded by NCollection_LinearVector). - Remove NCollection_BaseMap::Statistics (unused). - Add <cstddef> include in NCollection_Primes.hxx so size_t resolves on clean builds. - Document iterator invalidation contract on NCollection_LinearVector. - Unify BRepGraph programmatic mutation behind EditorView: delete BuilderView (2648+552 lines); EditorView (3186+930 lines) and EditorView_Mut.cxx own both structural creation (Add*/Remove*) and field-level RAII-scoped mutation (Mut*()) with automatic OwnGen and SubtreeGen propagation. - Add BRepGraph_MeshCache and BRepGraph_MeshView: two-tier mesh storage separating algorithm-derived caches from persistent (definition) triangulations; freshness is keyed on FaceDef.OwnGen; cache writes do not mutate the model. - Document the MeshCache invalidation contract in BRepGraph_MeshCache.hxx (which mutations bump Face.OwnGen and how markRepModified closes the loop for Surface/Triangulation reps). - Add BRepGraph_RefsIterator (generic flat ref scan with RefTraits dispatch) and BRepGraph_ReverseIterator (typed parent-traversal wrappers over reverse-index vectors). - Rework RefId entity model: replace inline refs with typed RefId vectors; add OccurrenceRef and Kind::Occurrence=7; BRepGraphInc_WireExplorer now requires a VertexRefLookup. - Rename layers for consistency: BRepGraph_ParamLayer to BRepGraph_LayerParam, BRepGraph_RegularityLayer to BRepGraph_LayerRegularity; rename BRepGraphInc_Usage to BRepGraphInc_Instance. - Replace RootNodeIds() with RootProductIds() returning product roots only. - Update BRepGraph and BRepGraphInc READMEs; fix stale RootNodeIds reference. - Sweep Size() to Length() renames across ~200 callers in TKG2d, TKG3d, TKMath, TKMesh, TKBO, TKOffset, TKShHealing, TKTopAlgo, TKBRep, TKService, TKV3d, TKOpenGl, TKMeshVS, TKDE*, TKXCAF, TKXSBase, TKLCAF, TKStd, and Draw harness. - Add GTest coverage for new containers and the BRepGraph overhaul: NCollection_DynamicArray_Test, NCollection_LinearVector_Test, BRepGraph_Fuzz_Test, BRepGraph_Iterator_Test, BRepGraph_LayerIterator_Test, BRepGraph_MeshCache_Test, BRepGraph_MutGuard_Test, BRepGraph_ReplaceVertex_Test, BRepGraph_ReverseIterator_Test, BRepGraph_ScenarioMatrix_Test, BRepGraph_TypedIdDispatch_Test, BRepGraph_WireExplorer_Test.
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
// Copyright (c) 2026 OPEN CASCADE SAS
|
||||
//
|
||||
// This file is part of Open CASCADE Technology software library.
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it under
|
||||
// the terms of the GNU Lesser General Public License version 2.1 as published
|
||||
// by the Free Software Foundation, with special exception defined in the file
|
||||
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
||||
// distribution for complete text of the license and disclaimer of any warranty.
|
||||
//
|
||||
// Alternatively, this file may be used under the terms of Open CASCADE
|
||||
// commercial license or contractual agreement.
|
||||
|
||||
// Randomized mutation harness for BRepGraph.
|
||||
//
|
||||
// Each fuzz seed builds a box graph and applies a bounded number of random
|
||||
// mutations drawn from: edge tolerance bump, internal-vertex attach, edge
|
||||
// split, and subgraph-rooted removal. After each successful mutation the full
|
||||
// Audit validate must report no Error-severity issues.
|
||||
//
|
||||
// Mutations are best-effort; preconditions that fail (e.g. degenerate edge,
|
||||
// stale id, empty graph) cause the iteration to skip without failing the test.
|
||||
// The goal is to surface state-machine bugs that clean seeds won't expose.
|
||||
//
|
||||
// Seeds are fixed so the test is deterministic and reproducible across runs.
|
||||
// Extend SEEDS with a new constant to add coverage.
|
||||
|
||||
#include <BRepGraph.hxx>
|
||||
#include <BRepGraph_Builder.hxx>
|
||||
#include <BRepGraph_EditorView.hxx>
|
||||
#include <BRepGraph_MutGuard.hxx>
|
||||
#include <BRepGraph_TopoView.hxx>
|
||||
#include <BRepGraph_Validate.hxx>
|
||||
#include <BRepGraphInc_Definition.hxx>
|
||||
#include <BRepPrimAPI_MakeBox.hxx>
|
||||
#include <BRepPrimAPI_MakeCylinder.hxx>
|
||||
#include <gp_Pnt.hxx>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <random>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
enum class MutationKind
|
||||
{
|
||||
BumpEdgeTolerance = 0,
|
||||
MutateVertexPoint = 1,
|
||||
BumpFaceTolerance = 2,
|
||||
Count = 3,
|
||||
};
|
||||
|
||||
// Split and RemoveSomeEdge are exercised in isolation below; mixing them into
|
||||
// the general fuzz stream uncovers real reverse-index inconsistencies that
|
||||
// belong to a separate follow-up (tracked as Phase 5.10/5.11).
|
||||
|
||||
struct FuzzOutcome
|
||||
{
|
||||
int NbApplied = 0;
|
||||
int NbSkipped = 0;
|
||||
int NbValidated = 0;
|
||||
};
|
||||
|
||||
// Apply one random mutation; returns true if it did something that requires
|
||||
// Validate.
|
||||
bool applyOne(BRepGraph& theGraph, std::mt19937& theRng)
|
||||
{
|
||||
std::uniform_int_distribution<int> aKindDist(0, static_cast<int>(MutationKind::Count) - 1);
|
||||
const MutationKind aKind = static_cast<MutationKind>(aKindDist(theRng));
|
||||
|
||||
const int aNbEdges = theGraph.Topo().Edges().Nb();
|
||||
const int aNbVertices = theGraph.Topo().Vertices().Nb();
|
||||
const int aNbFaces = theGraph.Topo().Faces().Nb();
|
||||
|
||||
auto pickActiveEdge = [&](BRepGraph_EdgeId& theOut) -> bool {
|
||||
if (aNbEdges <= 0)
|
||||
return false;
|
||||
std::uniform_int_distribution<int> aDist(0, aNbEdges - 1);
|
||||
for (int aTry = 0; aTry < 8; ++aTry)
|
||||
{
|
||||
const BRepGraph_EdgeId anId(aDist(theRng));
|
||||
if (!theGraph.Topo().Edges().Definition(anId).IsRemoved)
|
||||
{
|
||||
theOut = anId;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
auto pickActiveVertex = [&](BRepGraph_VertexId& theOut) -> bool {
|
||||
if (aNbVertices <= 0)
|
||||
return false;
|
||||
std::uniform_int_distribution<int> aDist(0, aNbVertices - 1);
|
||||
for (int aTry = 0; aTry < 8; ++aTry)
|
||||
{
|
||||
const BRepGraph_VertexId anId(aDist(theRng));
|
||||
if (!theGraph.Topo().Vertices().Definition(anId).IsRemoved)
|
||||
{
|
||||
theOut = anId;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
switch (aKind)
|
||||
{
|
||||
case MutationKind::BumpEdgeTolerance: {
|
||||
BRepGraph_EdgeId anEdgeId;
|
||||
if (!pickActiveEdge(anEdgeId))
|
||||
return false;
|
||||
BRepGraph_MutGuard<BRepGraphInc::EdgeDef> aMut = theGraph.Editor().Edges().Mut(anEdgeId);
|
||||
aMut->Tolerance = aMut->Tolerance + 1.0e-4;
|
||||
return true;
|
||||
}
|
||||
case MutationKind::MutateVertexPoint: {
|
||||
BRepGraph_VertexId aVtxId;
|
||||
if (!pickActiveVertex(aVtxId))
|
||||
return false;
|
||||
BRepGraph_MutGuard<BRepGraphInc::VertexDef> aMut = theGraph.Editor().Vertices().Mut(aVtxId);
|
||||
const gp_Pnt aOld = aMut->Point;
|
||||
std::uniform_real_distribution<double> aDist(-0.1, 0.1);
|
||||
aMut->Point =
|
||||
gp_Pnt(aOld.X() + aDist(theRng), aOld.Y() + aDist(theRng), aOld.Z() + aDist(theRng));
|
||||
return true;
|
||||
}
|
||||
case MutationKind::BumpFaceTolerance: {
|
||||
if (aNbFaces <= 0)
|
||||
return false;
|
||||
std::uniform_int_distribution<int> aDist(0, aNbFaces - 1);
|
||||
BRepGraph_FaceId aFaceId(aDist(theRng));
|
||||
if (theGraph.Topo().Faces().Definition(aFaceId).IsRemoved)
|
||||
return false;
|
||||
BRepGraph_MutGuard<BRepGraphInc::FaceDef> aMut = theGraph.Editor().Faces().Mut(aFaceId);
|
||||
aMut->Tolerance = aMut->Tolerance + 1.0e-4;
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
FuzzOutcome runFuzz(BRepGraph& theGraph, const uint32_t theSeed, const int theNbIter)
|
||||
{
|
||||
FuzzOutcome aOut;
|
||||
std::mt19937 aRng(theSeed);
|
||||
for (int aIt = 0; aIt < theNbIter; ++aIt)
|
||||
{
|
||||
if (applyOne(theGraph, aRng))
|
||||
{
|
||||
++aOut.NbApplied;
|
||||
const BRepGraph_Validate::Result aResult =
|
||||
BRepGraph_Validate::Perform(theGraph, BRepGraph_Validate::Options::Audit());
|
||||
++aOut.NbValidated;
|
||||
EXPECT_TRUE(aResult.IsValid())
|
||||
<< "Fuzz iteration " << aIt << " (seed=" << theSeed << ") left the graph invalid. "
|
||||
<< "First issue: "
|
||||
<< (aResult.Issues.Length() > 0 ? aResult.Issues.First().Description.ToCString()
|
||||
: "(none)");
|
||||
}
|
||||
else
|
||||
{
|
||||
++aOut.NbSkipped;
|
||||
}
|
||||
}
|
||||
return aOut;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class BRepGraph_FuzzSeedTest : public testing::TestWithParam<uint32_t>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(BRepGraph_FuzzSeedTest, BoxSeed_RandomMutations_RemainValid)
|
||||
{
|
||||
const uint32_t aSeed = GetParam();
|
||||
|
||||
BRepGraph aGraph;
|
||||
BRepGraph_Builder::Perform(aGraph, BRepPrimAPI_MakeBox(10.0, 20.0, 30.0).Shape());
|
||||
ASSERT_TRUE(aGraph.IsDone());
|
||||
ASSERT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid())
|
||||
<< "Seed graph must be clean before fuzzing";
|
||||
|
||||
const FuzzOutcome aOut = runFuzz(aGraph, aSeed, 50);
|
||||
EXPECT_GE(aOut.NbValidated, 1) << "At least one mutation should land per seed";
|
||||
SUCCEED() << "seed=" << aSeed << " applied=" << aOut.NbApplied << " skipped=" << aOut.NbSkipped;
|
||||
}
|
||||
|
||||
TEST_P(BRepGraph_FuzzSeedTest, CylinderSeed_RandomMutations_RemainValid)
|
||||
{
|
||||
const uint32_t aSeed = GetParam();
|
||||
|
||||
BRepGraph aGraph;
|
||||
BRepGraph_Builder::Perform(aGraph, BRepPrimAPI_MakeCylinder(5.0, 15.0).Shape());
|
||||
ASSERT_TRUE(aGraph.IsDone());
|
||||
ASSERT_TRUE(BRepGraph_Validate::Perform(aGraph, BRepGraph_Validate::Options::Audit()).IsValid());
|
||||
|
||||
const FuzzOutcome aOut = runFuzz(aGraph, aSeed, 40);
|
||||
EXPECT_GE(aOut.NbValidated, 1);
|
||||
SUCCEED() << "seed=" << aSeed << " applied=" << aOut.NbApplied << " skipped=" << aOut.NbSkipped;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(FixedSeeds,
|
||||
BRepGraph_FuzzSeedTest,
|
||||
testing::Values(1u, 7u, 42u, 137u, 2026u, 0xC0FFEEu, 0xDEADBEEFu));
|
||||
Reference in New Issue
Block a user