AMCAX Kernel
Geometry kernel for CAD/CAE/CAM
九韶内核 1.0.0.0
载入中...
搜索中...
未找到
brep 结构基础

BRep结构

边界表达(Boundary Representation,简称BRep)是当前最流行的数据结构。BRep可以在减轻存储压力的同时,保持完备性和无歧义。BRep使用实体边界上的点、线、面来表示实体,实体的体信息由包含在边界内的三维空间导出。其两个基本元素为:几何信息(Point、Curve、Surface);拓扑信息(Vertex、Edge、Face连接关系)。

拓扑结构

定义

我们先给出一个BRep结构的各级拓扑结构的定义:

  • Vertex 是拓扑意义上的点,对应几何上的 Point
  • Edge 是拓扑意义上的边,对应几何上的 Curve 以及两个约束 Curve 边界的 Vertex
  • Wire 是一些首尾相连的 Edge,相邻 Edge 共用 Vertex
  • Face 是拓扑意义上的面,对应几何上的 Surface 以及约束 Surface 边界的若干 Closed Wire
  • Shell 是一些相连的 Face,相邻 Face 要共用 1 个或多个 Edge
  • Solid 是被 Closed Shell 围成的空间
  • CompSolid 是一些 Solid
  • Compound 是若干拓扑结构的集合

常见例子

接下来我们将举一些例子,方便您更好的理解。

一个曲面构成的Face

不难看出,这个例子有 4 个 Vertex,4 个 Edge,1 个 Wire,1 个 Face。

两个相邻的曲面构成的Shell

因为中间重合部分的 Vertex 和 Edge 是共用的,所以这个例子有 6 个 Vertex,7 个 Edge,2 个 Wire,2 个 Face,1 个 Shell。

正方体

这个例子有 8 个 Vertex,12 个 Edge,6 个 Wire,6 个 Face,1 个 Shell。

圆柱

这个例子有 2 个 Vertex,3 个 Edge,3 个 Wire,3 个 Face,1 个 Shell。而 0 号 Edge 在 Wire 中被复用了,所以这是一条经典的 Seam Edge。

这个例子有 2 个 Vertex,3 个 Edge,1 个 Wire,1 个 Face,1 个 Shell。在这个例子中不仅有 Seam Edge,还有两条没有长度的Edge。事实上,这两条 Edge 尽管在 3D 中没有长度,但是在参数域上是有长度的,所以这是两条经典的 Degenerated Edge(退化边)。

Edge

接下来,我们将介绍比较常见的几种 Edge。

普通Edge

在一个封闭的 TopoShape(这里指 Shell、Solid)内,通常来说最常见的 Edge 是两个不同 Face 的邻接 Edge。普通 Edge 属于两个不同的 Face,且长度不为 0。

Seam Edge

一个常见的特例是圆柱的侧边,这条 Edge 被一个 Face 所有,且在 Face 的一个 Wire 中出现两次(一次正,一次反)。Seam Edge 是合法的。

Degenerated Edge(退化边)

一个常见的特例是球的顶边,这条 Edge 长度为 0,退化成了一个点。退化 Edge 是合法的。

自由 Edge

只属于 1 个 Face,且在 Face 的 1 个 Wire 中只出现一次的 Edge 称为自由 Edge。自由 Edge 在封闭 TopoShape 中是非法的。

非流形 Edge

在 1 个 Shape 或 Solid 中,属于超过 2 个 Face 的 Edge 称为非流形 Edge。非流形 Edge 是非法的。

拓扑结构的遍历

TopoShape 的树状结构

首先我们先来介绍一下 TopoShape 的树状结构,如下图所示:

遍历

对一个拓扑结构进行遍历是十分常见的操作,我们内核提供两种方式: AMCAX::TopoExplorerAMCAX::WireExplorer ,接下来我们将对这两种方式进行逐一介绍。

TopoExplorer

AMCAX::TopoExplorer 可以实现对 TopoShape 中的指定类型( Shape , Compound , CompSolid , Solid , Shell , Face , Wire , Edge , Vertex)按照树状结构进行先序遍历,找到指定类型的子 shape。比如,遍历一个 TopoShape 的面可以用如下代码:

for (AMCAX::TopoExplorer ex(shape, AMCAX::ShapeType::Face); ex.More(); ex.Next())
{
const AMCAX::TopoShape& crshape = ex.Current();
const AMCAX::TopoFace& crface = AMCAX::TopoCast::Face(crshape);
}
static AMCAX_API const TopoFace & Face(const TopoShape &s)
Cast shape to face
Class of a tool for exploring the B-Rep structure
定义 TopoExplorer.hpp:14
AMCAX_API bool More() const
Does the explorer have more shapes
Class of face
定义 TopoFace.hpp:12
Base class of shape, containing an underlying shape with a location and an orientation
定义 TopoShape.hpp:15

需要注意的是, AMCAX::TopoExplorer 不会考虑重复性(IsSame级别的“相等”),也就是说,如果一条 Edge 被两个 Face 共用,那 AMCAX::TopoExplorer 仍然会遍历这条 Edge 两次。如果不希望遍历两次,则可以使用一个 std::unordered_set 来去重,代码如下:

std::unordered_set<AMCAX::TopoShape> faceSet;
for (AMCAX::TopoExplorer ex(shape, AMCAX::ShapeType::Face); ex.More(); ex.Next())
{
const AMCAX::TopoShape& crshape = ex.Current();
if (faceSet.find(crshape) != faceSet.end())
{
continue;
}
const AMCAX::TopoFace& crface = AMCAX::TopoCast::Face(crshape);
}

在上文中,我们提到了重复性,重复性其实隐含了“相等”的定义。接下来我们将介绍关于 TopoShape 三种层次的“相等”,严格程度逐渐增加:

  • IsPartner:TopoTshape 一致
  • IsSame:TopoTshape 和 TopoLocation 一致
  • IsEqual:TopoTshape 和 TopoLocation,Orientation 一致
  • IndexSet:默认是 IsSame 级别

WireExplorer

不难发现,当我们需要对某一个 Wire 的 Edge 按顺序遍历时, AMCAX::TopoExplorer 其实是不能满足需求的。这时, AMCAX::WireExplorer 可以满足这样的需求,不过遍历速度会变慢。

for (AMCAX::WireExplorer ex(wire); ex.More(); ex.Next())
{
const AMCAX::TopoEdge& edge = ex.Current();
}
Class of edge
定义 TopoEdge.hpp:12
Class of wire
定义 TopoWire.hpp:12
Class of tool for exploring wire
定义 WireExplorer.hpp:20
AMCAX_API bool More() const
Does the explorer have more edges

获取拓扑信息

序号

由于树状结构和先序遍历的存在,事实上每条 Edge(其他的 Vertex、Wire、Face 也是类似的)都有自己的序号。而 AMCAX::TopoExplorerTool 类提供了一些快捷的遍历操作, AMCAX::TopoExplorerTool::MapShapes 可以获得某类拓扑结构的有序号集合(如 Edge),并且得到的集合是去重的。

AMCAX::TopoShape box = AMCAX::MakeBox(AMCAX::Point3(-5.0, -5.0, 0.0), AMCAX::Point3(5.0, 5.0, 3.0));
AMCAX::TopoExplorerTool::MapShapes(box, AMCAX::ShapeType::Edge, edgeIndexSet);
AMCAX::TopoEdge edge1 = static_cast<const AMCAX::TopoEdge&>(edgeIndexSet[3]);
//get id
int edgeid = edgeIndexSet.index(edge1);
std::cout << edgeid << std::endl;
索引集的模板类
定义 IndexSet.hpp:20
int index(const key_type &key) const
获取给定键的索引
定义 IndexSet.hpp:213
Class of making a box
定义 MakeBox.hpp:18
static AMCAX_API void MapShapes(const TopoShape &s, ShapeType t, IndexSet< TopoShape > &shapeSet)
Construct a set of sub-shapes of given type
PointT< double, 3 > Point3
三维点
定义 PointT.hpp:459

