AMCAX Kernel
Geometry kernel for CAD/CAE/CAM
AMCAX Kernel 1.0.0.0
Loading...
Searching...
No Matches
BRep Structural Foundation

BRep Structure

Boundary Representation (BRep) is currently the most popular data structure. BRep can maintain completeness and unambiguity while reducing storage requirements. BRep uses points, curves, and surfaces on the boundaries of entities to represent those entities, and the volume information of the entity is derived from the three-dimensional space enclosed within the boundary. Its two basic elements are: geometric information (Point, Curve, Surface); and topological information (Vertex, Edge, Face connectivity).

Topological Structure

Definition

Let’s first define the various levels of topological structures in a BRep:

  • Vertex is a point in topological terms, corresponding to a geometric Point.
  • Edge is a line in topological terms, corresponding to a geometric Curve, and the two vertices that constrain the Curve’s boundaries.
  • Wire is a set of edges that are connected end to end, with adjacent edges sharing vertices.
  • Face is a surface in topological terms, corresponding to a geometric Surface, and constrained by several closed wires.
  • Shell is a set of connected Faces, where adjacent Faces share one or more edges.
  • Solid is a space enclosed by a closed Shell.
  • CompSolid is a set of Solids.
  • Compound is a collection of multiple topological structures.

Common Examples

Here are some examples to help you better understand the concept.

A Face formed by a Surface

It’s easy to see that this example consists of 4 Vertices, 4 Edges, 1 Wire, and 1 Face.

Shell formed by Two Adjacent Surfaces

Since the overlapping vertices and edges in the middle are shared, this example consists of 6 Vertices, 7 Edges, 2 Wires, 2 Faces, and 1 Shell.

Cube

This example consists of 8 Vertices, 12 Edges, 6 Wires, 6 Faces, and 1 Shell.

Cylinder

This example consists of 2 Vertices, 3 Edges, 3 Wires, 3 Faces, and 1 Shell. Edge 0 is reused in the Wire, making it a classic Seam Edge.

Sphere

This example consists of 2 Vertices, 3 Edges, 1 Wire, 1 Face, and 1 Shell. In this case, there is not only a Seam Edge but also two Edges with no length. Despite having no length in 3D, these two Edges do have length in the parametric domain, making them classic Degenerated Edges.

Edge

Next, we will introduce some common types of Edges.

Ordinary Edge

In a closed TopoShape (like a Shell or Solid), the most common Edge is an edge that is adjacent to two different Faces. An ordinary Edge belongs to two different Faces and has a non-zero length.

Seam Edge

A common special case is the side edge of a cylinder. This Edge is owned by one Face and appears twice in the same Wire of that Face (once forward, once backward). Seam Edges are valid.

Degenerated Edge

A common special case is the top edge of a sphere. This Edge has zero length and degenerates into a point. Degenerated Edges are valid.

Free Edge

An Edge that belongs to only one Face and appears only once in that Face’s Wire is called a Free Edge. Free Edges are invalid in a closed TopoShape.

Non-Manifold Edge

An Edge that belongs to more than two Faces in one Shape or Solid is called a Non-Manifold Edge. Non-Manifold Edges are invalid.

Traversing Topological Structure

Tree Structure of TopoShape

Let’s first introduce the tree structure of a TopoShape, as shown below:

Traversing

Traversing a topological structure is a common operation. Our kernel provides two ways: AMCAX::TopoExplorer and AMCAX::WireExplorer. Let’s introduce both methods one by one.

TopoExplorer

AMCAX::TopoExplorer can traverse through specified types (Shape, Compound, CompSolid, Solid, Shell, Face, Wire, Edge, Vertex) within a TopoShape in a preorder manner, based on its tree structure, to find child shapes of the specified type. For example, to traverse the Faces of a TopoShape, you can use the following code:

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.
Definition TopoExplorer.hpp:14
AMCAX_API bool More() const
Does the explorer have more shapes.
Class of face.
Definition TopoFace.hpp:12
Base class of shape, containing an underlying shape with a location and an orientation.
Definition TopoShape.hpp:15

It’s important to note that AMCAX::TopoExplorer does not consider duplicates (level of "equality"). That is, if an Edge is shared by two Faces, AMCAX::TopoExplorer will traverse this Edge twice. If you don’t want to traverse it twice, you can use a std::unordered_set to remove duplicates, like this:

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);
}

