
在三维几何建模中,圆(Circle)是极为常用的一种基础曲线。它在构建圆柱体、圆弧、旋转体等几何对象中扮演着核心角色。本文将基于 OpenCascade 中的 gp_Circ 类型,结合完整的可交互代码示例 ,介绍圆的构造方法、参数提取与辅助可视化技巧。
一、什么是 gp_Circ?
gp_Circ 是 OpenCascade 中用于表示三维空间圆的几何类,属于 gp(Geometry Package)模块。它定义了一个由圆心、法向量和平面上的半径所构成的圆。
最常用的构造方式如下:
gp_Circ(const gp_Ax2& Ax2, const Standard_Real Radius);
其中 gp_Ax2 定义了圆的参考坐标系(包括圆心位置和法向量方向)。
下图展示了一个典型的 gp_Circ 圆形及其辅助图元:
图中蓝色圆为 gp_Circ 构建出的实体,红线为法向箭头,黄色平面为其构造所在的几何平面。
二、教学功能概览
代码实现了一个具备交互式教学功能的圆形构造器,核心逻辑基于 gp_Circ,可完成如下任务:
- 使用圆心+法向+半径构造圆
- 通过三点计算圆
- 提取圆的几何参数
- 可视化法向箭头
- 可视化圆所在平面
1. 从圆心、法向和半径构造圆

这是最直观且常用的方式。用户可以在窗口中调节圆心坐标、法向量分量与半径,点击“Display”按钮即可生成圆:
gp_Pnt ctr(center[0], center[1], center[2]);
gp_Dir dir(normal[0], normal[1], normal[2]);
m_circ = gp_Cir
c(gp_Ax2(ctr, dir), radius);
2. 通过三点计算圆

借助 GC_MakeCircle 工具类,可以通过三个不共线点来构造唯一确定的圆:
GC_MakeCircle mkCirc(p1, p2, p3);
m_circ = mkCirc.Value()->Circ();
该方法自动判断圆心、法向、半径,适用于几何逆推场景。
GC_MakeCircle具体信息参考:GC_MakeCircle Class Reference
3. 提取圆的参数

当前构造的 gp_Circ 圆形支持提取其关键参数,包括圆心位置、法向量方向与半径:
gp_Pnt ctr = m_circ.Location();
gp_Dir dir = m_circ.Axis().Direction();
double radius = m_circ.Radius();
提取结果将以文本形式实时展示于窗口右侧。
4. 可视化圆的法向方向

通过构造起点为圆心、方向为法向的辅助箭头,可以直观展示圆的朝向:
gp_Pnt end = ctr.Translated(dir.XYZ() * 15);
DisplayAuxLine(context, ctr, end, Quantity_NOC_RED, 2.0);
5. 显示圆所在的几何平面

