概述
上一篇文章《OCC开发_箱梁梁体建模》中详细介绍了箱梁梁体建模的过程。但是,对于实际桥梁,截面可能存在高度、腹板厚度、顶底板厚度变化,全桥的结构中心线存在平曲线和竖曲线。针对实际情况,通过一个截面拉伸来实现全桥建模显然不可能。因此,针对变高箱梁,本文新的思路来实现全桥建模。
思路
上一篇文章通过一个截面拉伸生成几何体的方式行不通,我们可以通过不同面来形成棱柱的方式实现。具体步骤如下:
- 生成控制数据(控制点位置、转角、梁高、底板厚);
- 生成各个截面(含内外轮廓,且偏移旋转到指定位置);
- 各个截面各轮廓生成棱柱;
- 布尔运算,外轮廓棱柱扣减各内轮廓棱柱;
- 保存.Brep文件;
- 查看效果。
实际的桥梁中,墩顶存在隔板,梁端存在托梁、槽口,隔板还存在人洞,本文暂不涉及这些构造。
代码实现
本文以某个具体桥梁为例来实现全桥建模,桥梁参数见效果栏。
#include <vector>
#include <tuple>
#include <gp_Pnt.hxx>
#include <gp_Circ.hxx>
#include <TopTools_ListOfShape.hxx>
#include <BRepTools.hxx>
#include <BRep_Tool.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepOffsetAPI_ThruSections.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <BRepPrimAPI_MakePrism.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <ShapeAnalysis_Edge.hxx>
#include <ShapeAnalysis_WireOrder.hxx>
#include <AIS_Shape.hxx>
#include <BRepAlgoAPI_Cut.hxx>
#pragma comment(lib, "TKernel.lib")
#pragma comment(lib, "TKMath.lib")
#pragma comment(lib, "TKG2d.lib")
#pragma comment(lib, "TKG3d.lib")
#pragma comment(lib, "TKGeomBase.lib")
#pragma comment(lib, "TKGeomAlgo.lib")
#pragma comment(lib, "TKBRep.lib")
#pragma comment(lib, "TKTopAlgo.lib")
#pragma comment(lib, "TKShHealing.lib")
#define pi 3.141592653589793 //π值预定义
//通过初始数据生成边
std::vector<TopoDS_Edge> GetEdges(std::vector<std::tuple <double, double, double>> tuples)
{
std::vector<TopoDS_Edge> anEdges;
for (int i = 0; i < tuples.size(); ++i)
{
int i1 = i;
int i2 = (i + 1) % tuples.size();
BRepBuilderAPI_MakeEdge tempRdge(gp_Pnt(std::get<0>(tuples[i1]), std::get<1>(tuples[i1]), std::get<2>(tuples[i1])),
gp_Pnt(std::get<0>(tuples[i2]), std::get<1>(tuples[i2]), std::get<2>(tuples[i2])));
anEdges.push_back(tempRdge.Edge());
}
return anEdges;
}
//通过边生成形状
TopTools_ListOfShape GetShape(std::vector<TopoDS_Edge> edges)
{
TopTools_ListOfShape aOrderedEdges;
for (int e = 0; e < edges.size(); ++e)
{
const TopoDS_Edge& anEdge = edges[e];
aOrderedEdges.Append(anEdge);
}
return aOrderedEdges;
}
//通过边生成BRepBuilderAPI_MakeWire
BRepBuilderAPI_MakeWire GetWire(std::vector<TopoDS_Edge> outerEdges)
{
TopTools_ListOfShape aOrderedEdges = GetShape(outerEdges);
BRepBuilderAPI_MakeWire aWireMaker;
aWireMaker.Add(aOrderedEdges);
return aWireMaker;
}
//生成截面定位点信息,未偏置和旋转
std::vector<std::vector<std::tuple <double, double, double>>> GetBoundaryPoints(double height, double bottomThick)
{
double topWidth = 16.00;
double bottomWidth = 11.00;
double edgeThick = 0.2;
double rootThick = 0.80;
double charmerWidth = 0.30;
double charmerHeight = 0.30;
double deckThick = 0.60;
double topThick = 0.30;
std::vector<std::tuple <double, double, double>> tuples;
tuples.push_back(std::make_tuple(-topWidth / 2, 0, 0.0));
tuples.push_back(std::make_tuple(topWidth / 2, 0, 0.0));
tuples.push_back(std::make_tuple(topWidth / 2, -edgeThick, 0.0));
tuples.push_back(std::make_tuple(bottomWidth / 2, -rootThick, 0.0));
tuples.push_back(std::make_tuple(bottomWidth / 2, -height, 0.00));
tuples.push_back(std::make_tuple(-bottomWidth / 2, -height, 0.0));
tuples.push_back(std::make_tuple(-bottomWidth / 2, -rootThick, 0.0));
tuples.push_back(std::make_tuple(-topWidth / 2, -edgeThick, 0.0));
std::vector<std::vector<std::tuple <double, double, double>>> outerTuples;
outerTuples.push_back(tuples);
for (int i = 0; i < 2; ++i)
{
std::vector<std::tuple <double, double, double>> outerTuple;
double gridWidth = bottomWidth / 2 - deckThick * 1.5;
double midX = (i == 0 ? -1 : 1) * (bottomWidth / 4 - deckThick / 4);
outerTuple.push_back(std::make_tuple(midX - gridWidth / 2 + charmerWidth, -topThick, 0.0));
outerTuple.push_back(std::make_tuple(midX - gridWidth / 2, -topThick - charmerHeight, 0.0));
outerTuple.push_back(std::make_tuple(midX - gridWidth / 2, -height + bottomThick + charmerHeight, 0.0));
outerTuple.push_back(std::make_tuple(midX - gridWidth / 2 + charmerWidth, -height + bottomThick, 0.0));
outerTuple.push_back(std::make_tuple(midX + gridWidth / 2 - charmerWidth, -height + bottomThick, 0.0));
outerTuple.push_back(std::make_tuple(midX + gridWidth / 2, -height + bottomThick + charmerHeight, 0.0));
outerTuple.push_back(std::make_tuple(midX + gridWidth / 2, -topThick - charmerHeight, 0.0));
outerTuple.push_back(std::make_tuple(midX + gridWidth / 2 - charmerWidth, -topThick, 0.0));
outerTuples.push_back(outerTuple);
}
return outerTuples;
}
//点偏移和旋转
std::tuple <double, double, double> Transform(std::tuple <double, double, double> inputPoint, std::tuple <double, double, double> centerPoint, double angle)
{
double x1 = std::get<0>(centerPoint)+ std::get<0>(inputPoint) * cos(angle);
double y1 = std::get<1>(centerPoint)+ std::get<1>(inputPoint);
double z1 = std::get<2>(centerPoint) + std::get<0>(inputPoint) * sin(angle);
return std::make_tuple(x1, y1, z1);
}
//实际位置截面(经过偏移和旋转)
std::vector<std::vector<std::tuple <double, double, double>>> GetBoundaryPoints(std::tuple <double, double, double> centerPoint, double angle, double height, double bottomThick)
{
std::vector<std::vector<std::tuple <double, double, double>>> sectionBoundaries = GetBoundaryPoints(height, bottomThick);
std::vector<std::vector<std::tuple <double, double, double>>> transformedBoundaries;
for (int i = 0; i < sectionBoundaries.size(); ++i)
{
std::vector<std::tuple <double, double, double>> transformedBoundary;
for (int j = 0; j < sectionBoundaries[i].size(); ++j)
{
//transformedBoundary.push_back(sectionBoundaries[i][j]);
transformedBoundary.push_back(Transform(sectionBoundaries[i][j], centerPoint, angle));
}
transformedBoundaries.push_back(transformedBoundary);
}
return transformedBoundaries;
}
//输出梁高列表(也可以用于板厚)
std::vector<double> GetHeightList(std::vector<double> canSegmentLengthList, double colTopBeamHeight, double midBeamHeight)
{
double detaHeight = colTopBeamHeight - midBeamHeight;
double totalLength = 0;
for (int i = 0; i < canSegmentLengthList.size(); ++i)
{
totalLength = totalLength + canSegmentLengthList[i];
}
double factorA = detaHeight / (totalLength * totalLength);
std::vector<double> heightList = { midBeamHeight };
double detaLength = 0;
for (int i = 0; i < canSegmentLengthList.size(); ++i)
{
detaLength = detaLength + canSegmentLengthList[i];
double height = factorA * detaLength * detaLength + midBeamHeight;
heightList.insert(heightList.begin(), height);
}
return heightList;
}
//输出里程、梁高、板厚列表
std::vector<std::tuple <double, double, double>> GetStaTupleList(std::vector<double> canSegmentLengthList, double midSta, double equalHeightLength, double colTopBeamHeight, double midBeamHeight, double colTopBottomThick, double midBottomThick)
{
std::vector<double> heightList = GetHeightList(canSegmentLengthList, colTopBeamHeight, midBeamHeight);//梁高列表
std::vector<double> bottomThickList = GetHeightList(canSegmentLengthList, colTopBottomThick, midBottomThick);//梁高列表
std::vector<std::tuple <double, double, double>> staTupleList;
staTupleList.push_back(std::make_tuple(midSta - equalHeightLength / 2, colTopBeamHeight, colTopBottomThick));
staTupleList.push_back(std::make_tuple(midSta + equalHeightLength / 2, colTopBeamHeight, colTopBottomThick));
double staStart = midSta - equalHeightLength / 2;
double staEnd = midSta + equalHeightLength / 2;
for (int i = 0; i < canSegmentLengthList.size(); ++i)
{
staStart = staStart - canSegmentLengthList[i];
staEnd = staEnd + canSegmentLengthList[i];
staTupleList.push_back(std::make_tuple(staEnd, heightList[i + 1], bottomThickList[i + 1]));
staTupleList.insert(staTupleList.begin(), std::make_tuple(staStart, heightList[i + 1], bottomThickList[i + 1]));
}
return staTupleList;
}
//获取位置、角度、梁高、顶板厚列表
//跨径组合65+110+65
//支点梁高6.5,板厚0.8,跨中梁高2.6,板厚0.32
//支点等高段3,变高段52.5,合拢段3,边跨9
//节段划分12+3*3.5+6*4+3*4.5
//起点里程为0,高程为0,终点高程2.4m
//平面在半径1000的圆曲线上
std::vector<std::tuple <std::tuple <double, double, double>, double, double, double>> GetControlMessageList()
{
double edgeSpanLength = 65;//边跨长
double midSpanLength = 100;//中跨长
double colTopBeamHeight = 6.5;//墩顶梁高
double midBeamHeight = 2.6;//跨中梁高
double colTopBottomThick = 0.8;//墩顶板厚
double midBottomThick = 0.32;//跨中板厚
double equalHeightLength = 3;//墩顶等高段长
double variableHeightLength = 52.5;//变高段长
double closedSegmentLength = 2;//合拢段长
double edgeSegmentLength = 9;//边跨现浇段长
std::vector<double> canSegmentLengthList = { 4.5,3.5,3.5,3.5,4,4,4,4,4,4,4.5,4.5,4.5 };//T构节段长列表
std::vector<std::tuple <double, double, double>> staList;//里程,梁高,底板厚
//计算两个T构的里程,梁高,底板厚
double midSta = edgeSpanLength; //墩顶中心里程
std::vector<std::tuple <double, double, double>> list1 = GetStaTupleList(canSegmentLengthList, midSta, equalHeightLength, colTopBeamHeight, midBeamHeight, colTopBottomThick, midBottomThick);
staList.insert(staList.end(), list1.begin(), list1.end());
midSta = edgeSpanLength + midSpanLength; //墩顶中心里程
std::vector<std::tuple <double, double, double>> list2 = GetStaTupleList(canSegmentLengthList, midSta, equalHeightLength, colTopBeamHeight, midBeamHeight, colTopBottomThick, midBottomThick);
staList.insert(staList.end(), list2.begin(), list2.end());
//计算两侧边跨段的里程,梁高,底板厚
double endSta0 = std::get<0>(staList[staList.size() - 1]);
double endSta1 = endSta0 + closedSegmentLength;
double endSta2 = endSta0 + (closedSegmentLength+ edgeSegmentLength);
double startSta0 = std::get<0>(staList[0]);
double startSta1 = startSta0 - closedSegmentLength;
double startSta2 = startSta0 - (closedSegmentLength + edgeSegmentLength);
staList.insert(staList.end(), std::make_tuple(endSta1, midBeamHeight, midBottomThick));
staList.insert(staList.end(), std::make_tuple(endSta2, midBeamHeight, midBottomThick));
staList.insert(staList.begin(), std::make_tuple(startSta1, midBeamHeight, midBottomThick));
staList.insert(staList.begin(), std::make_tuple(startSta2, midBeamHeight, midBottomThick));
//计算坐标和角度
std::vector<std::tuple <std::tuple <double, double, double>, double, double, double>> pointTuples;
for (int i = 0; i < staList.size(); ++i)
{
std::tuple <double, double, double> tuple = staList[i];
double detaSta = std::get<0>(tuple);
double detaHeight = detaSta * 0.01;//竖向,Y
double detaSita = detaSta / 1000;
double sita = pi / 2 - detaSita;
double detaX = 1000 * cos(sita);//纵向
double detaY = 1000 - 1000 * sin(sita);//横向
double beamHeight = std::get<1>(tuple);
double bottomThick = std::get<2>(tuple);
std::tuple <double, double, double> point = std::make_tuple(detaY , detaHeight, detaX);
pointTuples.push_back(std::make_tuple(point, detaSita, beamHeight, bottomThick));
}
return pointTuples;
}
//生成全桥
void GenerateBridge()
{
//生成控制信息
std::vector<std::tuple <std::tuple <double, double, double>, double, double, double>> pointTuples = GetControlMessageList();
//生成各个截面
std::vector<std::vector<std::vector<std::tuple <double, double, double>>>> sections;//所有截面实际定位信息(偏移旋转后)
for (int i = 0; i < pointTuples.size(); ++i)
{
std::tuple <std::tuple <double, double, double>, double, double, double> tuple = pointTuples[i];
std::tuple <double, double, double> points = std::get<0>(tuple);
sections.push_back(GetBoundaryPoints(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple)));
}
//生成截面各轮廓体
std::vector<BRepOffsetAPI_ThruSections> generators;
for (int i = 0; i < sections[0].size(); ++i)
{
BRepOffsetAPI_ThruSections generator(Standard_True, Standard_False);
generators.push_back(generator);
}
for (int i = 0; i < sections.size(); ++i)
{
std::vector<std::vector<std::tuple <double, double, double>>> section = sections[i];
for (int j = 0; j < section.size(); ++j)
{
std::vector<TopoDS_Edge> innerEdges0 = GetEdges(section[j]);
TopoDS_Wire wire = GetWire(innerEdges0);
generators[j].AddWire(wire);
}
}
std::vector<TopoDS_Shape> shapes;
for (int i = 0; i < sections[0].size(); ++i)
{
generators[i].Build();
shapes.push_back(generators[i].Shape());
}
//外轮廓体扣减内轮廓体
TopoDS_Shape S1 = BRepAlgoAPI_Cut(shapes[0], shapes[1]);
for (int i = 2; i < sections[0].size(); ++i)
{
S1 = BRepAlgoAPI_Cut(S1, shapes[i]);
}
//输出
BRepTools::Write(S1, "d:/wire.brep");//保存文件
}
//主函数
int main(int argc, char* argv[])
{
GenerateBridge();
return 0;
}
效果
桥梁在半径1000m的圆弧平曲线上,竖曲线为1%的纵坡。全桥跨径组合为65+110+65m,墩顶梁高6.5m,跨中梁高2.8m,顶板厚0.3m,底板厚0.32~0.8m,抛物线次数为2.0次,墩顶水平段3m,变高段52.5m,跨中合拢段和边跨合拢段均为2.0m,边跨现浇段9m,T构节段划分为12+33.5+64+3*4.5m。桥梁立面图纸(已经对称处理)如下:
全桥三维模型形状如下图(隔板未建模):