In the above, we discussed duplication, which inherently implies the definition of "equality". Next, we will introduce the three levels of "equality" for TopoShape, with increasing strictness:

  • IsPartner:Consistent with TopoTshape
  • IsSame:Consistent with TopoTshape and TopoLocation
  • IsEqual:Consistent with TopoTshape , TopoLocation , and Orientation
  • IndexSet:Default is IsSame level

WireExplorer

It's not difficult to realize that when we need to traverse the edges of a Wire in order, AMCAX::TopoExplorer cannot actually meet the demand. However, AMCAX::WireExplorer can fulfill this requirement, though the traversal speed will decrease.

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

Topological Information Retrieval

Index

Due to the tree structure and pre-order traversal, each Edge (similarly for Vertex, Wire, and Face) actually has its own index. The AMCAX::TopoExplorerTool class provides some convenient traversal operations. AMCAX::TopoExplorerTool::MapShapes can get an ordered set of indices for a specific type of topological structure (such as Edge), and the resulting set is unique.

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;
Template class of indexed set.
Definition IndexSet.hpp:20
int index(const key_type &key) const
Get the index of a given key.
Definition IndexSet.hpp:198
Class of making a box.
Definition 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
3D point
Definition PointT.hpp:459

In fact, this index matches the one shown in the lower-left corner of FreeCAD (FreeCAD starts from 1, while our kernel starts from 0).

Associated Face

AMCAX::TopoExplorerTool::MapShapesAndAncestors can retrieve a map from child shapes to their corresponding parent shapes, while AMCAX::TopoExplorerTool::MapShapesAndUniqueAncestors also provides a map. The difference is that when a seam edge is input, MapShapesAndUniqueAncestors will return a single associated face, while MapShapesAndAncestors will return two associated faces. For example, the mapping from Edge to its associated Face can be obtained as follows:

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);
Template class of indexed map.
Definition IndexMap.hpp:21
Class of making a cylinder.
Definition 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
3D direction
Definition DirectionT.hpp:587
FrameT< double, 3 > Frame3
3D frame
Definition FrameT.hpp:885

Degenerated Edge

Determining whether an Edge is degenerated is very important. For example, we should not sample along a degenerated edge. We can use AMCAX::TopoTool::Degenerated to check.

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.
Definition 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 Edge

You can determine whether an Edge is a seam edge using AMCAX::TopoTool::IsClosed.

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.

Accessing Geometry in Topological Structures

Point

A Vertex contains only one Point, representing a point in 3D space. The method to access the Point from a Vertex is 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 is a 3D parametric curve. pcurve is a 2D parametric curve. pcurve is bound to <Edge, Surface, Location>, meaning Edge, Surface, and Location are the indexes of a pcurve.

Edge and Curves

An Edge typically has one curve3d and two pcurves. In fact, a 3D parametric curve can also be constructed using a pcurve and surface, but due to tolerance, the two resulting 3D parametric curves will not coincide with the curve3d. In geometric modeling, there is no requirement for the number of curves in an Edge; it is fine if there are no curves. However, unless in special cases, the final geometric model should include one curve3d and two pcurves.

Next, let's introduce some special cases:

Edge on Geom3Plane

If the Edge lies on a plane (Geom3Plane), no additional pcurve is needed, because the Edge on the plane can be directly represented by a curve3d.

Seam Edge

A Seam Edge has one curve3d and two pcurves, both on one Face. The difference between the two pcurves lies in the Orientation of the Edge: one pcurve corresponds to the forward direction of the Edge, and the other corresponds to the reverse direction.

Degenerated Edge

A Degenerated Edge has only one pcurve, with no curve3d.

Free Edge (Edge not belonging to any face)

A Free Edge has one curve3d and one pcurve.

It is important to note that the kernel does not provide functionality for calculating a pcurve from a curve3d. The correct modeling approach is to first construct the pcurve and then use the pcurve and surface to calculate the 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.

Accessing the Curve of an Edge

Curve3d

The method to access the Curve3d of an Edge is 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.
Definition 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.

There are a few points to note:
1.The interface returns the raw curve. If an Edge is a segment formed by a Geom3Line and two Vertices, this interface will return an infinitely long line (Geom3Line), with fp and lp being the parameters of the two Vertices on the curve.