事实上,这个序号和在 FreeCAD 中左下角显示的序号是一致的(FreeCAD 从 1 开始,而我们内核从 0 开始)。

所属Face

AMCAX::TopoExplorerTool::MapShapesAndAncestors 可以获取子 shape 到所属的父 shape 的 Map,而 AMCAX::TopoExplorerTool::MapShapesAndUniqueAncestors 同样可以获取 Map,两者的区别在于当输入 seam 边时,MapShapesAndUniqueAncestors 会返回 1 个所属 face,而 MapShapesAndAncestors 会返回 2 个所属 face。比如 Edge 到所属 Face 的 Map 可以这样获得:

AMCAX::TopoShape cylinder = AMCAX::MakeCylinder(AMCAX::Frame3(AMCAX::Point3(0.0, 0.0, 0.0), AMCAX::Direction3(0.0, 0.0, 1.0)), 3.0, 5.0);
//MapShapesAndAncestors
AMCAX::TopoExplorerTool::MapShapesAndAncestors(cylinder, AMCAX::ShapeType::Edge, AMCAX::ShapeType::Face, edgeFaceMap);
//MapShapesAndUniqueAncestors
AMCAX::TopoExplorerTool::MapShapesAndUniqueAncestors(cylinder, AMCAX::ShapeType::Edge, AMCAX::ShapeType::Face, edgeFaceMap2);
索引映射的模板类
定义 IndexMap.hpp:21
Class of making a cylinder
定义 MakeCylinder.hpp:16
static AMCAX_API void MapShapesAndUniqueAncestors(const TopoShape &s, ShapeType ts, ShapeType ta, IndexMap< TopoShape, std::list< TopoShape > > &shapeMap, bool useOrientation=false)
Construct a map from sub-shapes of given type to all the unique ancestor shapes of given type
static AMCAX_API void MapShapesAndAncestors(const TopoShape &s, ShapeType ts, ShapeType ta, IndexMap< TopoShape, std::list< TopoShape > > &shapeMap)
Construct a map from sub-shapes of given type to all the ancestor shapes of given type
DirectionT< double, 3 > Direction3
三维方向
定义 DirectionT.hpp:587
FrameT< double, 3 > Frame3
三维标架
定义 FrameT.hpp:885

退化边

判断一条 Edge 是否是退化的是非常重要的,比如我们不应当在一条退化边上进行采样。我们可以通过 AMCAX::TopoTool::Degenerated 来判断。

AMCAX::TopoShape sphere = AMCAX::MakeSphere(AMCAX::Point3(-3.0, 0.0, 0.0), 1.0);
AMCAX::TopoExplorerTool::MapShapes(sphere, AMCAX::ShapeType::Edge, edgeSet);
std::cout << AMCAX::TopoTool::Degenerated(de)<<std::endl;
Class of making a sphere
定义 MakeSphere.hpp:16
static AMCAX_API const TopoEdge & Edge(const TopoShape &s)
Cast shape to edge
static AMCAX_API bool Degenerated(const TopoEdge &e)
Get the degenerated flag of an edge

Seam 边

可以通过 AMCAX::TopoTool::IsClosed 来判断一条 Edge 是否为 Seam 边。

AMCAX::TopoShape cylinder = AMCAX::MakeCylinder(AMCAX::Frame3(AMCAX::Point3(0.0, 0.0, 0.0), AMCAX::Direction3(0.0, 0.0, 1.0)), 3.0, 5.0);
AMCAX::TopoExplorerTool::MapShapes(cylinder, AMCAX::ShapeType::Edge, edgeSet2);
AMCAX::TopoEdge edge22 = AMCAX::TopoCast::Edge(edgeSet2[1]);
AMCAX::TopoExplorerTool::MapShapes(cylinder, AMCAX::ShapeType::Face, faceSet2);
AMCAX::TopoFace face11 = AMCAX::TopoCast::Face(faceSet2[0]);
std::cout << AMCAX::TopoTool::IsClosed(edge22, face11) << std::endl;
static AMCAX_API bool IsClosed(const TopoShape &s)
Is the shape closed

获取拓扑结构中的几何

Point

Vertex 中只有一个 Point,表示三维空间中的点。而从 Vertex 中获取 Point 的方法为 AMCAX::TopoTool::Point

AMCAX::TopoShape box = AMCAX::MakeBox(AMCAX::Point3(-5.0, -5.0, 0.0), AMCAX::Point3(5.0, 5.0, 3.0));
AMCAX::TopoExplorerTool::MapShapes(box, AMCAX::ShapeType::Vertex, vertexSet);
static AMCAX_API const TopoVertex & Vertex(const TopoShape &s)
Cast shape to vertex
static AMCAX_API Point3 Point(const TopoVertex &v)
Get the point of a vertex

Curve 与 PCurve

curve3d 是 3 维参数曲线。pcurve是 2 维参数曲线。pcurve 是与 <Edge, Surface, Location> 绑定的,即 Edge, Surface, Location 是一条 pcurve 的索引。

Edge 与曲线

一个 Edge 中通常有一条 curve3d,两条 pcurve。事实上,通过 pcurve 和 surface 还可以构建一条 3 维参数曲线,但由于 Tolerance 的存在,所以创建出来的两条 3 维参数曲线与 curve3d 基本不会重合。在几何建模的过程中,Edge 中的 Curve 数量没有要求,没有 Curve 也没有问题。但除特殊情况外,最终的几何模型应当包含 1 条 curve3d 和 2 条 pcurve。

接下来我们将介绍几种特殊情况:

Geom3Plane 上的 Edge

如果 Edge 位于一个平面(Geom3Plane)上,则不需要额外的 pcurve。因为平面上的 Edge 可以直接通过 Curve3d 来表示。

Seam Edge

Seam Edge 有 1 条 curve3d 和两条 pcurve,这两条 pcurve 都在 1 个 Face 上,两条 pcurve 的区别在于 Edge 的 Orientation:一条 pcurve 对应 Edge 的正向,另一条 pcurve 对应 Edge 的反向。

Degenerated Edge

Degenerated Edge 只有 1 条 pcurve,没有 curve3d。

自由 Edge(不属于任何面的边)

自由 Edge 有 1 条 curve3d,1 条 pcurve。

需要注意的是,内核不提供为 curve3d 计算 pcurve 的功能。正确的建模方式应当是先构建 pcurve,然后用 pcurve 和 surface 计算 curve3d:

AMCAX::TopoShape box = AMCAX::MakeBox(AMCAX::Point3(-5.0, -5.0, 0.0), AMCAX::Point3(5.0, 5.0, 3.0));
static AMCAX_API bool BuildCurves3d(const TopoShape &s)
Build 3d curves for all the edges in a shape

获取 Edge 的 Curve

Curve3d

获取 Edge 的 Curve3d 的方法是 AMCAX::TopoTool::Curve

AMCAX::TopoShape box = AMCAX::MakeBox(AMCAX::Point3(-5.0, -5.0, 0.0), AMCAX::Point3(5.0, 5.0, 3.0));
AMCAX::TopoExplorerTool::MapShapes(box, AMCAX::ShapeType::Edge, edgeSet);
double fp, lp;
const std::shared_ptr<AMCAX::Geom3Curve>& curve3d = AMCAX::TopoTool::Curve(AMCAX::TopoCast::Edge(edgeSet[0]), loc3, fp, lp);
Class of local transformation representing location of entities
定义 TopoLocation.hpp:14
static AMCAX_API const std::shared_ptr< Geom3Curve > & Curve(const TopoEdge &e, TopoLocation &l, double &first, double &last)
Get the curve of an edge with its location and bounds

