AMCAX Kernel 1.0.0.0
A Solid Modeling Sample

Overview

This tutorial provides the basic usage of AMCAX kernel to model a valve model.

Required Knowledge

The developers need to have some basic knowledge including the modern C++ programming language, 3D geometric modeling concepts, and the B-Rep topology structure. In specific, the AMCAX kernel is designed by using C++ 17 standard, and STL containers and algorithms are used as basic data structures and utilities. The geometric entities are represented by a Boundary-Representation (B-Rep) structure, including geometry objects and topology structures.

Model Preview

This tutorial uses a simplified valve model to show some of main modeling functionalities in the AMCAX kernel. The model is shown in the figure below:

The valve model.

The model consists of three sub-parts:

  1. The fixing structure at the inlet/outlet.
  2. The curved pipes for the passage of liquids.
  3. The block where a piston can be used to control the valve.

We will introduce the constructions step-by-step, and provide explanations in as much detail as possible.

Building the Hexagon Prism

Let's start with the inlet/outlet of the valve. A rounded-corner hexagon prism is created to represent the fixing structure.

Creating a Polygon

The first step is create a hexagon using the AMCAX::MakePolygon class. The resulting face can be constructed by using the AMCAX::MakeFace class.

AMCAX::Point3 p1(-80.0, 0.0, 56.0);
AMCAX::MakePolygon makePolygon;
int n = 6;
for (int i = 0; i < n; ++i)
{
double t = M_PI * 2.0 * i / n;
makePolygon.Add(p1.Translated(AMCAX::Vector3(0.0, std::cos(t), std::sin(t)) * 50.0));
}
makePolygon.Close();
AMCAX::TopoWire w = makePolygon;
Class of making a face.
Definition: MakeFace.hpp:22
Class of making a polygon.
Definition: MakePolygon.hpp:18
AMCAX_API void Close()
Close the polygon.
AMCAX_API void Add(const Point3 &p)
Add a new point to the polygon.
Class of face.
Definition: TopoFace.hpp:12
Class of wire.
Definition: TopoWire.hpp:12

Click here example1 to get the complete source code for the above example, which you can download according to your learning needs.

Applying 2D Fillets

To construct a round-corner hexagon, we apply a 2D fillet to each vertex of the hexagon by using the AMCAX::MakeFillet2d class.

AMCAX::MakeFillet2d makeFillet2d(f);
AMCAX::TopoExplorerTool::MapShapes(f, AMCAX::ShapeType::Vertex, vertices);
for (int i = 0; i < vertices.size(); ++i)
{
makeFillet2d.AddFillet(AMCAX::TopoCast::Vertex(vertices[i]), 5.0);
}
AMCAX::TopoShape face2 = makeFillet2d.Shape();
int size() const noexcept
Get the size of the set.
Definition: IndexSet.hpp:83
Class of make fillet and chamfer on a vertex of a planar face.
Definition: MakeFillet2d.hpp:20
static AMCAX_API const TopoVertex & Vertex(const TopoShape &s)
Cast shape to vertex.
static AMCAX_API void MapShapes(const TopoShape &s, ShapeType t, IndexSet< TopoShape > &shapeSet)
Construct a set of sub-shapes of given type.
Base class of shape, containing an underlying shape with a location and an orientation.
Definition: TopoShape.hpp:15

In the above sample code, the AMCAX::IndexSet class is essentially an unordered set whose elements have their own indices. The AMCAX::TopoExplorerTool::MapShapes function will find all the vertices in the shape. Click here example2 to get the complete source code for the above example, which you can download according to your learning needs.

Extruding a Prism

Next, we extrude the hexagon to create a prism by using the AMCAX::MakePrism class.

AMCAX::TopoShape hexagonPrism = AMCAX::MakePrism(face2, AMCAX::Vector3(14.0, 0.0, 0.0));
Class of making a prism or an extrusion shape.
Definition: MakePrism.hpp:16

