mirror of
https://github.com/Open-Cascade-SAS/OCCT.git
synced 2026-10-18 15:04:55 +08:00
- Added new GTests covering multiple historical regressions (intersection, projection, pipe shell, boolean ops, fillet, distances, gp transforms, string hashing). - Registered new test sources in the relevant `GTests/FILES.cmake` lists. - Removed legacy Tcl tests and removed the associated QADraw commands from `QABugs_*.cxx`.
247 lines
9.3 KiB
C++
247 lines
9.3 KiB
C++
// Copyright (c) 2025 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.
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <BRep_Builder.hxx>
|
|
#include <BRep_Tool.hxx>
|
|
#include <BRepBuilderAPI_MakeEdge.hxx>
|
|
#include <BRepBuilderAPI_MakeFace.hxx>
|
|
#include <BRepBuilderAPI_MakeWire.hxx>
|
|
#include <BRepCheck_Analyzer.hxx>
|
|
#include <BRepGProp.hxx>
|
|
#include <BRepOffsetAPI_MakePipeShell.hxx>
|
|
#include <GC_MakeArcOfCircle.hxx>
|
|
#include <Geom_Plane.hxx>
|
|
#include <GProp_GProps.hxx>
|
|
#include <gp_Ax1.hxx>
|
|
#include <gp_Ax2.hxx>
|
|
#include <gp_Circ.hxx>
|
|
#include <gp_Dir.hxx>
|
|
#include <gp_Pln.hxx>
|
|
#include <gp_Pnt.hxx>
|
|
#include <Law_Linear.hxx>
|
|
#include <Precision.hxx>
|
|
#include <Standard_Handle.hxx>
|
|
#include <TopoDS.hxx>
|
|
#include <TopoDS_Edge.hxx>
|
|
#include <TopoDS_Face.hxx>
|
|
#include <TopoDS_Shell.hxx>
|
|
#include <TopoDS_Shape.hxx>
|
|
#include <TopoDS_Solid.hxx>
|
|
#include <TopoDS_Wire.hxx>
|
|
#include <TopExp_Explorer.hxx>
|
|
|
|
#include <cmath>
|
|
|
|
namespace
|
|
{
|
|
|
|
//! Computes the total linear length of a wire.
|
|
double wireLength(const TopoDS_Wire& theWire)
|
|
{
|
|
GProp_GProps aProps;
|
|
BRepGProp::LinearProperties(theWire, aProps);
|
|
return aProps.Mass();
|
|
}
|
|
|
|
//! Builds a bent tube solid (wallSolid) and an inner gas solid (gasSolid).
|
|
//! Mirrors the OCC332 Draw command algorithm:
|
|
//! - A circular cross-section profile is swept along an arc spine
|
|
//! - A linear scaling law varies the inner radius from radius_l to radius_r
|
|
//! - The outer radius is offset by wall_thickness throughout
|
|
//!
|
|
//! @param[in] theWallThickness wall thickness of the tube
|
|
//! @param[in] theDia1 inner diameter at start of bend
|
|
//! @param[in] theDia2 inner diameter at end of bend
|
|
//! @param[in] theMajorRadius radius of the bend arc (spine radius)
|
|
//! @param[in] theLength arc length along the spine
|
|
//! @param[out] theWallSolid resulting tube wall solid
|
|
//! @param[out] theGasSolid resulting inner gas solid
|
|
//! @return false if any construction step fails
|
|
bool buildBentTube(double theWallThickness,
|
|
double theDia1,
|
|
double theDia2,
|
|
double theMajorRadius,
|
|
double theLength,
|
|
TopoDS_Solid& theWallSolid,
|
|
TopoDS_Shape& theGasSolid)
|
|
{
|
|
const double aBendAngle = theLength / theMajorRadius;
|
|
if (aBendAngle >= M_PI)
|
|
return false;
|
|
|
|
const double aRadiusL = theDia1 / 2.0;
|
|
const double aRadiusR = theDia2 / 2.0;
|
|
|
|
gp_Ax2 anOrigin(gp_Pnt(5000.0, -300.0, 1000.0), gp_Dir(0.0, -1.0, -1.0));
|
|
|
|
gp_Pln aCirc1Plane(anOrigin.Location(), anOrigin.Direction());
|
|
gp_Circ aFaceCircle(anOrigin, aRadiusL);
|
|
gp_Circ anOutFaceCircle(anOrigin, aRadiusL + theWallThickness);
|
|
|
|
gp_Pnt aCircCenter = anOrigin.Location().Translated(theMajorRadius * anOrigin.XDirection());
|
|
gp_Ax1 aCircAxis(aCircCenter, anOrigin.YDirection());
|
|
gp_Pln aCirc2Plane = aCirc1Plane.Rotated(aCircAxis, aBendAngle);
|
|
|
|
gp_Ax2 aSpineAxis(aCircCenter, anOrigin.YDirection(), -anOrigin.XDirection());
|
|
gp_Circ aSpineCircle(aSpineAxis, theMajorRadius);
|
|
|
|
TopoDS_Wire aWire1 = BRepBuilderAPI_MakeWire(BRepBuilderAPI_MakeEdge(aFaceCircle)).Wire();
|
|
TopoDS_Wire anOutWire = BRepBuilderAPI_MakeWire(BRepBuilderAPI_MakeEdge(anOutFaceCircle)).Wire();
|
|
|
|
occ::handle<Geom_Curve> aSpineCurve(
|
|
GC_MakeArcOfCircle(aSpineCircle, 0.0, aBendAngle, true).Value());
|
|
|
|
occ::handle<Law_Linear> aLaw1 = new Law_Linear();
|
|
occ::handle<Law_Linear> aLaw2 = new Law_Linear();
|
|
aLaw1->Set(aSpineCurve->FirstParameter(), 1.0, aSpineCurve->LastParameter(), aRadiusR / aRadiusL);
|
|
aLaw2->Set(aSpineCurve->FirstParameter(),
|
|
1.0,
|
|
aSpineCurve->LastParameter(),
|
|
(aRadiusR + theWallThickness) / (aRadiusL + theWallThickness));
|
|
|
|
BRepBuilderAPI_MakeEdge aMkEdge(aSpineCurve);
|
|
if (!aMkEdge.IsDone())
|
|
return false;
|
|
TopoDS_Wire aSpineWire = BRepBuilderAPI_MakeWire(aMkEdge.Edge()).Wire();
|
|
|
|
BRepOffsetAPI_MakePipeShell aMkPipe1(aSpineWire);
|
|
aMkPipe1.SetTolerance(1.0e-8, 1.0e-8, 1.0e-6);
|
|
aMkPipe1.SetLaw(aWire1, aLaw1, false, false);
|
|
aMkPipe1.Build();
|
|
if (!aMkPipe1.IsDone())
|
|
return false;
|
|
|
|
BRepOffsetAPI_MakePipeShell aMkPipe2(aSpineWire);
|
|
aMkPipe2.SetTolerance(1.0e-8, 1.0e-8, 1.0e-6);
|
|
aMkPipe2.SetLaw(anOutWire, aLaw2, false, false);
|
|
aMkPipe2.Build();
|
|
if (!aMkPipe2.IsDone())
|
|
return false;
|
|
|
|
// Build opening face at start of bend (annular ring)
|
|
BRepBuilderAPI_MakeFace aMkFace;
|
|
occ::handle<Geom_Plane> aPlane1 = new Geom_Plane(aCirc1Plane);
|
|
aMkFace.Init(aPlane1, false, Precision::Confusion());
|
|
aMkFace.Add(TopoDS::Wire(aMkPipe2.FirstShape()));
|
|
aMkFace.Add(TopoDS::Wire(aMkPipe1.FirstShape().Reversed()));
|
|
if (!aMkFace.IsDone())
|
|
return false;
|
|
TopoDS_Face aFace1 = aMkFace.Face();
|
|
|
|
// Build opening face at end of bend (annular ring)
|
|
occ::handle<Geom_Plane> aPlane2 = new Geom_Plane(aCirc2Plane);
|
|
aMkFace.Init(aPlane2, false, Precision::Confusion());
|
|
aMkFace.Add(TopoDS::Wire(aMkPipe2.LastShape()));
|
|
aMkFace.Add(TopoDS::Wire(aMkPipe1.LastShape().Reversed()));
|
|
if (!aMkFace.IsDone())
|
|
return false;
|
|
TopoDS_Face aFace2 = aMkFace.Face();
|
|
|
|
// Assemble tube shell from inner pipe face, outer pipe face, and two annular ring faces
|
|
TopoDS_Shell aTubeShell;
|
|
BRep_Builder aBuilder;
|
|
aBuilder.MakeShell(aTubeShell);
|
|
|
|
TopExp_Explorer aFaceExp(aMkPipe1.Shape(), TopAbs_FACE);
|
|
if (aFaceExp.More())
|
|
aBuilder.Add(aTubeShell, aFaceExp.Current().Reversed());
|
|
|
|
// Extract the gas solid before modifying mkPipe1 to a solid
|
|
aMkPipe1.MakeSolid();
|
|
theGasSolid = aMkPipe1.Shape();
|
|
|
|
aFaceExp.Init(aMkPipe2.Shape(), TopAbs_FACE);
|
|
if (aFaceExp.More())
|
|
aBuilder.Add(aTubeShell, aFaceExp.Current());
|
|
|
|
aBuilder.Add(aTubeShell, aFace1.Reversed());
|
|
aBuilder.Add(aTubeShell, aFace2);
|
|
aTubeShell.Closed(BRep_Tool::IsClosed(aTubeShell));
|
|
|
|
aBuilder.MakeSolid(theWallSolid);
|
|
aBuilder.Add(theWallSolid, aTubeShell);
|
|
|
|
return true;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
//=================================================================================================
|
|
|
|
// Bug OCC332: BRepOffsetAPI_MakePipeShell with linear scaling law along a bent tube.
|
|
// Verifies that:
|
|
// - Both wallSolid and gasSolid are geometrically valid shapes
|
|
// - wallSolid contains 4 circular cross-section wires matching expected circumferences:
|
|
// pi*dia1, pi*(dia1+2*wall), pi*dia2, pi*(dia2+2*wall)
|
|
// within 0.1% tolerance (as validated in the original TCL test).
|
|
TEST(BRepOffsetAPI_MakePipeShellTest, Bug332_BentTubeWithScalingLaw)
|
|
{
|
|
const double aWallThickness = 10.0;
|
|
const double aDia1 = 30.0;
|
|
const double aDia2 = 50.0;
|
|
const double aMajorRadius = 100.0;
|
|
const double aLength = 200.0;
|
|
|
|
TopoDS_Solid aWallSolid;
|
|
TopoDS_Shape aGasSolid;
|
|
const bool aBuilt =
|
|
buildBentTube(aWallThickness, aDia1, aDia2, aMajorRadius, aLength, aWallSolid, aGasSolid);
|
|
ASSERT_TRUE(aBuilt) << "buildBentTube construction failed";
|
|
|
|
// Validate both solids
|
|
EXPECT_TRUE(BRepCheck_Analyzer(aWallSolid).IsValid()) << "wallSolid is not valid";
|
|
EXPECT_TRUE(BRepCheck_Analyzer(aGasSolid).IsValid()) << "gasSolid is not valid";
|
|
|
|
// Collect all wire lengths in wallSolid
|
|
NCollection_Sequence<double> aWireLengths;
|
|
for (TopExp_Explorer anExp(aWallSolid, TopAbs_WIRE); anExp.More(); anExp.Next())
|
|
{
|
|
const double aLen = wireLength(TopoDS::Wire(anExp.Current()));
|
|
if (aLen > Precision::Confusion())
|
|
aWireLengths.Append(aLen);
|
|
}
|
|
|
|
// Expected circular circumferences from the TCL test (pi * diameter):
|
|
// Wire4 in TCL: pi * dia1 = pi * 30 (inner start, radius=15)
|
|
// Wire3 in TCL: pi * (dia1 + 2*wall) = pi * 50 (outer start, radius=25)
|
|
// Wire6 in TCL: pi * dia2 = pi * 50 (inner end, radius=25)
|
|
// Wire5 in TCL: pi * (dia2 + 2*wall) = pi * 70 (outer end, radius=35)
|
|
const double aPi = M_PI;
|
|
const double aExpLen1 = aPi * aDia1; // pi*30 ~ 94.25
|
|
const double aExpLen2 = aPi * (aDia1 + 2.0 * aWallThickness); // pi*50 ~ 157.08
|
|
// aExpLen3 = pi*dia2 = pi*50, which equals aExpLen2 when dia2/2 == dia1/2 + wall
|
|
const double aExpLen4 = aPi * (aDia2 + 2.0 * aWallThickness); // pi*70 ~ 219.91
|
|
|
|
const double aTol = 0.001; // 0.1% relative tolerance
|
|
|
|
auto countMatching = [&](double theExpected) -> int {
|
|
int aCount = 0;
|
|
for (const double aLen : aWireLengths)
|
|
{
|
|
if (std::abs(aLen - theExpected) / theExpected <= aTol)
|
|
++aCount;
|
|
}
|
|
return aCount;
|
|
};
|
|
|
|
EXPECT_GE(countMatching(aExpLen1), 1)
|
|
<< "No wire found with circumference pi*dia1 = " << aExpLen1;
|
|
// aExpLen2 and aExpLen3 are identical (dia2/2 == dia1/2 + wall), so expect at least 2 matches
|
|
EXPECT_GE(countMatching(aExpLen2), 2)
|
|
<< "Expected 2 wires with circumference pi*50 = " << aExpLen2;
|
|
EXPECT_GE(countMatching(aExpLen4), 1)
|
|
<< "No wire found with circumference pi*(dia2+2*wall) = " << aExpLen4;
|
|
}
|