有几点需要注意:
1.接口返回的是原始的曲线,或者说,如果一条 Edge 是一个 Geom3Line 和两个 Vertex 构成的线段,那么这个接口获取到的是一条无限长的直线(Geom3Line),fp 和 lp 是两个 Vertex 在曲线上的参数。

2.AMCAX::TopoTool::Curve 有重载函数,一个输入 loc 而另一个没有。输入 loc 的接口得到的一定是指向 edge 中曲线内存的一个指针,而没输入 loc 的接口,在 edge 的 location 为空时,返回指向内存的指针,非空时返回指向一条新构建曲线的指针。

3.如果输入的是一个退化边,会返回空指针。

4.输入 loc 的接口获得的 Curve 和实际的位置差了一个 location,而对 Curve 直接应用 location.Transformation() 是禁止的,这会改变 edge 的内容。正确的做法是,复制一份 Curve 再对 CopyCurve 进行变换:

std::shared_ptr<AMCAX::Geom3Curve> CopyCurve = std::dynamic_pointer_cast<AMCAX::Geom3Curve>(curve3d->Copy());
CopyCurve->Transform(loc3.Transformation());// apply the location to the copy curve
AMCAX_API const Transformation3 & Transformation() const
Get the composite transformation

pcurve

获取 pcurve 的方法是 AMCAX::TopoTool::CurveOnSurface

AMCAX::TopoShape cylinder = AMCAX::MakeCylinder(AMCAX::Frame3(AMCAX::Point3(0.0, 0.0, 0.0), AMCAX::Direction3(0.0, 0.0, 1.0)), 3.0, 5.0);
AMCAX::TopoExplorerTool::MapShapes(cylinder, AMCAX::ShapeType::Edge, edgeSet2);
AMCAX::TopoExplorerTool::MapShapes(cylinder, AMCAX::ShapeType::Face, faceSet2);
double fp2=0., lp2=0.;
std::shared_ptr<AMCAX::Geom2Curve> pcurve = AMCAX::TopoTool::CurveOnSurface(AMCAX::TopoCast::Edge(edgeSet2[0]), AMCAX::TopoCast::Face(faceSet2[0]), fp2, lp2);
Class of making a 2D edge
定义 MakeEdge2d.hpp:22
static AMCAX_API bool Write(const TopoShape &s, std::ostream &os, int format=3)
Write a shape to a stream
static AMCAX_API std::shared_ptr< Geom2Curve > CurveOnSurface(const TopoEdge &e, const TopoFace &f, double &first, double &last, const std::shared_ptr< bool > &isStored=nullptr)
Get the pcurve of an edge on a given face

需要注意的是,如果输入的是 seam 边,需要特殊处理,根据 Edge 的 Orientation 会返回不同的 pcurve:

AMCAX::TopoShape cylinder = AMCAX::MakeCylinder(AMCAX::Frame3(AMCAX::Point3(0.0, 0.0, 0.0), AMCAX::Direction3(0.0, 0.0, 1.0)), 3.0, 5.0);
AMCAX::TopoExplorerTool::MapShapes(cylinder, AMCAX::ShapeType::Edge, edgeSet2);
AMCAX::TopoExplorerTool::MapShapes(cylinder, AMCAX::ShapeType::Face, faceSet2);
double fp3=0., lp3=0.;
std::shared_ptr<AMCAX::Geom2Curve> pcurve2 = AMCAX::TopoTool::CurveOnSurface(AMCAX::TopoCast::Edge(edgeSet2[1]), AMCAX::TopoCast::Face(faceSet2[0]), fp3, lp3);
std::cout << pcurve2->Value(fp3) << ";" << pcurve2->Value(lp3) << std::endl;
double fp4 = 0., lp4 = 0.;
AMCAX::OrientationType reverseOrientation = AMCAX::TopoTool::Complement(AMCAX::TopoCast::Edge(edgeSet2[1]).Orientation());
AMCAX::TopoEdge reversedEdge = AMCAX::TopoCast::Edge(edgeSet2[1].Oriented(reverseOrientation));
std::shared_ptr<AMCAX::Geom2Curve> pcurve3 = AMCAX::TopoTool::CurveOnSurface(reversedEdge, AMCAX::TopoCast::Face(faceSet2[0]), fp4, lp4);
std::cout << pcurve3->Value(fp4) << ";" << pcurve3->Value(lp4) << std::endl;
static AMCAX_API OrientationType Complement(const OrientationType &o)
Get the complement of an orientation
OrientationType
Type of orientations
定义 TopologyMacros.hpp:11

在上面的示例中,获取了圆柱 seam 边的两条 pcurve ,这两条 pcurve 的朝向始终一致,与 curve3d 的朝向一致,这不需要考虑 edge 的朝向。

Surface

一个 Face 中只有一个 Surface。获取 Surface 的方式是 AMCAX::TopoTool::Surface

AMCAX::TopoShape box = AMCAX::MakeBox(AMCAX::Point3(-5.0, -5.0, 0.0), AMCAX::Point3(5.0, 5.0, 3.0));
AMCAX::TopoExplorerTool::MapShapes(box, AMCAX::ShapeType::Face, faceSet);
std::shared_ptr< AMCAX::Geom3Surface > surface1 = AMCAX::TopoTool::Surface(AMCAX::TopoCast::Face(faceSet[0]), loc);
std::shared_ptr< AMCAX::Geom3Surface > surface2 = AMCAX::TopoTool::Surface(AMCAX::TopoCast::Face(faceSet[0]));
static AMCAX_API const std::shared_ptr< Geom3Surface > & Surface(const TopoFace &f, TopoLocation &l)
Get the surface and the location of the surface from a face

输入 loc 的接口获得的 Surface 和实际的位置差了一个 location,而对 Surface 直接应用 location.Transformation() 是禁止的,这会改变 face 的内容。正确的做法是,复制一份 Surface 再对 CopySurface 进行变换:

std::shared_ptr<AMCAX::Geom3Surface> CopySurface = std::dynamic_pointer_cast<AMCAX::Geom3Surface>(surface1->Copy());
CopySurface->Transform(loc.Transformation());// apply the location to the copy surface
virtual AMCAX_API std::shared_ptr< Geom3Geometry > Copy() const =0
获取拷贝的几何对象

Mesh

Mesh 指的是三角网格。Mesh 在 CAD 中的存在意义是渲染。由于 OpenGL 的绘制基本单元是点、线段、三角形,因此曲线必须离散成多边形进行绘制,曲面必须离散成 Mesh 进行绘制,因此在包含图形用户界面(GUI)的应用程序中,看到的 BRep 模型通常是通过三角网格来可视化的。如下图所示。

显然,离散会带来误差,这意味着在包含 GUI 的应用程序中显示的 BRep 模型并不是模型本身,而是模型的 Mesh,二者之间的误差由离散精度,即网格的疏密程度决定,考虑到 CAD 模型的 Mesh 生成必须非常快,Mesh 的精度可想而知并不高。

由离散带来的问题是非常多的,如通过视觉判断两个事实上相同的圆是否重叠,或两个相同的球是否重叠,就会出现下图所示的情况。或者将光滑曲面渲染成出现褶皱的情况。另一类问题是网格生成失败,这会导致存在破洞。因此看到模型上有洞,需要综合考量 Mesh 的原因和模型本身的原因。

接下来我们将介绍 Mesh 的生成、获取、读写方法。