Click here example3 to get the complete source code for the above example, which you can download according to your learning needs.

Applying 3D Chamfers

Then, we apply 3D chamfers by using AMCAX::MakeChamfer class.

AMCAX::MakeChamfer makeChamfer(hexagonPrism);
for (AMCAX::TopoExplorer ex(hexagonPrism, AMCAX::ShapeType::Edge); ex.More(); ex.Next())
{
const AMCAX::TopoEdge& edge = AMCAX::TopoCast::Edge(ex.Current());
AMCAX::TopoVertex vFirst, vLast;
AMCAX::TopoExplorerTool::Vertices(edge, vFirst, vLast);
if (std::fabs(pFirst[0] - pLast[0]) < AMCAX::Precision::Confusion())
{
makeChamfer.Add(0.5, edge);
}
}
AMCAX::TopoShape hex = makeChamfer.Shape();
The class of chamfer.
Definition: MakeChamfer.hpp:20
static constexpr double Confusion()
Get the confusion tolerance.
Definition: Precision.hpp:122
static AMCAX_API const TopoEdge & Edge(const TopoShape &s)
Cast shape to edge.
Class of edge.
Definition: TopoEdge.hpp:12
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.
static AMCAX_API void Vertices(const TopoEdge &e, TopoVertex &vfirst, TopoVertex &vlast, bool cumOri=false)
Get the two vertices of an edge.
static AMCAX_API Point3 Point(const TopoVertex &v)
Get the point of a vertex.
Class of vertex.
Definition: TopoVertex.hpp:12

Here, the AMCAX::TopoExplorer class is used to traverse all the edges in the shape. And if an edge has two vertices sharing the same x coordinate, then we chamfer along the edge. Click here example4 to get the complete source code for the above example, which you can download according to your learning needs.

Building the Curved Pipe

Next, we create a curved pipe for inlet/outlet. The process seems a little bit complicated, but the main sub-steps are quite simple. The pipe is created by using the AMCAX::MakePipe class. This class requires a spine wire representing the sweeping path and a profile shape to be used to sweep.

Construct a Spine Wire

The spine wire is a 'S' type path, consisting of five curves: line – arc – line – arc – line. All the curves are G1 connected. This means that the middle line is tangent to both the arcs. Here, we first create 2D circles using the AMCAX::MakeGeom2Circle class, and then use the AMCAX::GccLine2Tangent class to find the line tangent to two circles.

AMCAX::Point2 center1(-26.3, 58.5);
AMCAX::Point2 center2(-63.8, 76.0);
std::shared_ptr<AMCAX::Geom2Circle> circle2d1 = AMCAX::MakeGeom2Circle(AMCAX::Axis2(center1, AMCAX::CartesianCoordinateSystem::DY2()), 20.0);
std::shared_ptr<AMCAX::Geom2Circle> circle2d2 = AMCAX::MakeGeom2Circle(AMCAX::Axis2(center2, -AMCAX::CartesianCoordinateSystem::DY2()), 20.0);
if (!gcc.IsDone() || gcc.NSolutions() != 1)
{
throw AMCAX::ConstructionError();
}
double parSol1, parArg1, parSol2, parArg2;
AMCAX::Point2 pointSol1, pointSol2;
gcc.Tangency1(0, parSol1, parArg1, pointSol1);
gcc.Tangency2(0, parSol2, parArg2, pointSol2);
AMCAX::Line2 tangentLine = gcc.Solution(0);
Class of adaptor of 2D geometric curves.
Definition: AdaptorGeom2Curve.hpp:19
static AMCAX_API const Direction2 & DY2()
Get the y-direction in 2D.
static AMCAX_API GccQualifiedCircle Enclosing(const Circle2 &c)
Construct an enclosing circle.
static AMCAX_API GccQualifiedLine Outside(const Line2 &l)
Construct an outside line.
Class of constructing a 2D line tangent to two curves or points.
Definition: GccLine2Tangent.hpp:19
Class of 2D line.
Definition: LineT.hpp:245
Class of making 2D geometric circles.
Definition: MakeGeom2Circle.hpp:13