通过当前圆的位置与方向构造一个 gp_Pln 平面,并渲染为透明黄色图形:
(想了解gp_Pln可参考【OCCT+ImGUI系列】003-MD-几何平面类gp_Pln)
gp_Pln pln(ctr, dir);
Handle(Geom_Plane) gpln = new Geom_Plane(pln);
Handle(AIS_Shape) face = new AIS_Shape(BRepBuilderAPI_MakeFace(gpln->Pln(), -20, 20, -20, 20).Shape());
这可以清晰展示圆的构造依托于哪个几何平面,便于与其他几何图元关联分析。
三、可视化渲染逻辑
每次构造圆后,都会通过以下步骤生成可交互圆形对象:
Handle(Geom_Circle) geomCirc = new Geom_Circle(m_circ);
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(geomCirc);
mainShape = new AIS_Shape(edge);
context->Display(mainShape, Standard_True);
该对象可直接在交互视图中旋转、缩放,并与辅助图元协同显示。
四、代码
class MDCircle004 : public BaseScene, public VisSceneComponents, public TutorialWindow {
public:
MDCircle004() { openTutorialWindow(); }
void displayScene(const Handle(V3d_View)& view, const Handle(AIS_InteractiveContext)& context) override {
if (context.IsNull()) return;
if (!bIsSceneInit) {
sceneInit(view, context);
bIsSceneInit = true;
}
renderTutorialWindow(context);
}
void sceneInit(const Handle(V3d_View)& view, const Handle(AIS_InteractiveContext)& context) override {
DisplayCircle(context);
}
void customInitTutorialWindow(const Handle(AIS_InteractiveContext)& context) override {
DisplayCircle(context);
}
void renderTutorialContent(const Handle(AIS_InteractiveContext)& context) override {
const char* items[] = {
"Construct from Center/Normal/Radius",
"Construct from 3 Points",
"Extract Circle Parameters",
"Show Circle Normal Arrow",
"Show Circle Plane"
};
ImGui::Combo("Circle Tutorial Mode", &tutorialModeIndex, items, IM_ARRAYSIZE(items));
if (tutorialModeIndex == 0){
if (ImGui::SliderFloat3("Center", center.data(), -100.f, 100.f)) {}
if (ImGui::SliderFloat3("Normal", normal.data(), -1.f, 1.f)) {}
if (ImGui::SliderFloat("Radius", &radius, 0.1f, 100.f)) {}
}
if (tutorialModeIndex == 1) {
if (ImGui::SliderFloat3("P1", point1.data(), -100.f, 100.f)) {}
if (ImGui::SliderFloat3("P2", point2.data(), -100.f, 100.f)) {}
if (ImGui::SliderFloat3("P3", point3.data(), -100.f, 100.f)) {}
if (ImGui::SliderFloat("Angle (deg)", &angleDeg, 0, 360)) {}
}
if (tutorialModeIndex == 2) {
ImGui::Text("Center: (%.2f, %.2f, %.2f)", m_circ.Location().X(), m_circ.Location().Y(), m_circ.Location().Z());
ImGui::Text("Normal: (%.2f, %.2f, %.2f)", m_circ.Axis().Direction().X(), m_circ.Axis().Direction().Y(), m_circ.Axis().Direction().Z());
ImGui::Text("Radius: %.2f", m_circ.Radius());
}
if (ImGui::Button("Display")) {
DisplayCircle(context);
}
}
private:
int tutorialModeIndex = 0;
gp_Circ m_circ;
std::array<float, 3> center{ 0.f, 0.f, 0.f };
std::array<float, 3> normal{ 0.f, 0.f, 1.f };
float radius = 10.0f;
std::array<float, 3> point1{ 0.f, 0.f, 0.f };
std::array<float, 3> point2{ 10.f, 0.f, 0.f };
std::array<float, 3> point3{ 0.f, 10.f, 0.f };
float angleDeg = 45.0f;
Handle(AIS_Shape) mainShape;
std::vector<Handle(AIS_Shape)> auxShapes;
void DisplayCircle(const Handle(AIS_InteractiveContext)& context) {
ClearAuxShapes(context);
if (!mainShape.IsNull()) {
context->Remove(mainShape, Standard_False);
mainShape.Nullify();
}
try {
switch (tutorialModeIndex) {
case 0: { // Center + Normal + Radius
gp_Pnt ctr(center[0], center[1], center[2]);
gp_Dir dir(normal[0], normal[1], normal[2]);
m_circ = gp_Circ(gp_Ax2(ctr, dir), radius);
break;
}
case 1: { // From 3 points
gp_Pnt p1(point1[0], point1[1], point1[2]);
gp_Pnt p2(point2[0], point2[1], point2[2]);
gp_Pnt p3(point3[0], point3[1], point3[2]);
// 替代方式:从三点构造圆
GC_MakeCircle mkCirc(p1, p2, p3);
if (!mkCirc.IsDone()) {
ImGui::TextColored(ImVec4(1, 0, 0, 1), "Failed to create circle from 3 points.");
return;
}
m_circ = mkCirc.Value()->Circ();
break;
}
case 2: { // Extract Params
gp_Pnt ctr = m_circ.Location();
gp_Dir dir = m_circ.Axis().Direction();
double radius = m_circ.Radius();
center = { (float)ctr.X(), (float)ctr.Y(), (float)ctr.Z() };
normal = { (float)dir.X(), (float)dir.Y(), (float)dir.Z() };
this->radius = (float)radius;
break;
}
case 3: { // Show normal arrow
gp_Pnt ctr = m_circ.Location();
gp_Dir dir = m_circ.Axis().Direction();
gp_Pnt end = ctr.Translated(dir.XYZ() * 15);
DisplayAuxLine(context, ctr, end, Quantity_NOC_RED, 2.0);
break;
}
case 4: { // Show Circle Plane
gp_Pnt ctr = m_circ.Location();
gp_Dir dir = m_circ.Axis().Direction();
gp_Pln pln(ctr, dir);
Handle(Geom_Plane) gpln = new Geom_Plane(pln);
Handle(AIS_Shape) face = new AIS_Shape(BRepBuilderAPI_MakeFace(gpln->Pln(), -20, 20, -20, 20).Shape());
face->SetTransparency(0.7);
face->SetColor(Quantity_NOC_YELLOW);
context->Display(face, Standard_True);
auxShapes.push_back(face);
break;
}
}
Handle(Geom_Circle) geomCirc = new Geom_Circle(m_circ);
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(geomCirc);
mainShape = new AIS_Shape(edge);
mainShape->SetColor(Quantity_NOC_BLUE1);
context->Display(mainShape, Standard_True);
}
catch (...) {
ImGui::TextColored(ImVec4(1, 0, 0, 1), "Error constructing or displaying circle.");
}
}
void ClearAuxShapes(const Handle(AIS_InteractiveContext)& context) {
for (auto& s : auxShapes) context->Remove(s, Standard_False);
auxShapes.clear();
}
Handle(AIS_Shape) DisplayAuxLine(
const Handle(AIS_InteractiveContext)& context,
const gp_Pnt& a,
const gp_Pnt& b,
Quantity_NameOfColor color = Quantity_NOC_RED,
float width = 2.0f) {
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(a, b);
Handle(AIS_Shape) ais = new AIS_Shape(edge);
ais->SetColor(color);
ais->SetWidth(width);
context->Display(ais, Standard_False);
auxShapes.push_back(ais);
return ais;
}
};
254

被折叠的 条评论
为什么被折叠?