AMCAX::TopoShape box = AMCAX::MakeBox(AMCAX::Point3(-5.0, -5.0, 0.0), AMCAX::Point3(5.0, 5.0, 3.0));
AMCAX::TopoExplorerTool::MapShapes(box, AMCAX::ShapeType::Face, faceSet);
AMCAX::BRepMeshIncrementalMesh mesher(box, 0.005, true);
const auto& mesh = AMCAX::TopoTool::Triangulation(AMCAX::TopoCast::Face(faceSet[0]), loc2);
std::cout<<AMCAX::OBJTool::WriteShape(box, "./mesh.obj")<<std::endl;
auto mesh2 = AMCAX::OBJTool::ReadFile("./mesh.obj");
Class of meshing
定义 BRepMeshIncrementalMesh.hpp:16
static AMCAX_API std::shared_ptr< TriangularMesh > ReadFile(const std::string &file)
从文件读取网格
static AMCAX_API bool WriteShape(const TopoShape &shape, const std::string &file, bool divideGroup=true)
将形状中的网格写入文件
static AMCAX_API const std::shared_ptr< TriangularMesh > & Triangulation(const TopoFace &f, TopoLocation &l, unsigned char purpose=0)
Get the triangular mesh from a face and the location of the face under a given purpose

虽然 Mesh 对于渲染而言是必要的,但不一定要在模型构建后使用内核为模型生成 Mesh ,因为包含 GUI 的软件会自行生成 Mesh,进而用于渲染。渲染中的法向是一个关键信息,如果 Mesh 不提供法向,渲染时会根据 Mesh 自动计算一个近似的法向。如果需要确保三角网格的法向和 CAD 模型的法向是一致的,则可以使用 AMCAX::MakeShapeTool::EnsureNormalConsistency()

TopoShape 的构建:MakeShape

快捷创建拓扑结构是一个基础需求,而 AMCAX::MakeVertexAMCAX::MakeEdgeAMCAX::MakeWireAMCAX::MakeFace 等提供了不同拓扑结构的快捷创建方式,这是非常常用也是非常重要的功能。

MakeVertex

创建 Vertex 可以通过一个 Point3 来创建:

AMCAX::Point3 p1(1.,2.,3.);
Class of making a vertex
定义 MakeVertex.hpp:16
Class of vertex
定义 TopoVertex.hpp:12

MakeEdge

AMCAX::MakeEdge 可以根据 Curve 快捷创建 Edge。如果 MakeEdge 的输入中没有 Vertex,那么 MakeEdge 会自动构建两个 Vertex。这意味着,如果想要用 MakeEdge 构建两条相连的 Edge,务必注意 Vertex 的共用问题,简单地构建只会得到 2 个 Edge 和 4 个 Vertex。此外,退化边也是可以用 MakeEdge 构建的。MakeEdge 功能众多,我们将分类介绍:

简单构建

可以通过两个 Point3 构建线段 Edge,也可以用两个 TopoVertex 构建。需要注意的是两个 Point3/TopoVertex 的距离要大于 AMCAX::Precision::Confusion(),否则就会被判断为同一个 Point3/TopoVertex。

AMCAX::Point3 p1(0.,0.,0.);
AMCAX::Point3 p2(1.0, 0.0, 0.0);
Class of making an edge
定义 MakeEdge.hpp:24

基于数学表达构建

比如可以基于 Circle3,Line3 等数学表达构建,以下以 Line3 为例:

AMCAX::Point3 p1(0.,0.,0.);
AMCAX::Point3 p2(1.0, 0.0, 0.0);
AMCAX::Line3 line(p1, AMCAX::Direction3(1.0, 0.0, 0.0));
AMCAX::TopoEdge edge3 = AMCAX::MakeEdge(line); // infinite length for infinite curve!
AMCAX::TopoEdge edge4 = AMCAX::MakeEdge(line, 0.0, 1.0); // set parameter range
AMCAX::TopoEdge edge5 = AMCAX::MakeEdge(line, p1, p2); // set first and last point
AMCAX::TopoEdge edge6 = AMCAX::MakeEdge(line, v1, v2); // set first and last vertex
LineS< 3 > Line3
三维直线
定义 LineT.hpp:457

另外 MakeEdge 会根据数学表达自动调整参数、Point3、Vertex 的顺序,以及 Vertex 的 Orientation:

AMCAX::TopoEdge edge7 = AMCAX::MakeEdge(line, 1.0, 0.0); // same as MakeEdge(line, 0.0, 1.0)
AMCAX::TopoEdge edge8 = AMCAX::MakeEdge(line, p2, p1); // same as MakeEdge(line, p1, p2)
AMCAX::TopoEdge edge9 = AMCAX::MakeEdge(line, v2, v1); // same as MakeEdge(line, v1, v2)

基于curve 3d 构建

这是从 curve 构建 Edge 的重要功能。在这里 MakeEdge 仍然会自动调整参数、Point3、Vertex 的顺序,以及 Vertex 的 Orientation。构建方法如下:

AMCAX::Point3 p1(0.,0.,0.);
AMCAX::Point3 p2(1.0, 0.0, 0.0);
std::shared_ptr<AMCAX::Geom3Curve> curve = std::make_shared<AMCAX::Geom3Line>(line);
AMCAX::TopoEdge edge11 = AMCAX::MakeEdge(curve, 0.0, 1.0);
AMCAX::TopoEdge edge12 = AMCAX::MakeEdge(curve, p1, p2);
AMCAX::TopoEdge edge13 = AMCAX::MakeEdge(curve, p1, p2, 0.0, 1.0);
AMCAX::TopoEdge edge14 = AMCAX::MakeEdge(curve, v1, v2);
AMCAX::TopoEdge edge15 = AMCAX::MakeEdge(curve, v1, v2, 0.0, 1.0);

基于 pcurve 构建

需要注意的是基于 pcurve 和 Surface 构建的 Edge 是没有 curve3d 的。构建方法如下:

AMCAX::Point3 p1(0.,0.,0.);
AMCAX::Point3 p2(1.0, 0.0, 0.0);
std::shared_ptr<AMCAX::Geom2Curve> pcurve = std::make_shared<AMCAX::Geom2Line>(); // ox2d
std::shared_ptr<AMCAX::Geom3Surface> surf = std::make_shared<AMCAX::Geom3Plane>(); // xoy
AMCAX::TopoEdge edge16 = AMCAX::MakeEdge(pcurve, surf);
AMCAX::TopoEdge edge17 = AMCAX::MakeEdge(pcurve, surf, 0.0, 1.0);
AMCAX::TopoEdge edge18 = AMCAX::MakeEdge(pcurve, surf, p1, p2);
AMCAX::TopoEdge edge19 = AMCAX::MakeEdge(pcurve, surf, p1, p2, 0.0, 1.0);
AMCAX::TopoEdge edge20 = AMCAX::MakeEdge(pcurve, surf, v1, v2);
AMCAX::TopoEdge edge21 = AMCAX::MakeEdge(pcurve, surf, v1, v2, 0.0, 1.0);

MakeWire

构建一个wire可以通过以下两种方式:

单独添加 Edge

AMCAX::TopoEdge e1 = AMCAX::MakeEdge(AMCAX::Point3(-4., -1.0, 0.0), AMCAX::Point3(4., -1.0, 0.0));
AMCAX::TopoEdge e2 = AMCAX::MakeEdge(AMCAX::Point3(4., -1.0, 0.0), AMCAX::Point3(4., 1.0, 0.0));
AMCAX::TopoEdge e3 = AMCAX::MakeEdge(AMCAX::Point3(4., 1.0, 0.0), AMCAX::Point3(-4., 1.0, 0.0));
AMCAX::TopoEdge e4 = AMCAX::MakeEdge(AMCAX::Point3(-4., 1.0, 0.0), AMCAX::Point3(-4., -1.0, 0.0));
AMCAX::MakeWire makewire;
makewire.Add(e1);
makewire.Add(e2);
makewire.Add(e3);
makewire.Add(e4);
AMCAX::TopoWire w = makewire.Wire();
//Error
AMCAX::MakeWire makewire2;
makewire.Add(e1);
makewire.Add(e3);
makewire.Add(e2);
makewire.Add(e4);
std::cout<<makewire2.IsDone()<<std::endl;
bool r = makewire2.Error() == AMCAX::WireError::EmptyWire;
std::cout << r << std::endl;
Class of making a wire
定义 MakeWire.hpp:17
AMCAX_API const TopoWire & Wire()
Get the constructed wire
AMCAX_API void Add(const TopoEdge &e)
Add an edge to the wire
AMCAX_API WireError Error() const
Get the error status
AMCAX_API bool IsDone() const override
Is the construction algorithm done
@ EmptyWire
No initial wire
定义 MakeShapeError.hpp:41