Then, we map the curves from 2D to 3D and build the five edges and the the spine wire. Here, the AMCAX::MakeArcOfCircle class is used to create arcs, the AMCAX::MakeSegment class is used to create line segments, the AMCAX::MakeEdge class is used to create edges from curves, and the AMCAX::MakeWire class is used to create the spine wire from edges.

std::shared_ptr<AMCAX::Geom3Curve> c13d = AMCAX::GeometryTool::To3d(localframe, circle2d1);
std::shared_ptr<AMCAX::Geom3Curve> c23d = AMCAX::GeometryTool::To3d(localframe, circle2d2);
AMCAX::Line3 line = AMCAX::CurveCalculation::To3d(localframe, tangentLine);
std::shared_ptr<AMCAX::Geom3TrimmedCurve> arc1 = AMCAX::MakeArcOfCircle(std::static_pointer_cast<AMCAX::Geom3Circle>(c13d)->Circle(), 0.0, parArg1, true);
std::shared_ptr<AMCAX::Geom3TrimmedCurve> arc2 = AMCAX::MakeArcOfCircle(std::static_pointer_cast<AMCAX::Geom3Circle>(c23d)->Circle(), 0.0, parArg2, true);
std::shared_ptr<AMCAX::Geom3TrimmedCurve> seg0 = AMCAX::MakeSegment(arc1->Value(0.0), arc1->Value(0.0).Translated(AMCAX::Vector3(26.3, 0.0, 0.0)));
std::shared_ptr<AMCAX::Geom3TrimmedCurve> seg1 = AMCAX::MakeSegment(line, parSol1, parSol2);
std::shared_ptr<AMCAX::Geom3TrimmedCurve> seg2 = AMCAX::MakeSegment(arc2->Value(0.0), arc2->Value(0.0).Translated(AMCAX::Vector3(-16.2, 0.0, 0.0)));
AMCAX::MakeWire makewire;
makewire.Add({e0, e1, e2, e3, e4});
AMCAX::TopoWire spine = makewire.Wire();
static AMCAX_API const Direction3 & DY()
Get the y-direction in 3D.
static AMCAX_API const Point3 & Origin()
Get the origin point in 3D.
static AMCAX_API const Direction3 & DX()
Get the x-direction in 3D.
static AMCAX_API Point3 To3d(const Frame3 &pos, const Point2 &p)
Convert a 2D point to a 3D point.
static AMCAX_API std::shared_ptr< Geom3Curve > To3d(const Frame3 &position, const std::shared_ptr< Geom2Curve > &curve)
Convert a 2D curve to 3D curve.
Class of 3D line.
Definition: LineT.hpp:346
Class of making arcs of 3D circle.
Definition: MakeArcOfCircle.hpp:17
Class of making an edge.
Definition: MakeEdge.hpp:24
Class of making 3D line segments.
Definition: MakeSegment.hpp:17
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.

Creating Profile Wires

The profile is a simple circle and is created by using the AMCAX::MakeGeom3Circle class.

AMCAX::MakeGeom3Circle makecircle(AMCAX::Point3(-26.3, 0.0, 78.5), AMCAX::Direction3(1.0, 0.0, 0.0), 10.0);
AMCAX::MakeEdge makeedge(makecircle.Value());
AMCAX::TopoWire profile1 = AMCAX::MakeWire(makeedge.Edge());
Class of making 3D geometric circles.
Definition: MakeGeom3Circle.hpp:13

Creating 2D Offset Wire

Since the pipe is tunnel which has an inner face and an outer face, we create an offset shape for the profile of the outer face. The shape is copied by the AMCAX::CopyShape class, and then use the AMCAX::MakeOffset class to create a 2D offset wire.