2.AMCAX::TopoTool::Curve has an overloaded function, one that takes a loc input and one that does not. The interface with loc returns a pointer to the curve memory inside the edge, while the interface without loc, when the edge's location is null, returns a pointer to the memory; otherwise, it returns a pointer to a newly constructed curve.

3.If the input is a degenerated edge, a null pointer will be returned.

4.The interface with loc returns a Curve with an additional location compared to the actual location, and applying location.Transformation() directly on the Curve is prohibited, as this will alter the edge's content. The correct approach is to copy the Curve and then apply transformations to the 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

The method to access the pcurve is 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.
Definition 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.

It is important to note that if the input is a seam edge, special handling is required. Depending on the Edge's Orientation, different pcurves will be returned:

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.
Definition TopologyMacros.hpp:11

In the above example, two pcurves for the seam edge of a cylinder were obtained. The orientation of these two pcurves is always consistent and matches the orientation of the curve3d. The edge's orientation is not considered.

Surface

A Face contains only one Surface. The method to access the Surface is 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.

The interface with loc returns a Surface with an additional location compared to the actual location, and applying location.Transformation() directly on the Surface is prohibited, as this will alter the face's content. The correct approach is to copy the Surface and then apply transformations to the 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
Get the copied geometry object.

Mesh

Mesh refers to a triangular mesh. The significance of Mesh in CAD is for rendering. Since the basic drawing elements in OpenGL are points, line segments, and triangles, curves must be discretized into polygons for rendering, and surfaces must be discretized into Mesh for rendering. Therefore, in applications with a graphical user interface (GUI), the BRep model that is seen is typically visualized through a triangular mesh. As shown in the figure below.

Clearly, discretization introduces errors, meaning that the BRep model displayed in applications with a GUI is not the model itself, but rather its Mesh. The error between the two is determined by the discretization precision, which is the density of the mesh. Considering that Mesh generation in CAD models must be very fast, the precision of the Mesh is understandably not very high.

There are many issues caused by discretization, such as visually judging whether two identical circles overlap or whether two identical spheres overlap, which can lead to situations like the one shown in the image below. Another issue is rendering a smooth surface as if it has wrinkles. Another type of problem is mesh generation failure, which can cause holes to appear. Therefore, when holes are seen in the model, one needs to consider both the Mesh reasons and the model itself.

Next, we will introduce the generation, retrieval, and read/write methods of 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.
Definition BRepMeshIncrementalMesh.hpp:16
static AMCAX_API std::shared_ptr< TriangularMesh > ReadFile(const std::string &file)
Read a mesh from a file.
static AMCAX_API bool WriteShape(const TopoShape &shape, const std::string &file, bool divideGroup=true)
Write the mesh of a shape to a file.
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.

Although Mesh is necessary for rendering, it is not always required to use the kernel to generate Mesh for the model after it is constructed, because software with a GUI will generate the Mesh automatically for rendering. Normals in rendering are crucial information. If the Mesh does not provide normals, an approximate normal will be automatically calculated based on the Mesh during rendering. If it is necessary to ensure that the normals of the triangular mesh match those of the CAD model, the AMCAX::MakeShapeTool::EnsureNormalConsistency() can be used.

Building TopoShape: MakeShape

Quickly creating topological structures is a basic requirement, and functions like AMCAX::MakeVertex, AMCAX::MakeEdge, AMCAX::MakeWire, AMCAX::MakeFace, etc., provide quick creation methods for different topological structures. These are commonly used and important functionalities.

MakeVertex

A Vertex can be created with a Point3:

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

MakeEdge

AMCAX::MakeEdge can quickly create an Edge based on a Curve. If no Vertex is provided in the input for MakeEdge, it will automatically create two Vertices. This means that if you want to create two connected Edges using MakeEdge, you must pay attention to the shared Vertex. A simple construction will result in two Edges and four Vertices. Additionally, degenerated edges can also be created using MakeEdge. MakeEdge has many functionalities, and we will introduce them categorically:

Simple Construction

You can create a line segment Edge using two Point3s or two TopoVertices. Note that the distance between the two Point3s/TopoVertices must be greater than AMCAX::Precision::Confusion(), otherwise, they will be considered the same Point3/TopoVertex.

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

Based on Mathematical Expressions

For example, you can build it based on Line3, Circle3, etc. Here’s an example using 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
3D line
Definition LineT.hpp:457