注:每次添加的边必须与之前已经添加的 edge 在几何上是相连的。如上面的例子,如果按照e1—e3—e2—e4的顺序进行添加,那么在添加 e3 的时候会因为不与 e1 相连导致添加不了,此时调用 IsDone() 会返回 false,调用 Error() 会返回 WireError::DisconnectedWire。如果能确定需要连成 wire 的 edge 是相连的,但不知道相连关系,那么必须使用下面一次性添加 edge list 的方式。

添加 Edge 的 list

列表中 edge 的顺序不会影响构造的结果。

AMCAX::TopoEdge e1 = AMCAX::MakeEdge(AMCAX::Point3(-4., -1.0, 0.0), AMCAX::Point3(4., -1.0, 0.0));
AMCAX::TopoEdge e2 = AMCAX::MakeEdge(AMCAX::Point3(4., -1.0, 0.0), AMCAX::Point3(4., 1.0, 0.0));
AMCAX::TopoEdge e3 = AMCAX::MakeEdge(AMCAX::Point3(4., 1.0, 0.0), AMCAX::Point3(-4., 1.0, 0.0));
AMCAX::TopoEdge e4 = AMCAX::MakeEdge(AMCAX::Point3(-4., 1.0, 0.0), AMCAX::Point3(-4., -1.0, 0.0));
std::list<AMCAX::TopoShape> elist = { e1, e3, e2, e4 };
AMCAX::MakeWire makewire3;
makewire.Add(elist);
AMCAX::TopoWire w3 = makewire.Wire();

注:MakeWire 能够构建不包含 Seam Edge、退化边的 Wire,支持单独添加 Edge 或添加 Edge的 list。MakeWire 不能正确构建含有退化边的wire,因为此接口会根据顶点 3D 点的几何位置来判断定向,而退化边的顶点是同一个,因此添加的退化边的定向可能不正确。MakeWire 不能正确构建含有非流形顶点的 wire,此接口要求在一个 wire 内的顶点的度数必须为2(或1,即开的 wire 端点)。

MakeFace

AMCAX::MakeFace 功能也很多,我们将分类进行介绍:

基于数学表达

可以基于 Plane,Cylinder 等数学表达构建:

AMCAX::TopoFace face1 = AMCAX::MakeFace(plane, 0.0, 1.0, 0.0, 1.0);
Class of making a face
定义 MakeFace.hpp:22
平面类
定义 Plane.hpp:13

基于Surface

基于 Geom3Surface 的 Face 构建是 MakeFace 最常用的功能:

std::shared_ptr<AMCAX::Geom3Surface> surface = std::make_shared<AMCAX::Geom3Plane>();
AMCAX::TopoFace face2 = AMCAX::MakeFace(surface, 0.0, 1.0, 0.0, 1.0, AMCAX::Precision::Confusion());
static constexpr double Confusion() noexcept
获取混淆容差
定义 Precision.hpp:122

MakeFace 要求在提供 Surface 的同时提供 Tolerance。需要注意的是,当输入的曲面为无穷大,且没有约束参数范围时,不会自动生成 Wire。其余情况都会自动生成 wire。

基于 Wire

单独 Wire

当 Wire 是平面上的 Wire 时,单独输入 Wire 构建 Face 是可以构建成功的:

AMCAX::TopoWire wire = AMCAX::MakeWire(circleEdge);
CircleS< 3 > Circle3
三维圆
定义 CircleT.hpp:178

注:输入不在平面上的 Wire 可能会构建失败。

Wire + 数学表达

注:需要提供 Face 表示的区域在 Wire 的内部还是外部(最后的 bool 参数,true 表示在内部)。

Wire + Surface

AMCAX::TopoWire wire = AMCAX::MakeWire(circleEdge);
std::shared_ptr<AMCAX::Geom3Surface> surface = std::make_shared<AMCAX::Geom3Plane>();
AMCAX::TopoFace face7 = AMCAX::MakeFace(surface, wire, true);

Wire + Face

将 Wire 添加到 Face 中:

AMCAX::TopoWire wire = AMCAX::MakeWire(circleEdge);
std::shared_ptr<AMCAX::Geom3Surface> surface = std::make_shared<AMCAX::Geom3Plane>();
AMCAX::TopoFace face7 = AMCAX::MakeFace(surface, wire, true);
AMCAX::Frame3 frame2(AMCAX::Point3(), AMCAX::Direction3(0.0, 0.0, -1.0));
AMCAX::TopoEdge circleEdge2 = AMCAX::MakeEdge(AMCAX::Circle3(frame2, 0.5));
AMCAX::TopoWire wire2 = AMCAX::MakeWire(circleEdge2);
AMCAX::TopoFace face8 = AMCAX::MakeFace(face7, wire2);

执行后,结果如下图所示:左边为 face7,右图为 face8。

但是要注意 Wire 的 Orientation,也就是说,如果这样写,便会得到错误的结果:

而修正的方式是在 Edge 或 Wire 层级将其 Orientation 设为 Reversed:

AMCAX::TopoWire wire2 = AMCAX::MakeWire(circleEdge2);
AMCAX::TopoFace face8 = AMCAX::MakeFace(face7, AMCAX::TopoCast::Wire(wire2.Oriented(AMCAX::OrientationType::Reversed)));
static AMCAX_API const TopoWire & Wire(const TopoShape &s)
Cast shape to wire
AMCAX_API TopoShape Oriented(OrientationType orient) const
Get the shape with given orientation

TopoShape的构建:TopoBuilder

尽管 Make*.hpp 提供了一些方便的构建 TopoShape 的 API,但是部分建模问题只有 TopoBuilder 能够解决。接下来我们将介绍 TopoBuilder 构建不同拓扑结构的方法。

Vertex

AMCAX::TopoBuilder::MakeVertex 提供构建一个顶点的功能。

// method 1
builder.MakeVertex(v, p, tol);
// method 2
builder.MakeVertex(v);
builder.UpdateVertex(v, p, tol);
Class of a tool for building B-Rep structure
定义 TopoBuilder.hpp:37
AMCAX_API void MakeVertex(TopoVertex &v) const
Make an empty vertex
AMCAX_API void UpdateVertex(const TopoVertex &v, const Point3 &p, double tol) const
Update the point of a vertex

Edge

针对不同类型的 Edge 有不同的构建方法。我们将逐一介绍:

普通 Edge

正常的建模思路是,通过几何计算得到了全部所需的 Point、Curve3d 和 PCurve,再构建拓扑结构:

第一步:准备好 Curve3d,根据 curve 构建 Edge。

AMCAX::MakeGeom3Line line(AMCAX::Point3(0.0, 0.0, 0.0), AMCAX::Direction3(1.0, 0.0, 0.0));
std::shared_ptr<AMCAX::Geom3Curve >curve = line.Value();
builder.MakeEdge(edge, curve, AMCAX::Precision::Confusion());
builder.Range(edge, 0.0, 2.0);
构造三维几何直线的类
定义 MakeGeom3Line.hpp:15
AMCAX_API void MakeEdge(TopoEdge &e) const
Make an empty edge
AMCAX_API void Range(const TopoEdge &e, double first, double last, bool only3d=false) const
Set the range of parameter of an edge