AMCAX::MakeOffset offset(profile2);
offset.Perform(6.0);
if (offset.IsDone())
{
profile2 = AMCAX::TopoCast::Wire(offset.Shape());
}
Class of copying a shape.
Definition: CopyShape.hpp:12
Class of making offset wires.
Definition: MakeOffset.hpp:19
static AMCAX_API const TopoWire & Wire(const TopoShape &s)
Cast shape to wire.

Creating Pipes

Next, we use the AMCAX::MakePipe class to create sweeping solids by using a face, generated by the AMCAX::MakeFace class, as the profile shape. We will compute a boolean cut of these two solid at the next step.

Class of make pipe algorithm.
Definition: MakePipe.hpp:20

Click here example5 to obtain the complete source code for curved track pipe1, which you can download according to your learning needs. Click here example6 to obtain the complete source code for curved track pipe2, which you can download according to your learning needs.

Building the Valve

Finally, we build the central part of the valve, which is simplified to a cylinder where a piston can be used to control the valve.

Creating a Cylinder

A cylinder is created by using the AMCAX::MakeCylinder class.

static AMCAX_API const Direction3 & DZ()
Get the z-direction in 3D.
Class of making a cylinder.
Definition: MakeCylinder.hpp:15

Click here example7 to get the complete source code for the above example, which you can download according to your learning needs.

Applying 3D Fillets

Here, we additionally add a small 3D fillet example to the middle cylinder by using the AMCAX::MakeFillet class as follows:

AMCAX::MakeFillet fillet(middleCylinder);
for (AMCAX::TopoExplorer ex(middleCylinder, AMCAX::ShapeType::Edge); ex.More(); ex.Next())
{
const auto& edge = AMCAX::TopoCast::Edge(ex.Current());
{
fillet.Add(1.0, edge);
}
}
middleCylinder = fillet.Shape();
The class of fillet.
Definition: MakeFillet.hpp:23
static AMCAX_API bool IsClosed(const TopoShape &s)
Is the shape closed.

Click here example8 to get the complete source code for the above example, which you can download according to your learning needs.

Mirroring the Curved Pipe

As the valve should have both the inlet and the outlet, we have to mirror the pipe by using the AMCAX::TransformShape class.

AMCAX::TopoShape pipe3 = AMCAX::TransformShape(pipe1, tr, true);
AMCAX::TopoShape pipe4 = AMCAX::TransformShape(pipe2, tr, true);
Class of transforming a shape.
Definition: TransformShape.hpp:12
void SetMirror(const PointT< OtherScalar, DIM > &point)
Set the transformation as mirroring by point.
Definition: TransformationT.hpp:89

Click here example9 to obtain the mirror curved track pipe1, the complete source code to construct pipe3, you can download according to your learning needs. Click here example10 to obtain the mirror curved track pipe2, to construct the complete source code of pipe4, you can download according to your learning needs. Click here example11 to obtain the mirror hex, construct hex2 complete source code, you can download according to your learning needs.

Boolean Operations

The final step is boolean all the parts together by using the AMCAX::BoolBRepFuse class and the AMCAX::BoolBRepCut class. Here shows the sample code:

AMCAX::TopoShape result = AMCAX::BoolBRepFuse(middleCylinder, pipe2);
result = AMCAX::BoolBRepFuse(result, pipe4);
result = AMCAX::BoolBRepFuse(result, hex);
result = AMCAX::BoolBRepFuse(result, hex2);
result = AMCAX::BoolBRepCut(result, pipe1);
result = AMCAX::BoolBRepCut(result, pipe3);
The class of cut operation.
Definition: BoolBRepCut.hpp:14
The class of fuse operation.
Definition: BoolBRepFuse.hpp:14

Don't forget to construct a tunnel for pistons to contol the valve, although the construction is simplified to be a cylinder.

Meshing

After generated a shape, you may want to render the shape by modern rendering engines like OpenGL. Thus, we also provide a simple meshing tool to generate meshes for shapes.

Building Meshes