MakeEdge will automatically adjust parameters, Point3, and Vertex order based on the mathematical expression:

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)

Based on 3D Curve

This is an important functionality for building Edges from curves. Here, MakeEdge will still automatically adjust parameters, Point3, and Vertex order, as well as 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);

Based on pcurve

It is important to note that Edges created based on pcurve and Surface do not have 3D curves. Here's how you can construct them:

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

A wire can be constructed in the following two ways:

Add Edge Individually

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.
Definition 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.
Definition MakeShapeError.hpp:41

Note: Each edge added must geometrically connect to the edge that has already been added. For the example above, if the edges are added in the order e1—e3—e2—e4, when e3 is added, it will fail because it is not connected to e1. In this case, calling IsDone() will return false, and calling Error() will return WireError::DisconnectedWire. If it is known that the edges to be connected into a wire are connected, but the exact connection order is unknown, then the edge list method described below should be used.

Add a List of Edge

The order of edges in the list will not affect the result of the wire construction.

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();

Note: MakeWire can construct a wire that does not contain Seam Edges or degenerate edges. It supports adding individual edges or an edge list. MakeWire cannot correctly construct a wire containing degenerate edges because it determines orientation based on the 3D positions of the vertices, and degenerate edges have identical vertices, which may result in incorrect orientation. MakeWire also cannot correctly construct a wire containing non-manifold vertices, as this interface requires that the degree of vertices within a wire must be 2 (or 1, for open wire endpoints).

MakeFace

AMCAX::MakeFace has many functionalities, which we will introduce categorically:

Based on Mathematical Expression

t can be constructed based on mathematical expressions such as Plane, Cylinder, etc.:

AMCAX::TopoFace face1 = AMCAX::MakeFace(plane, 0.0, 1.0, 0.0, 1.0);
Class of making a face.
Definition MakeFace.hpp:22
Class of Plane.
Definition Plane.hpp:13

Based on Surface

Constructing a face from a Geom3Surface is the most common functionality of 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
Get the confusion tolerance.
Definition Precision.hpp:122

MakeFace requires providing tolerance when providing a surface. It is important to note that if the input surface is infinite and there are no constrained parameter ranges, a wire will not be automatically generated. In other cases, a wire will be generated automatically.

Based on Wire

Single Wire

When the wire lies on a plane, a face can be successfully constructed by directly inputting the wire:

AMCAX::TopoWire wire = AMCAX::MakeWire(circleEdge);
CircleS< 3 > Circle3
3D circle
Definition CircleT.hpp:178

Note: Inputting a wire that does not lie on a plane may result in failure.

Wire + Mathematical Expression

Note: The final boolean parameter indicates whether the face region is inside the wire (true means inside).

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

Adding a wire to an existing 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);

After execution, the result is as shown in the figure below: the left side is face7, and the right side is face8.

However, it is important to note the orientation of the wire. For example, if the code is written as below, it will produce an incorrect result:

The correct approach is to set the orientation of the edge or wire to Reversed at the edge or wire level.

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.

Building TopoShape: TopoBuilder

Although Make*.hpp provides some convenient APIs for building TopoShapes, certain modeling problems can only be solved by TopoBuilder. In this section, we will introduce the methods for constructing different topological structures using TopoBuilder.

Vertex

AMCAX::TopoBuilder::MakeVertex provides functionality to create a vertex.

// 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.
Definition 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

There are different methods to build different types of edges. We will introduce them one by one:

Ordinary Edge

The usual modeling approach is to calculate all the required Points, Curve3d, and PCurve using geometry, then build the topological structure:

Step 1: Prepare the Curve3d and construct the Edge using the curve.

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);
Class of making 3D geometric lines.
Definition 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 sets the parameter range for the TopoEdge. If the parameter range of the curve matches the expected range of the edge, this step can be skipped.

Step 2: Prepare the start and end vertices and add them to the edge.

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.

Note: Pay attention to the orientation of the start and end vertices. The orientation of the starting vertex is Forward, and the ending vertex is Reversed. The default orientation is Forward, so you need to change the orientation of the end vertex. There are two ways to do this: modify the orientation of the vertex itself or create a new one without modifying the original vertex. AMCAX::TopoShape::SetOrientation and AMCAX::TopoShape::Reverse modify the original vertex, while AMCAX::TopoShape::Oriented creates a new one and passes it to the builder.