AMCAX::TopoBuilder::Range 设置了 TopoEdge 的参数范围,如果 curve 的参数范围与 edge 期待的参数范围一致时,这一步可以省略。

第二步:准备起始 Vertex 和终止 Vertex,为 Edge 添加 Vertex。

AMCAX::Point3 start(0.0, 0.0, 0.0);
AMCAX::Point3 last(2.0, 0.0, 0.0);
builder.MakeVertex(v1, start, tol);
builder.MakeVertex(v2, last, tol);
builder.Add(edge, v1);
//method 1
v2.SetOrientation(AMCAX::OrientationType::Reversed);
//method 2
v2.Reverse();
//method3
builder.Add(edge, v2.Oriented(AMCAX::OrientationType::Reversed));
AMCAX_API void Add(TopoShape &s, const TopoShape &c) const
Add a sub-shape to a shape
AMCAX_API void SetOrientation(OrientationType orient)
Set the orientation of shape
AMCAX_API void Reverse()
Reverse the orientation of the shape

注:在这里要注意起始 Vertex 和终止 Vertex 的 Orientation。Edge 的起始 Vertex 是 Forward,终止 Vertex 是 Reversed。 Orientation 默认是 Forward ,所以需要对终止 Vertex 的 Orientation 进行改变。改变有两种形式:一是对自身进行改变,二是不对自身进行改变,产生一个新的。其中 AMCAX::TopoShape::SetOrientationAMCAX::TopoShape::Reverse 对自身进行改变, AMCAX::TopoShape::Oriented 不对自身进行改变,而是产生一个新的,然后传入到 builder 里。

第三步:在构建好 Face 后,添加 pcurve:

AMCAX::MakeGeom3Plane mp1(AMCAX::Point3(0., 0., 0.), AMCAX::Point3(0., 2., 0.), AMCAX::Point3(0., 2., 2.));
AMCAX::MakeGeom3Plane mp2(AMCAX::Point3(0., 0., 0.), AMCAX::Point3(2., 0., 0.), AMCAX::Point3(2., 2., 0.));
AMCAX::TopoFace face1 = AMCAX::MakeFace(mp1.Value(), 0.,2.,0.,2.,AMCAX::Precision::Confusion());
AMCAX::TopoFace face2 = AMCAX::MakeFace(mp2.Value(), 0.,2.,0.,2.,AMCAX::Precision::Confusion());
std::shared_ptr< AMCAX::Geom2Curve > pcurve1;
std::shared_ptr< AMCAX::Geom2Curve > pcurve2;
builder.UpdateEdge(edge, pcurve1, face1, AMCAX::Precision::PConfusion());
builder.UpdateEdge(edge, pcurve2, face2, AMCAX::Precision::PConfusion());
构造三维几何平面的类
定义 MakeGeom3Plane.hpp:13
static constexpr double PConfusion(double t) noexcept
获取二维参数空间中的混淆容差
定义 Precision.hpp:127
AMCAX_API void UpdateEdge(const TopoEdge &e, const std::shared_ptr< Geom3Curve > &c, double tol) const
Update the curve and tolerance of an edge

要注意 pcurve 的方向应该与 curve3d 保持一致,edge 的 Orientation 对 pcurve 的方向没有任何影响,记住“几何是几何,拓扑是拓扑”。 而添加 Vertex 的步骤只要在 MakeEdge 后就可以,也就是说第二步第三步互换是没有问题的,在 Range 前添加 Vertex 也是没有问题的。

Seam Edge

其他的步骤都相同,仅第三步有所不同:

builder.UpdateEdge(edge, pcurve1, pcurve2, face, Precision::PConfusion());

要注意 pcurve1、pcurve2 的方向仍然应该与 curve3d 保持一致。两个 pcurve 的区别是:pcurve1 是 Forward edge 在 Surface 参数域上的 pcurve,pcurve2 是 Reversed edge 在 Surface 参数域上的 pcurve。

Degenerated Edge

其他的步骤都相同,仅第一步不同,没有 curve3d:

builder.MakeEdge(edge);
builder.Degenerated(edge, true);
builder.Range(edge, 0.0, 2.0 * M_PI);
AMCAX_API void Degenerated(const TopoEdge &e, bool d) const
Set the degenerate flag of an edge

在这里要注意将退化 Flag 设为 True。而 pcurve 的方向无需考虑与 curve3d 保持一致,只需与 edge 的 Orientation 保持一致即可。

自由 Edge

其他的步骤都相同,仅第三步不同,只有 1 条 pcurve。

Wire

正常的思路是:
第一步先根据参数域上的 Wire 确定 Wire 的方向,再确定好选用哪些 Edge,这些 Edge 的 Orientation是什么:

std::vector<std::pair<int, bool>> wireEdges1 =
{
{1, true},
{2, true},
{3, true},
{4, true}
};

第二步构建并向 Wire 添加 Edge:

builder.MakeWire(wire1);
for (const auto& edgeInfo : wireEdges1)
{
int edgeID = edgeInfo.first;
bool isForward = edgeInfo.second;
AMCAX::TopoShape crshape = allEdges[edgeID - 1];
if (isForward)
{
edge = AMCAX::TopoCast::Edge(crshape.Oriented(AMCAX::OrientationType::Forward));
}
else
{
edge = AMCAX::TopoCast::Edge(crshape.Oriented(AMCAX::OrientationType::Reversed));
}
builder.Add(wire1, edge);
}
AMCAX_API void MakeWire(TopoWire &w) const
Make an empty wire

这里的 Edge 的添加顺序可以是任意的。
第三步将 Wire 的 Closed Flag 设置好:

bool isClosedWire1 = AMCAX::TopoTool::IsClosed(wire1);
wire1.Closed(isClosedWire1);
AMCAX_API bool Closed() const
Is the shape closed

Face

构建 Face 的方法如下:

AMCAX::MakeGeom3Plane plane1(p1, -DZ);
builder.MakeFace(face1, plane1.Value(), AMCAX::Precision::Confusion());
builder.Add(face1, wire1);
static AMCAX_API const Direction3 & DZ() noexcept
获取三维中的 z 方向
AMCAX_API void MakeFace(TopoFace &f) const
Make an empty face

Shell

构建 Shell 时需要注意 Closed Flag,方法如下:

builder.MakeShell(shell);
std::vector<AMCAX::TopoFace> allFaces = { face1 };
for (const auto& face : allFaces)
{
builder.Add(shell, face);
}
bool isClosedShell = AMCAX::TopoTool::IsClosed(shell);
shell.Closed(isClosedShell);
AMCAX_API void MakeShell(TopoShell &s) const
Make an empty shell
Class of shell
定义 TopoShell.hpp:12

Solid

构建 Solid 的方式如下:

builder.MakeSolid(solid);
builder.Add(solid, shell);
AMCAX_API void MakeSolid(TopoSolid &s) const
Make an empty solid
Class of solid
定义 TopoSolid.hpp:12

Compound

构建 Compound 的方式如下:

builder.MakeCompound(comp);
builder.Add(comp, anyShape);
AMCAX_API void MakeCompound(TopoCompound &c) const
Make an empty compound
Class of compound
定义 TopoCompound.hpp:12

Orientation

OrientationType 共有 4 种类型:Forward,Reversed,Internal,External,其中最常用的是 Forward 和 Reversed。任何一级的 TopoShape 都可以用 SetOrientation() 来设置 Orientation,或者用 Orientation() 来获取 Orientation。如果想获得一个与当前 TopoShape 只有 Orientation 不同的 TopoShape,可以使用 Orientated() 。Orientation 是与几何、Location 独立的成员,改变任何一个不会影响其他任意成员。