The AMCAX::BRepMeshIncrementalMesh class is often used to generate a mesh for each face in the shape. The edges have a polygon on mesh in each adjacent face, but the meshes only coincide at the boundary edges and are not topological connected. You can also use AMCAX::MakeShapeTool::EnsureNormalConsistency() to compute normals for vertices in the mesh. The sample code is as follows:

AMCAX::BRepMeshIncrementalMesh mesher(result, 0.005, true);
if (mesher.IsDone())
{
}
Class of meshing.
Definition: BRepMeshIncrementalMesh.hpp:16
static AMCAX_API bool EnsureNormalConsistency(const TopoShape &s, double angTol=0.001, bool forceComputeNormals=false)
Ensure the consistency of normal in the triangular meshes of a shape.

Exporting to a STL file

We provide tools for exporting meshes to STL files and OBJ files. Here is an example of exporting to a STL file.

AMCAX::STLTool::WriteShape(result, "partSample.stl");
static AMCAX_API bool WriteShape(const TopoShape &shape, const std::string &file, bool asciiMode=true)
Write the mesh of a shape to a file.

Click here example12 to obtain the full source code of the entity modeling example, you can download it according to your needs.

Appendix

The complete code of this sample is listed here:

void MakeValve()
{
// Make a polygon
AMCAX::Point3 p1(-80.0, 0.0, 56.0);
AMCAX::MakePolygon makePolygon;
int n = 6;
for (int i = 0; i < n; ++i)
{
double t = M_PI * 2.0 * i / n;
makePolygon.Add(p1.Translated(AMCAX::Vector3(0.0, std::cos(t), std::sin(t)) * 50.0));
}
makePolygon.Close();
AMCAX::TopoWire w = makePolygon;
// 2D fillet
AMCAX::MakeFillet2d makeFillet2d(f);
AMCAX::TopoExplorerTool::MapShapes(f, AMCAX::ShapeType::Vertex, vertices);
for (int i = 0; i < vertices.size(); ++i)
{
makeFillet2d.AddFillet(AMCAX::TopoCast::Vertex(vertices[i]), 5.0);
}
AMCAX::TopoShape face2 = makeFillet2d.Shape();
// Extrusion
AMCAX::TopoShape hexagonPrism = AMCAX::MakePrism(face2, AMCAX::Vector3(14.0, 0.0, 0.0));
// 3D Chamfer
AMCAX::MakeChamfer makeChamfer(hexagonPrism);
for (AMCAX::TopoExplorer ex(hexagonPrism, AMCAX::ShapeType::Edge); ex.More(); ex.Next())
{
const AMCAX::TopoEdge& edge = AMCAX::TopoCast::Edge(ex.Current());
AMCAX::TopoVertex vFirst, vLast;
AMCAX::TopoExplorerTool::Vertices(edge, vFirst, vLast);
if (std::fabs(pFirst[0] - pLast[0]) < AMCAX::Precision::Confusion())
{
makeChamfer.Add(0.5, edge);
}
}
AMCAX::TopoShape hex = makeChamfer.Shape();
// Spine of pipe
AMCAX::Point2 center1(-26.3, 58.5);
AMCAX::Point2 center2(-63.8, 76.0);
std::shared_ptr<AMCAX::Geom2Circle> circle2d1 = AMCAX::MakeGeom2Circle(AMCAX::Axis2(center1, AMCAX::CartesianCoordinateSystem::DY2()), 20.0);
std::shared_ptr<AMCAX::Geom2Circle> circle2d2 = AMCAX::MakeGeom2Circle(AMCAX::Axis2(center2, -AMCAX::CartesianCoordinateSystem::DY2()), 20.0);
if (!gcc.IsDone() || gcc.NSolutions() != 1)
{
throw AMCAX::ConstructionError();
}
double parSol1, parArg1, parSol2, parArg2;
AMCAX::Point2 pointSol1, pointSol2;
gcc.Tangency1(0, parSol1, parArg1, pointSol1);
gcc.Tangency2(0, parSol2, parArg2, pointSol2);
AMCAX::Line2 tangentLine = gcc.Solution(0);
std::shared_ptr<AMCAX::Geom3Curve> c13d = AMCAX::GeometryTool::To3d(localframe, circle2d1);
std::shared_ptr<AMCAX::Geom3Curve> c23d = AMCAX::GeometryTool::To3d(localframe, circle2d2);
AMCAX::Line3 line = AMCAX::CurveCalculation::To3d(localframe, tangentLine);
std::shared_ptr<AMCAX::Geom3TrimmedCurve> arc1 = AMCAX::MakeArcOfCircle(std::static_pointer_cast<AMCAX::Geom3Circle>(c13d)->Circle(), 0.0, parArg1, true);
std::shared_ptr<AMCAX::Geom3TrimmedCurve> arc2 = AMCAX::MakeArcOfCircle(std::static_pointer_cast<AMCAX::Geom3Circle>(c23d)->Circle(), 0.0, parArg2, true);
std::shared_ptr<AMCAX::Geom3TrimmedCurve> seg0 = AMCAX::MakeSegment(arc1->Value(0.0), arc1->Value(0.0).Translated(AMCAX::Vector3(26.3, 0.0, 0.0)));
std::shared_ptr<AMCAX::Geom3TrimmedCurve> seg1 = AMCAX::MakeSegment(line, parSol1, parSol2);
std::shared_ptr<AMCAX::Geom3TrimmedCurve> seg2 = AMCAX::MakeSegment(arc2->Value(0.0), arc2->Value(0.0).Translated(AMCAX::Vector3(-16.2, 0.0, 0.0)));
AMCAX::MakeWire makewire;
makewire.Add({e0, e1, e2, e3, e4});
AMCAX::TopoWire spine = makewire.Wire();
// Profile of pipe
AMCAX::MakeGeom3Circle makecircle(AMCAX::Point3(-26.3, 0.0, 78.5), AMCAX::Direction3(1.0, 0.0, 0.0), 10.0);
AMCAX::MakeEdge makeedge(makecircle.Value());
AMCAX::TopoWire profile1 = AMCAX::MakeWire(makeedge.Edge());
// Make 2D offset
AMCAX::MakeOffset offset(profile2);
offset.Perform(6.0);
if (offset.IsDone())
{
profile2 = AMCAX::TopoCast::Wire(offset.Shape());
}
// Make pipe
// Make a cylinder
// 3D fillet
AMCAX::MakeFillet fillet(middleCylinder);
for (AMCAX::TopoExplorer ex(middleCylinder, AMCAX::ShapeType::Edge); ex.More(); ex.Next())
{
const auto& edge = AMCAX::TopoCast::Edge(ex.Current());
{
fillet.Add(1.0, edge);
}
}
middleCylinder = fillet.Shape();
// Mirror tranformation
AMCAX::TopoShape pipe3 = AMCAX::TransformShape(pipe1, tr, true);
AMCAX::TopoShape pipe4 = AMCAX::TransformShape(pipe2, tr, true);
AMCAX::TopoShape hex2 = AMCAX::TransformShape(hex, tr, true);
// Boolean
AMCAX::TopoShape result = AMCAX::BoolBRepFuse(middleCylinder, pipe2);
result = AMCAX::BoolBRepFuse(result, pipe4);
result = AMCAX::BoolBRepFuse(result, hex);
result = AMCAX::BoolBRepFuse(result, hex2);
result = AMCAX::BoolBRepCut(result, pipe1);
result = AMCAX::BoolBRepCut(result, pipe3);
// Make a hole
result = AMCAX::BoolBRepCut(result, hole);
// Meshing
AMCAX::BRepMeshIncrementalMesh mesher(result, 0.005, true);
if (mesher.IsDone())
{
}
// Export to a STL file
AMCAX::STLTool::WriteShape(result, "partSample.stl");
}