Step 3: After constructing the Face, add the 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());
Class of making geometric planes.
Definition MakeGeom3Plane.hpp:13
static constexpr double PConfusion(double t) noexcept
Get the confusion tolerance in the 2D parametric space.
Definition 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.

The direction of the pcurve should be consistent with the curve3d. The orientation of the edge does not affect the direction of the pcurve. Remember: "Geometry is geometry, topology is topology." The process of adding vertices can occur after creating the edge, so swapping steps 2 and 3 is fine. Adding vertices before calling Range is also acceptable.

Seam Edge

The steps are mostly the same, but with a slight variation in step 3:

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

Note: The directions of pcurve1 and pcurve2 must remain consistent with the curve3d. pcurve1 corresponds to the Forward edge in the surface parameter domain, while pcurve2 corresponds to the Reversed edge in the surface parameter domain.

Degenerated Edge

Other steps are the same, but step 1 is different, as there is no 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.

Make sure to set the degenerated flag to True. The direction of the pcurve does not need to match the curve3d, just the orientation of the edge.

Free Edge

The steps remain the same, but step 3 differs, where only one pcurve is used.

Wire

The standard approach is:
Step 1: Determine the direction of the Wire based on the parameter domain, and identify which edges to use and their orientations:

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

Step 2: Construct the Wire and add the edges:

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.

The order in which edges are added can be arbitrary.
Step 3: Set the closed flag for the Wire:

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

Face

To create a face, use the following method:

AMCAX::MakeGeom3Plane plane1(p1, -DZ);
builder.MakeFace(face1, plane1.Value(), AMCAX::Precision::Confusion());
builder.Add(face1, wire1);
static AMCAX_API const Direction3 & DZ() noexcept
Get the z-direction in 3D.
AMCAX_API void MakeFace(TopoFace &f) const
Make an empty face.

Shell

When constructing a shell, be mindful of the closed flag. The method is as follows:

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.
Definition TopoShell.hpp:12

Solid

To construct a solid, use this method:

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

Compound

To create a compound, use this method:

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

Orientation

There are 4 types of OrientationType: Forward, Reversed, Internal, and External, with Forward and Reversed being the most commonly used. Any level of TopoShape can set its Orientation using SetOrientation() or retrieve its Orientation using Orientation(). If you want to get a TopoShape that only differs in Orientation from the current TopoShape, you can use Orientated().Orientation is independent of geometry and Location, meaning that changing one will not affect any other members.

Vertex

For an Edge, the Orientation of the starting Vertex is Forward, and the Orientation of the ending Vertex is Reversed. For example, a line segment Edge with two different Vertices will have one with Forward Orientation and the other with Reversed Orientation. Similarly, an Edge formed by a circle with two identical Vertices will still have one Vertex with Forward Orientation and the other with Reversed Orientation.

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

Note: In this example, because the Edge formed by a circle has two identical Vertices, only one vertex needs to be constructed. In this case, AMCAX::TopoTool::IsClosed(edge) will return true. If two vertices are constructed, AMCAX::TopoTool::IsClosed(edge) will return false.

Edge

The Forward direction of an Edge is the direction of curve3d. If there is no curve3d, it's the direction of the pcurve. The direction of a Reversed Edge is opposite to that of the curve.
For example, given three Edges formed using 3d curves on the xoy plane and a Geom3Plane constructed using the natural frame:

Since the direction of the Wire must be counterclockwise in the parameter domain, Reversed Edge0, Forward Edge2, and Forward Edge1 should form the Wire.

If a clockwise Wire is used, i.e., Forward Edge0, Reversed Edge1, and Reversed Edge2, the Face will define the region outside the Wire. Below are the counterclockwise and clockwise Wires:

If the Geom3Plane is constructed using Frame3(O, -OZ, OX), the Wire direction should be clockwise, i.e., Forward Edge0, Reversed Edge1, and Reversed Edge2, ensuring that the Face is correctly defined as the area enclosed by the Wire.

Wire

Reversing a Wire is equivalent to reversing the Orientation of each Edge in the Forward Wire.

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

Reversing a Face changes the direction of the region it encloses to the opposite of the surface normal. Reversing a Face is equivalent to reversing the Orientation of each Wire in the Forward Face.

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