Vertex

对于一条 Edge ,其起始 Vertex 的 Orientation 为 Forward,终止 Vertex 的 Orientation 为 Reversed。比如一个线段 Edge 有两个不同的 Vertex,则 Orientation 一个是 Forward,另一个是 Reversed。再比如圆构成的 Edge 有两个相同的 Vertex,但 Orientation 也是一个为 Forward,另一个为 Reversed。

auto c = std::make_shared<AMCAX::Geom3Circle>(AMCAX::Frame3(), 1.0);
v2.SetOrientation(AMCAX::OrientationType::Reversed);
// two vertex
builder.Add(edge, v1);
builder.Add(edge, v2);
std::cout << AMCAX::TopoTool::IsClosed(edge) << std::endl;//0
// one vertex
builder.Add(edge2, v1);
builder.Add(edge2, v1.Oriented(AMCAX::OrientationType::Reversed));
std::cout << AMCAX::TopoTool::IsClosed(edge2) << std::endl;//1

注:在上面这个例子中,因为圆构成的 Edge 有两个相同的 Vertex,所有只需要构造一个顶点就可以,这时 AMCAX::TopoTool::IsClosed(edge) 会返回 true。如果构造了两个顶点,那么 AMCAX::TopoTool::IsClosed(edge) 会返回 false。

Edge

Edge 的 Forward 方向是 curve3d 的方向。如果没有 curve3d 就是 pcurve 的方向。Reversed Edge 的方向与 curve 方向相反。
比如已知三条用 xoy 平面上的 3d curve 构建的 Edge,以及用自然标架构造的 Geom3Plane:

由于必须保证在参数域上,Wire 的方向是逆时针的,所以应当使用 Reversed 的 Edge0,Forward 的 Edge2,Forward 的 Edge1 构成 Wire。

如果使用了顺时针的 Wire ,即 Forward 的 Edge0,Reversed 的 Edge1,Reversed 的 Edge2,那么 Face 会定义为 Wire 不围成的部分。以下分别为逆时针和顺时针的 Wire:

如果 Geom3Plane 是用 Frame3(O, -OZ, OX) 构造的,那么 Wire 的方向应为顺时针方向,即 Forward 的 Edge0,Reversed 的 Edge1,Reversed 的 Edge2,这样就可以确保 Face 被正确定义为 Wire 围成的区域。

Wire

Wire 的 Reversed 等价于将 Forward 的 Wire 中的每个 Edge 的 Orientation 变为反向。

AMCAX::TopoShape box = AMCAX::MakeBox(AMCAX::Point3(-5.0, -5.0, 0.0), AMCAX::Point3(5.0, 5.0, 3.0));
AMCAX::TopoExplorerTool::MapShapes(box, AMCAX::ShapeType::Wire, wireSet);
AMCAX::TopoExplorerTool::MapShapes(wire1, AMCAX::ShapeType::Edge, edgeSet);
std::cout << int(edge1.Orientation()) << std::endl;//0
AMCAX::TopoExplorerTool::MapShapes(rewire, AMCAX::ShapeType::Edge, edgeSet2);
AMCAX::TopoEdge reedge = AMCAX::TopoCast::Edge(edgeSet2[0]);
std::cout << int(reedge.Orientation()) << std::endl;//1
AMCAX_API OrientationType Orientation() const
Get the orientation of shape
AMCAX_API TopoShape Reversed() const
Get the shape with reversed orientation

Face

对 Face 进行 Reversed 后, Face 所包围的方向变为曲面法向的反向。对 Face 进行 Reversed 等价于将 Forward 的 Face 中 Wire 的 Orientation 变为反向。

AMCAX::TopoShape box = AMCAX::MakeBox(AMCAX::Point3(-5.0, -5.0, 0.0), AMCAX::Point3(5.0, 5.0, 3.0));
AMCAX::TopoExplorerTool::MapShapes(box, AMCAX::ShapeType::Face, faceSet);
std::cout << int(face1.Orientation()) << std::endl;//1
AMCAX::TopoExplorerTool::MapShapes(face1, AMCAX::ShapeType::Wire, wireSet2);
std::cout << int(wire2.Orientation()) << std::endl;//1
std::cout << int(reface.Orientation()) << std::endl;//0
AMCAX::TopoExplorerTool::MapShapes(reface, AMCAX::ShapeType::Wire, rewireSet2);
AMCAX::TopoWire rewire2 = AMCAX::TopoCast::Wire(rewireSet2[0]);
std::cout << int(rewire2.Orientation()) << std::endl;//0

其中获取 Face 所在曲面的法向的方法如下:

std::shared_ptr< AMCAX::Geom3Surface > surface = AMCAX::TopoTool::Surface(face1, location);
AMCAX::AdaptorGeom3Surface adaptor(surface);
AMCAX::Plane plane = adaptor.GetPlane();
AMCAX::Frame3 frame = plane.Position();
std::cout << dir << std::endl;//1 0 -0
三维几何曲面适配器的类
定义 AdaptorGeom3Surface.hpp:16
constexpr const DirectionT< Scalar, DIM > & Direction() const noexcept
获取三维主方向(z 方向)
定义 FrameT.hpp:441
const Frame3 & Position() const
获取局部标架
定义 SurfaceBase.hpp:56

Shell

对 Shell 进行 Reversed 后 ,Shell 所包围的方向变为反向。对 Shell 进行 Reversed 等价于将 Forward 的 Shell 中的每个 Face 的 Orientation 变为反向。

AMCAX::MakeBox b(AMCAX::Point3(-5.0, -5.0, 0.0), AMCAX::Point3(5.0, 5.0, 3.0));
AMCAX::TopoShell shell = b.Shell();
AMCAX::TopoExplorerTool::MapShapes(shell, AMCAX::ShapeType::Face, faceSet2);
std::cout << int(face2.Orientation()) << std::endl;//1
AMCAX::TopoExplorerTool::MapShapes(reshell, AMCAX::ShapeType::Face, refaceSet2);
AMCAX::TopoFace reface2 = AMCAX::TopoCast::Face(refaceSet2[0]);
std::cout << int(reface2.Orientation()) << std::endl;//0
static AMCAX_API const TopoShell & Shell(const TopoShape &s)
Cast shape to shell

Solid

对 Solid 进行 Reversed 后, Solid 所包围的方向变为反向。对 Solid 进行 Reversed 等价于将 Forward 的 Solid 中的每个 Face 的 Orientation 变为反向。

AMCAX::TopoShape box = AMCAX::MakeBox(AMCAX::Point3(-5.0, -5.0, 0.0), AMCAX::Point3(5.0, 5.0, 3.0));
AMCAX::TopoExplorerTool::MapShapes(solid, AMCAX::ShapeType::Face, faceSet2);
std::cout << int(face2.Orientation()) << std::endl;//1
AMCAX::TopoExplorerTool::MapShapes(resolid, AMCAX::ShapeType::Face, refaceSet2);
AMCAX::TopoFace reface2 = AMCAX::TopoCast::Face(refaceSet2[0]);
std::cout << int(reface2.Orientation()) << std::endl;//0
AMCAX::OCCTIO::OCCTTool::Write(solid, "solid.brep");
AMCAX::OCCTIO::OCCTTool::Write(resolid, "resolid.brep");
static AMCAX_API const TopoSolid & Solid(const TopoShape &s)
Cast shape to solid

Tolerance

Tolerance 是表示某个 TopoShape 误差的量,比如一个 Vertex 在 Edge1 的曲线上的点是 Point3(0.0, 0.0, 0.0),在 Edge2 的曲线上的点是 Point3(1.0, 0.0, 0.0),在 tolerance=2.0 的情况下,这两个点可以视为一个点,也就是说这样的 Vertex 尽管在不同的 Edge 上的几何点不同,但作为拓扑结构,这个 Vertex 是合法的。
获取 TopoShape 的 Tolerance 的方法是:AMCAX::TopoTool::Tolerance()。只有 Vertex、Edge、Face 这种包含了几何信息的结构才有 Tolerance。而更改 Tolerance 的方法是: AMCAX::TopoBuilder::UpdateVertex()AMCAX::TopoBuilder::UpdateEdge()AMCAX::TopoBuilder::UpdateFace()

接下来我们将逐一介绍 Vertex、Edge、Face 中的 Tolerance:

Vertex

Vertex 的 Tolerance 是一个设定的值,其意义为 Point 到 Vertex 的距离(Point 到 Vertex 中 Point 的距离)不大于 Tolerance 时,认为 Point 在 Vertex 上。而 Vertex 的 Point 有很多来源:本身的 Point3,Curve 在某一参数上的值,Surface 在某一 uv 参数上的值,Vertex 的 Tolerance 要不小于这些点之间的最大距离。

AMCAX::Point3 p1(0., 0., 0.);
auto circle = std::make_shared<AMCAX::Geom3Circle>(AMCAX::Frame3(), 1.0);
double fp = circle->FirstParameter();
AMCAX::Point3 p2 = circle->Value(fp);
builder.MakeVertex(v1, p1,2.0);
builder.MakeVertex(v2, p2, 2.0);
std::cout << AMCAX::TopoTool::Tolerance(v1);//2.0
std::cout << AMCAX::TopoTool::Tolerance(v2);//2.0
double tol = 2.5;
builder.UpdateVertex(v1, p1, tol);
builder.UpdateVertex(v2, p2, tol);
std::cout << AMCAX::TopoTool::Tolerance(v1);//2.5
std::cout << AMCAX::TopoTool::Tolerance(v2);//2.5
static AMCAX_API double Tolerance(const TopoFace &f)
Get the tolerance of the face

Edge

Edge 的 Tolerance 是一个设定的值,其意义为 Point 到 Edge 的距离不大于 Tolerance 时,认为 Point 在 Edge 上。此外,Edge 有 curve3d 和 pcurve,而 pcurve 可以通过 Surface 构建一个等价的 curve3d。若干条 curve3d 在采样下的最大距离不能大于 Edge 的 Tolerance,如果不满足则必须增大 Edge 的 Tolerance。另外,Vertex 的 Tolerance 必须比所在 Edge 的 Tolerance 更大或相等。

AMCAX::Point3 p1(0., 0., 0.);
auto circle = std::make_shared<AMCAX::Geom3Circle>(AMCAX::Frame3(), 1.0);
double fp = circle->FirstParameter();
AMCAX::Point3 p2 = circle->Value(fp);
builder.MakeVertex(v1, p1,2.0);
builder.MakeVertex(v2, p2, 2.0);
AMCAX::MakeEdge e(v1, v2);
AMCAX::TopoEdge edge = e.Edge();
std::cout << AMCAX::TopoTool::Tolerance(edge);//1e-7
builder.UpdateEdge(edge, 1.0);
std::cout << AMCAX::TopoTool::Tolerance(edge);//1.0

Face

Face 的 Tolerance 是一个设定的值,其意义为 Point 到 Face 的距离不大于 Tolerance 时,认为 Point 在 Face 上,较多用于布尔操作里。除此之外,Vertex、Edge 的 Tolerance 必须比所在 Face 的 Tolerance 更大或相等。

AMCAX::Geom3Plane plane(frame);
AMCAX::TopoFace face = AMCAX::MakeFace(plane.GetPlane(), 0., 2., 0., 2.);
std::cout << AMCAX::TopoTool::Tolerance(face);//1e-7
builder.UpdateFace(face, 0.5);
std::cout << AMCAX::TopoTool::Tolerance(face);//0.5
三维平面的类
定义 Geom3Plane.hpp:14
AMCAX_API void UpdateFace(const TopoFace &f, const std::shared_ptr< Geom3Surface > &s, const TopoLocation &l, double tol) const
Update the surface, location and tolerance of a face

注:当存在从属关系时,Vertex 的 Tolerance ≥ Edge 的 Tolerance ≥ Face 的 Tolerance。

Closed

TopoShape 是否封闭是一个关键的 Flag。我们可以使用 AMCAX::TopoTool::IsClosed() 来判断 TopoShape 是否为封闭的。在这里需要与 AMCAX::TopoShape::Closed() 进行区分, AMCAX::TopoShape::Closed() 返回的 bool 是一个用户任意定义的 flag。比如,将一条线段构建成一个 Edge,尽管不封闭,但仍然可以用 AMCAX::TopoShape::Closed(true) 将其 flag 设为 true。

AMCAX::Point3 p1(1.0, 0.0, 0.0);
edge.Closed(true);
std::cout << edge.Closed() << std::endl;//1
std::cout << AMCAX::TopoTool::IsClosed(edge) << std::endl;//0

AMCAX::TopoTool::IsClosed() 是对某几类 TopoShape 进行拓扑上的判断。接下来将逐一介绍输入不同 ShapeType 的 TopoShape 时, AMCAX::TopoTool::IsClosed 的用途。

Edge

AMCAX::TopoTool::IsClosed() 用于判断 Edge 的 FirstVertex 和 LastVertex 是否 IsSame。

auto c = std::make_shared<AMCAX::Geom3Circle>(AMCAX::Frame3(), 1.0);
AMCAX::TopoEdge edge1 = AMCAX::MakeEdge(c, v1, v1);
std::cout << AMCAX::TopoTool::IsClosed(edge1) << std::endl;//1

Wire

AMCAX::TopoTool::IsClosed() 用于判断 Wire 是否没有边界 Vertex,即对 Wire 遍历 Vertex,每个 Vertex 都出现偶数次。

AMCAX::MakePolygon polygon(AMCAX::Point3(-1., -1., 0.), AMCAX::Point3(1., -1., 0.), AMCAX::Point3(1., 1., 0.), AMCAX::Point3(-1., 1., 0.),true);
AMCAX::TopoWire w = polygon.Wire();
std::cout << AMCAX::TopoTool::IsClosed(w) << std::endl;//1
Class of making a polygon
定义 MakePolygon.hpp:18

Shell

AMCAX::TopoTool::IsClosed() 用于判断 Shell 是否没有边界 Edge(自由Edge),即对 Shell 遍历 Edge,除退化边外每个 Edge 都出现偶数次。

AMCAX::MakeBox box(2., 2., 2.);
AMCAX::TopoShell shell = box.Shell();
std::cout << AMCAX::TopoTool::IsClosed(shell) << std::endl;//1

其他

对 edge wire shell 外的其他类型执行 AMCAX::TopoTool::IsClosed() 时,其结果就是 AMCAX::TopoShape::Closed() 的结果。

//solid
AMCAX::TopoSolid solid = box.Solid();
solid.Closed(false);
std::cout << solid.Closed() << std::endl;//0
std::cout << AMCAX::TopoTool::IsClosed(solid) << std::endl;//0
solid.Closed(true);
std::cout << solid.Closed() << std::endl;//1
std::cout << AMCAX::TopoTool::IsClosed(solid) << std::endl;//1
//face
AMCAX::TopoExplorerTool::MapShapes(b, AMCAX::ShapeType::Face, FaceSet);
face1.Closed(false);
std::cout << face1.Closed() << std::endl;//0
std::cout << AMCAX::TopoTool::IsClosed(face1) << std::endl;//0
face1.Closed(true);
std::cout << face1.Closed() << std::endl;//1
std::cout << AMCAX::TopoTool::IsClosed(face1) << std::endl;//1