The method to retrieve the normal of the surface where the Face lies is as follows:

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
Class of adaptor of 3D geometric surfaces.
Definition AdaptorGeom3Surface.hpp:16
constexpr const DirectionT< Scalar, DIM > & Direction() const noexcept
Get the main direction (z direction) in 3D.
Definition FrameT.hpp:441
const Frame3 & Position() const
Get the local frame.
Definition SurfaceBase.hpp:56

Shell

Reversing a Shell changes the direction of the region it encloses to the opposite direction. Reversing a Shell is equivalent to reversing the Orientation of each Face in the Forward Shell.

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

Reversing a Solid changes the direction of the region it encloses to the opposite direction. Reversing a Solid is equivalent to reversing the Orientation of each Face in the Forward Solid.

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 represents the amount of error in a TopoShape. For example, a vertex on the curve of Edge1 is at Point3(0.0, 0.0, 0.0), and the same vertex on the curve of Edge2 is at Point3(1.0, 0.0, 0.0). With a tolerance of 2.0, these two points can be considered as the same point. In other words, although the geometric points of this vertex on different edges are different, it is still valid as a topological structure.
To obtain the Tolerance of a TopoShape, the method is: AMCAX::TopoTool::Tolerance(). Only structures containing geometric information, such as Vertex, Edge, and Face, have Tolerance. To change the Tolerance, the methods are: AMCAX::TopoBuilder::UpdateVertex(), AMCAX::TopoBuilder::UpdateEdge(), AMCAX::TopoBuilder::UpdateFace().

Next, we will introduce the Tolerance for Vertex, Edge, and Face in turn:

Vertex

The Tolerance of a Vertex is a predefined value. Its significance is that when the distance from a Point to the Vertex (i.e., the distance from the Point to the Point within the Vertex) is less than or equal to the Tolerance, the Point is considered to lie on the Vertex. The Point in a Vertex can come from various sources: the Point3 itself, the value of a Curve at a certain parameter, or the value of a Surface at certain u,v parameters. The Tolerance of the Vertex must be no less than the maximum distance between these points.

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

The Tolerance of an Edge is a predefined value. Its significance is that when the distance from a Point to the Edge is less than or equal to the Tolerance, the Point is considered to lie on the Edge. Additionally, an Edge has a curve3d and a pcurve, where the pcurve can be used to construct an equivalent curve3d through a Surface. The maximum distance between multiple curve3d samples must not exceed the Tolerance of the Edge. If this condition is not met, the Tolerance of the Edge must be increased. Furthermore, the Tolerance of a Vertex must be greater than or equal to the Tolerance of the Edge it lies on.

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

The Tolerance of a Face is a predefined value. Its significance is that when the distance from a Point to the Face is less than or equal to the Tolerance, the Point is considered to lie on the Face. This is often used in Boolean operations. Additionally, the Tolerance of a Vertex or Edge must be greater than or equal to the Tolerance of the Face they lie on.

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
Class of 3D plane.
Definition 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.

Note: When there is a dependency relationship, the Tolerance of a Vertex must be greater than or equal to the Tolerance of an Edge, and the Tolerance of an Edge must be greater than or equal to the Tolerance of a Face.

Closed

Whether a TopoShape is closed is a crucial flag. We can use AMCAX::TopoTool::IsClosed() to determine if a TopoShape is closed. This should be distinguished from AMCAX::TopoShape::Closed(), which returns a boolean flag that can be arbitrarily defined by the user. For example, even if a line segment is constructed into an Edge that is not closed, it can still be set as closed using AMCAX::TopoShape::Closed(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

On the other hand, AMCAX::TopoTool::IsClosed() performs a topological check on certain types of TopoShape. Below is an explanation of how AMCAX::TopoTool::IsClosed() works for different ShapeType inputs.

Edge

AMCAX::TopoTool::IsClosed() is used to determine whether the FirstVertex and LastVertex of an Edge are the same (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() is used to determine whether a Wire has no boundary Vertex, meaning that each Vertex in the Wire appears an even number of times.

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.
Definition MakePolygon.hpp:18

Shell

AMCAX::TopoTool::IsClosed() is used to determine whether a Shell has no boundary Edge (free Edge), meaning that each Edge in the Shell (excluding degenerate edges) appears an even number of times.

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

Others

For TopoShape types other than Edge, Wire, and Shell, the result of AMCAX::TopoTool::IsClosed() is the same as the result of 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