CAM API
  • CAM API
v3.0.3
Search Results for

    Show / Hide Table of Contents

    Geometry — ENCY CAM API

    This document covers the full geometry domain of the ENCY CAM API: the model tree, entity inspection, B-rep traversal (Face / Loop / CoEdge / Surface / Curve), mesh access, coordinate systems, geometry import, interactive picking, and the geometry tree context-menu extension point.

    All .NET examples use the ComWrapper<T> pattern and the extension methods from CAMAPI.DotnetHelper. Raw IDL access is shown as secondary "direct access" notes where it differs meaningfully.


    Table of contents

    1. Concepts and object model
    2. ICAMAPIGeomLibrary — factory
    3. ICAMAPIGeometryModel — model root
    4. ICAMAPIGeometryTreeNode — tree navigation
    5. Traversing the geometry tree
    6. ICAMAPIGeometryEntity — entity inspection
    7. B-rep structure: Face, Loop, CoEdge
    8. ICamApiSurface — underlying surface
    9. ICamApiCurve and ICamApiCurve5D
    10. ICamApiMesh — triangulated representation
    11. ICamApiCoordinateSystem
    12. ICAMAPIGeometryImporter — importing files
    13. ICamApiGeomPicker — interactive picking
    14. IExtensionGeomModelNodePopup — context menu
    15. ICMAPITurnGeneratrixExtractor — lathe generatrix
    16. Export helpers
    17. Entity type reference
    18. ICamApiGeometryModelSketcher — creating primitives
    19. ICamApiPointSnapper — snapping points to faces

    1. Concepts and object model

    The geometry domain is built around a tree of named nodes. Each node (ICAMAPIGeometryTreeNode) carries one ICAMAPIGeometryEntity payload that describes what the node represents (group, face, curve, coordinate system, mesh, etc.). The tree is rooted at ICAMAPIGeometryModel.RootNode.

    ICAMAPIGeomLibrary            — factory: creates models, importers, extractors
      └─ ICAMAPIGeometryModel     — the whole geometry model (owns the tree)
           ├─ ICAMAPIGeometryTreeNode  — one node in the tree
           │    └─ ICAMAPIGeometryEntity  — the actual geometry payload
           │         ├─ ICamApiFaceGeometryEntity  → ICamApiFace
           │         │    ├─ ICamApiLoop           → ICamApiCoEdge → ICamApiSurfaceCurve
           │         │    └─ ICamApiSurface        (NURBS form available)
           │         ├─ ICamApiCurveGeometryEntity → ICamApiCurve / ICamApiCurve5D
           │         └─ ICamApiCSGeometryEntity    (4×4 matrix)
           └─ ICAMAPIGeometryTreeNodeIterator  — depth-first tree walker
    

    The library (ICAMAPIGeomLibrary) is accessed from the active project via ICamApiProject.CAMAPIGeomModel (model) and ICamApiProject.GeomImporter (importer), or created standalone through the library factory. It is not obtained through ICAMAPIGeomLibrary directly in most extension scenarios — instead the project exposes the model and importer directly.


    2. ICAMAPIGeomLibrary — factory

    ICAMAPIGeomLibrary is a low-level factory used when creating geometry models outside of a project context (e.g., in standalone utilities or unit tests).

    Properties

    Property Type Access Description
    HideUserMessages bool R/W Suppress interactive dialogs during import
    VisTolerancePercent int R/W Visualization chord tolerance, 0–100
    SearchFontFolder string R/W Priority folder for font files (useful in CI/VM environments)

    Methods

    Method Returns Description
    CreateGeometryModel() ICAMAPIGeometryModel Create a new empty geometry model
    CreateGeometryImporter() ICAMAPIGeometryImporter Create an importer bound to no model yet
    CreateTurnGeneratrixExtractor() ICMAPITurnGeneratrixExtractor Create a lathe generatrix extractor
    CreateFacesToTriangulatedFilesConverter() ICamApiFacesToTriangulatedFilesConverter Create a face-to-mesh file converter

    .NET helper usage

    // geomLibCom is a ComWrapper<ICAMAPIGeomLibrary>
    geomLibCom.SetHideUserMessages(true);
    using var modelCom = geomLibCom.CreateGeometryModel();
    using var importerCom = geomLibCom.CreateGeometryImporter();
    

    Helper class: GeomLibraryHelper in CAMAPI.DotnetHelper.


    3. ICAMAPIGeometryModel — model root

    Obtained in extension code via the active project:

    using var modelCom = new ComWrapper<ICAMAPIGeometryModel>(activeProject.CAMAPIGeomModel);
    

    Properties

    Property Type Access Description
    RootNode ICAMAPIGeometryTreeNode R Root of the geometry tree
    ActiveNode ICAMAPIGeometryTreeNode R/W Node that was most recently imported or activated

    Methods

    Method Description
    GetNodes() Returns ICAMAPIGeometryTreeNodeIterator for a depth-first walk
    FindByFullName(fullName) Find a node by its full path name (e.g. "Root/Body/Face_1")
    Transform(node, matrix) Apply a TST3DMatrix transformation to a node
    ExportSelectedToSTL(fileName) Export selected nodes to STL
    ExportSelectedToOSD(fileName) Export selected nodes to OSD
    ExportSelectedToDXF(fileName) Export selected nodes to DXF
    DeselectAll() Deselect all nodes
    DeleteNode(node) Remove a node from the tree
    GetFaceListOfSelected() Returns ICamApiFaceList of selected face nodes
    AddGroup(parentNode) Add an empty group node under parentNode
    AddCadGroup(name, lcs?) Add a CAD-typed group (a fresh GeCAD model) at the given LCS; helper defaults lcs to identity. The returned node hosts an editable ICadApiModel — reach it with AsCadModel() from CADAPI.DotnetHelper
    ExportSelectedToStep(fileName) Export selected nodes to a STEP file (experimental)
    RegisterHandler(ident, handler, events) Subscribe an event handler (e.g. for SelectionChanged); ident is your key for unregistering, events is an optional IListString filter (empty = all)
    UnregisterHandler(ident) Remove a previously registered handler by its ident

    Helper: modelCom.ExportSelectedToStep(path), modelCom.AddCadGroup("Fixtures"). AddCadGroup is the entry point to the CAD axis (GeCAD) that sits on top of the geometry model.

    Finding a node by name

    using var nodeCom = modelCom.InvokeAndWrap(m =>
        (m.FindByFullName("Root/Body/Face_1", out var status), status));
    

    Applying a transformation

    // Shift a node by (100, 200, 0)
    var shiftMatrix = T3DMatrix.MakeShiftMatrix(new T3DPoint { X = 100, Y = 200, Z = 0 });
    // Nest Invoke so the node reference is acquired inside its own wrapper's MTA context
    geomNodeCom.Invoke(node =>
        modelCom.Invoke(m => m.Transform(node, shiftMatrix)));
    

    See full example: Geometry/ExtensionUtilityGeometryModelNet/project/main/GeometryNodeTransformExample.cs

    Exporting the active node

    using var modelCom = new ComWrapper<ICAMAPIGeometryModel>(activeProject.CAMAPIGeomModel);
    modelCom.Invoke(m => m.ExportSelectedToOSD(exportedFilePath, out var status));
    

    See full example: Geometry/ExtensionUtilityGeometryModelNet/project/main/GeometryModelExportExample.cs

    Direct access (IDL): ExportSelectedToOSD takes an [out] TResultStatus* parameter. The .NET helper wraps this and throws on error.

    Observing selection changes

    Register a handler to be notified when the set of selected geometry nodes changes — both from interactive viewport picking and from programmatic Selected / DeselectAll calls. The handler object implements ICamApiEventHandler (the generic event-handler marker) together with ICamApiHandlerSelectionChanged. AddedNodes / RemovedNodes carry the FullName of the nodes that became / stopped being selected — resolve them via FindByFullName.

    class SelectionWatcher : ICamApiEventHandler, ICamApiHandlerSelectionChanged
    {
        public void SelectionChanged(string handlerIdent, IListString addedNodes, IListString removedNodes)
        {
            for (int i = 0; i < addedNodes.Count(); i++)
                Console.WriteLine("selected: " + addedNodes.Get(i));
        }
    }
    
    var watcher = new SelectionWatcher();
    modelCom.Invoke(m => m.RegisterHandler("my-watcher", watcher, null, out var st));
    // ... later
    modelCom.Invoke(m => m.UnregisterHandler("my-watcher", out var st));
    

    4. ICAMAPIGeometryTreeNode — tree navigation

    Each node in the geometry tree exposes parent/child/sibling links, forming a classical linked-tree structure.

    Properties

    Property Type Access Description
    Name string R Short name of this node
    FullName string R Full path from root (e.g. "Root/Body/Face_1")
    Selected bool R/W Selection state
    Visible bool R/W Visibility state
    Parent ICAMAPIGeometryTreeNode R Parent node (null for root)
    Child ICAMAPIGeometryTreeNode R First child node (null if none)
    Sibling ICAMAPIGeometryTreeNode R Next sibling node (null if none)
    GeometryEntity ICAMAPIGeometryEntity R The geometry payload

    .NET helper

    The GeometryTreeNodeHelper extension class provides typed wrappers for all properties, plus:

    // Enumerate only the direct children of a node
    foreach (var childCom in nodeCom.EnumerateChildren())
    {
        Console.WriteLine(childCom.Name());
        childCom.Dispose();
    }
    

    Rendering and tessellation

    Helper method Type Description
    DoubleNormal() / SetDoubleNormal(v) bool Double-sided rendering — mill both sides of surfaces (vs. only the normal side)
    VisTol() / SetVisTol(v) double Visual tessellation tolerance (lower = finer mesh)
    VisMeshU() / SetVisMeshU(v) int Isoparametric curves shown in the U direction
    VisMeshV() / SetVisMeshV(v) int Isoparametric curves shown in the V direction

    User parameters on a node

    Nodes carry free-form name/value parameters typed by TCAMAPIGeomParamType (aptInteger=0, aptReal=1, aptString=2, aptBoolean=3). System parameters added by the engine or at import time cannot be overwritten or deleted (attempting to do so throws).

    Helper method Description
    ParamsCount() Number of parameters on the node
    GetParamName(i) / GetParamValue(i) / GetParamType(i) Read parameter 0..ParamsCount-1
    AddParam(name, type, value) Add or overwrite a user parameter
    DeleteParam(name) Remove a user parameter; false if it did not exist
    nodeCom.AddParam("BatchNo", TCAMAPIGeomParamType.aptString, "A-17");
    for (int i = 0; i < nodeCom.ParamsCount(); i++)
        Console.WriteLine($"{nodeCom.GetParamName(i)} = {nodeCom.GetParamValue(i)}");
    

    CAD content of a node

    For a CAD-typed node, nodeCom.AsCadModel() (CADAPI.DotnetHelper) returns an ICadApiModel for inspecting or mutating the CAD content; it is null for non-CAD nodes (mesh, body, plain folders). This is the same CAD axis reached by ICAMAPIGeometryModel.AddCadGroup. Sketches, extrude, cuts, fillets: cad.md.


    5. Traversing the geometry tree

    Preferred approach: iterator + LINQ

    ICAMAPIGeometryTreeNodeIterator performs a depth-first walk. The AsEnumerable() extension method from GeometryTreeNodeIteratorHelper wraps it as a standard IEnumerable, with correct COM disposal.

    using var iteratorCom = modelCom.InvokeAndWrap(m =>
        (m.GetNodes(out var status), status));
    
    foreach (var nodeCom in iteratorCom.AsEnumerable())
    {
        // nodeCom is a ComWrapper<ICAMAPIGeometryTreeNode>
        // It is automatically disposed after the loop body
        if (nodeCom.EntityType() == TCAMAPIGeometryEntityType.etFace)
            Console.WriteLine(nodeCom.FullName());
    }
    

    The iterator exposes three navigation methods:

    Method Returns Description
    MoveToChild() bool Move down to first child; returns false if none
    MoveToSibling() bool Move to next sibling; returns false if none
    MoveToParent() bool Move up to parent; returns false at root
    Current() ICAMAPIGeometryTreeNode Current node
    Reset() — Reset to tree root

    Manual low-level traversal (explicit MoveToChild / MoveToSibling)

    When you need direct control of the iterator (instead of the AsEnumerable() wrapper above), use Invoke / InvokeAndWrap for every access. Navigate to the deepest first child, then walk siblings:

    using var iteratorCom = modelCom.InvokeAndWrap(m => m.GetNodes(out var status));
    
    // descend to the leaf level
    while (iteratorCom.Invoke(it => it.MoveToChild())) { }
    
    // walk all siblings at that level
    do
    {
        using var nodeCom = iteratorCom.InvokeAndWrap(it => it.Current());
        if (nodeCom.EntityType() == TCAMAPIGeometryEntityType.etFace)
            Console.WriteLine(nodeCom.Name());
    } while (iteratorCom.Invoke(it => it.MoveToSibling()));
    

    See full example: Geometry/ExtensionUtilityGeometryModelNet/project/main/GeometryNodesIteratorExample.cs

    Collecting selected nodes

    var selectedNodes = new List<ComWrapper<ICAMAPIGeometryTreeNode>>();
    foreach (var nodeCom in iteratorCom.AsEnumerable())
    {
        if (nodeCom.Selected())
            selectedNodes.Add(nodeCom.TransferOwnership()); // take ownership away from the iterator
    }
    

    See full example: Geometry/UtilityGeometryEntityReader/project/main/ExtensionGeometryEntityReader.cs


    6. ICAMAPIGeometryEntity — entity inspection

    Each tree node carries one ICAMAPIGeometryEntity. The entity's EntityType property determines what additional interface is available.

    ICAMAPIGeometryEntity properties

    Property Type Access Description
    EntityClass string R Internal class name string
    EntityType TCAMAPIGeometryEntityType R Discriminator enum (see §17)
    Color int R/W RGB color packed as integer
    ParentColor bool R/W When true, entity inherits color from its parent node
    UpdateStamp long R Monotonically increasing change counter

    GetBoundBox

    // Pass an identity matrix for the world bounding box
    var identity = T3DMatrix.Unit;
    var box = entityCom.GetBoundBox(identity);
    // box.Min and box.Max are TST3DPoint
    

    Direct access (IDL): GetBoundBox(in LCS: TST3DMatrix, [out] TResultStatus*): TST3DBox

    Specialised entity interfaces

    The raw COM object also implements one of the following interfaces depending on EntityType. Query with a direct cast inside nodeCom.Invoke(...):

    ICamApiCSGeometryEntity (etCS)

    nodeCom.Invoke(node =>
    {
        if (node.GeometryEntity is ICamApiCSGeometryEntity entityCs)
        {
            var matrix = entityCs.Matrix; // TST3DMatrix — position/orientation
            Console.WriteLine($"CS origin: {matrix.vT.X}, {matrix.vT.Y}, {matrix.vT.Z}");
        }
    });
    

    ICamApiFaceGeometryEntity (etFace)

    nodeCom.Invoke(node =>
    {
        if (node.GeometryEntity is ICamApiFaceGeometryEntity entityFace)
        {
            using var faceCom = ComWrapper.Create(entityFace.Face);
            // work with ICamApiFace — see §7
        }
    });
    

    ICamApiCurveGeometryEntity (etCurve)

    nodeCom.Invoke(node =>
    {
        if (node.GeometryEntity is ICamApiCurveGeometryEntity entityCurve)
        {
            using var curveCom = ComWrapper.Create(entityCurve.Curve);
            Console.WriteLine($"Length: {curveCom.Invoke(c => c.FullLen)}");
        }
    });
    

    Helper: CurveGeometryEntityHelper.Curve(entityCom) wraps the Curve property access so the returned ICamApiCurve is owned by a ComWrapper.

    ICamApiImportedGeometryEntity (imported folder metadata)

    A node that represents the root of an imported CAD model can be queried for the source file path and PLM metadata. This interface is implemented by the tree-node object itself (not reached through GeometryEntity), so the cast is performed on ICAMAPIGeometryTreeNode.

    Helper method Returns Description
    importedCom.CADModelLocalFileName() string Absolute path of the source CAD file
    importedCom.ImportedFromPLM() bool true if the model was imported from a PLM system
    importedCom.PLMObjectID() string Identifier of the imported file in PLMManager (empty when ImportedFromPLM is false)
    using var importedCom = nodeCom.InvokeAndWrap(node => node as ICamApiImportedGeometryEntity);
    if (!importedCom.IsNull)
    {
        string src = importedCom.CADModelLocalFileName();
        bool   plm = importedCom.ImportedFromPLM();
        string pId = importedCom.PLMObjectID();
    }
    

    Helper class: ImportedGeometryEntityHelper in CAMAPI.DotnetHelper.

    See full entity-reading example: Geometry/UtilityGeometryEntityReader/project/main/ExtensionGeometryEntityReader.cs


    7. B-rep structure: Face, Loop, CoEdge

    B-rep topology follows the standard Face → Loop → CoEdge hierarchy.

    ICamApiFace
      └─ ICamApiLoop (outer + inner loops via GetNext chain)
           └─ ICamApiCoEdge (via CoEdges iterator)
                └─ ICamApiSurfaceCurve (trimming curve on surface)
    

    ICamApiFace

    Obtained from ICamApiFaceGeometryEntity.Face or from ICamApiFaceList.

    Member Description
    Surface The underlying ICamApiSurface
    Orientation true = face normal agrees with surface normal
    GetFirstLoop() First ICamApiLoop; chain via loop.GetNext()
    GetMesh(tol) Triangulate to ICamApiMesh at given tolerance
    GetNurbsForm() Return face converted to NURBS representation
    IsCylindricalHole(tol, ...) Convenience test; returns center, axis, radius, ZMin, ZMax
    SurfaceKind Classified analytic kind of the surface — TCamApiSurfaceKind
    GetMinCurvatureRadius(tol) Minimum radius of curvature over the face; MaxFloat for a planar face
    GetMaxCurvatureRadius(tol) Maximum radius of curvature over the face — on a sphere this is the sphere radius. MaxFloat when no finite curved direction exists (planar face, or the straight direction of a cylinder or cone)
    GetPlane(out origin, out normal) If planar, returns plane origin and unit normal; true when planar
    GetCylinder(out center, out axis, out radius) If cylindrical, returns axis and radius; true when cylindrical
    GetCone(out center, out axis, out radius, out halfAngle) If conical, returns apex, axis, base radius and half-angle (rad); true when conical

    Enumerate all loops

    foreach (var loopCom in faceCom.EnumerateLoops())
    {
        bool isOuter = loopCom.Invoke(l => l.IsOuter);
        // ...
        loopCom.Dispose();
    }
    

    Check for a cylindrical hole

    if (faceCom.IsCylindricalHole(0.01,
            out var center, out var axis, out var radius, out var zMin, out var zMax))
    {
        Console.WriteLine($"Hole radius={radius}, axis=({axis.X},{axis.Y},{axis.Z})");
    }
    

    Analytic surface recognition

    SurfaceKind classifies the face's underlying surface. The Get* methods then extract the analytic parameters for the matching kind (they return false when the face is not of that kind).

    TCamApiSurfaceKind values: skOther, skPlanar, skCylindrical, skConical, skSpherical, skToroidal.

    switch (faceCom.SurfaceKind())
    {
        case TCamApiSurfaceKind.skPlanar:
            faceCom.GetPlane(out var origin, out var normal);
            break;
    
        case TCamApiSurfaceKind.skCylindrical:
            faceCom.GetCylinder(out var center, out var axis, out var radius);
            break;
    
        case TCamApiSurfaceKind.skConical:
            faceCom.GetCone(out var apex, out var coneAxis, out var baseRadius, out var halfAngle);
            break;
    }
    
    double rMin = faceCom.GetMinCurvatureRadius(0.01);   // MaxFloat for a plane
    

    FaceHelper exposes SurfaceKind, GetMinCurvatureRadius, GetMaxCurvatureRadius, GetPlane, GetCylinder, GetCone.

    ICamApiLoop

    Member Description
    IsOuter true for the outer (boundary) loop
    CoEdges ICamApiCoEdgeIterator for the co-edges in this loop
    GetNext() Next loop on the same face (null after last)
    GetNurbsForm(needUVCurve) NURBS form of the loop boundary

    ICamApiCoEdge

    Member Description
    Geometry ICamApiSurfaceCurve — the trimming curve on the surface
    Orientation Whether the curve direction agrees with the loop traversal direction
    Edge The underlying ICamApiEdge (3D curve) this co-edge runs along

    Iterate co-edges

    using var iterCom = loopCom.InvokeAndWrap(l => l.CoEdges);
    foreach (var coEdgeCom in iterCom.AsEnumerable())
    {
        bool orientation = coEdgeCom.Orientation();
        using var curveCom = coEdgeCom.Geometry();
        var startPt = curveCom.Invoke(c => c.StartPoint);
        coEdgeCom.Dispose();
    }
    

    Helper classes: FaceHelper, CoEdgeHelper, CoEdgeIteratorHelper in CAMAPI.DotnetHelper.

    ICamApiEdge

    The 3D curve running between two vertices — the geometry a co-edge sits on (coEdge.Edge()). Geometry only; adjacency and convexity come from ICamApiEdgeAnalyzer (below).

    Property Description
    StartPoint, EndPoint Edge endpoints in model space (TST3DPoint)
    Length Straight-line distance between the endpoints

    Helper: EdgeHelper (StartPoint, EndPoint, Length).

    ICamApiSurfaceCurve

    A curve living on a surface (the 3D trim curve of a co-edge).

    Property/Method Description
    TMin, TMax Parameter range
    StartPoint, EndPoint 3D start/end points
    StartNormal, EndNormal Surface normal at start/end
    StartTangent, EndTangent Curve tangent at start/end
    SaveToReceiver(receiver, tMin, tMax, tol) Discretise into polyline via ICamApiAbstractCurveReceiver

    ICamApiFaceList

    A flat list of faces, e.g. from ICAMAPIGeometryModel.GetFaceListOfSelected().

    using var faceListCom = modelCom.InvokeAndWrap(m =>
        (m.GetFaceListOfSelected(out var status), status));
    
    int count = faceListCom.Count();
    for (int i = 0; i < count; i++)
    {
        using var faceCom = faceListCom.Face(i);
        // ...
    }
    

    GetMesh(index, tol, holeCappingSize, fromThread) on ICamApiFaceList triangulates the i-th face with optional hole capping.

    Edge adjacency and convexity — ICamApiEdgeAnalyzer

    Imported CAM geometry has no shared edges between faces — each face carries its own trim curves, so there is no built-in "which two faces meet at this edge" relation. ICamApiEdgeAnalyzer reconstructs that: it matches coincident per-face edges within a tolerance and classifies each edge as convex / concave / smooth / boundary. Pass all faces of a solid so boundary (free) edges are detected correctly.

    It is a singleton extension — resolve it via EdgeAnalyzerHelper.GetSingleton().

    Interface Member Description
    ICamApiEdgeAnalyzer CreateFaceList() Create an empty ICamApiFaceListBuilder
    Build(faces, tolerance, out status) Analyze the face set → ICamApiEdgeFaceInfoList
    ICamApiFaceListBuilder AddFace(face) / AddRange(faces) Accumulate faces (AddFace surfaces failures via TResultStatus)
    Build() Immutable ICamApiFaceList snapshot
    ICamApiEdgeFaceInfoList Count, Item[i] Per-edge results
    GetByEdge(edge) Info for a given ICamApiEdge (endpoint match); null if none
    ICamApiEdgeFaceInfo FaceA, FaceB The two adjacent faces; FaceB is null for a boundary edge
    IsConvex, IsConcave, IsSmooth, IsBoundary Edge classification
    using var analyzerCom = EdgeAnalyzerHelper.GetSingleton();
    
    using var builderCom = analyzerCom.CreateFaceList();
    builderCom.AddRange(faceListCom);              // ICamApiFaceList of the whole solid
    using var facesCom = builderCom.Build();
    
    using var edgeInfoListCom = analyzerCom.Build(facesCom, 0.001);
    for (int i = 0; i < edgeInfoListCom.Count(); i++)
    {
        using var infoCom = edgeInfoListCom.Item(i);
        if (infoCom.IsConcave())
        {
            using var faceACom = infoCom.FaceA();
            using var faceBCom = infoCom.FaceB();   // IsNull for a boundary edge
            // ... concave edge between faceA and faceB (fillet/pocket candidate)
        }
    }
    

    Helpers: EdgeAnalyzerHelper, FaceListBuilderHelper, EdgeFaceInfoListHelper, EdgeFaceInfoHelper.

    Shape descriptors — ICamApiShapeAnalyzer

    Where ICamApiEdgeAnalyzer answers how faces connect, ICamApiShapeAnalyzer answers what a shape looks like numerically: it computes shape descriptors — form characteristics of a single face or a whole face set — intended for shape recognition and AI classification.

    Also a singleton extension: ShapeAnalyzerHelper.GetSingleton().

    Descriptors come at two levels:

    • Level 1 — scale-dependent: areas, volume, bounding box, centroid, curvature radii, face and edge counts. Real millimetres; two parts of the same shape and different size differ here.
    • Level 2 — scale-invariant: ratios, normalized inertia, sphericity, distance histogram. Dimensionless, so the same shape at any size gives the same numbers. This is what GetVector() packs for a classifier.
    Method Description
    CreateFaceList() Empty ICamApiFaceListBuilder (same builder as the edge analyzer)
    BuildFaceInfo(face, tolerance) Descriptor of one face → ICamApiShapeFaceInfo. Throws on error
    BuildBodyInfo(faces, tolerance) Descriptor of a face set → ICamApiShapeBodyInfo. Throws on error

    tolerance drives tessellation, curvature sampling and edge matching — the same value used for the edge analyzer is a reasonable choice.

    ICamApiShapeFaceInfo — level 1: Area, BoxMin, BoxMax, MinCurvatureRadius, MaxCurvatureRadius, LoopCount (1 = simple face, ≥2 = face with holes), SurfaceKind; level 2: CurvatureRatio (≈1 when the face bends alike in both directions like a sphere or plane, ≈0 when one direction is straight like a cylinder or cone), GetNormalizedInertia(), GetVector().

    ICamApiShapeBodyInfo — level 1: IsClosed, Volume, TotalArea, BoxMin, BoxMax, Centroid, FaceCount, GetFaceCountByKind(kind), ConvexEdgeCount, ConcaveEdgeCount, SmoothEdgeCount, BoundaryEdgeCount, GetPrincipalInertia(...); level 2: Sphericity (exactly 1 for a ball), GetNormalizedInertia(), GetDistanceHistogram(), GetVector().

    using var analyzerCom = ShapeAnalyzerHelper.GetSingleton();
    
    using var builderCom = analyzerCom.CreateFaceList();
    builderCom.AddRange(faceListCom);            // ALL faces of the solid
    using var facesCom = builderCom.Build();
    
    using var bodyCom = analyzerCom.BuildBodyInfo(facesCom, 0.001);
    if (bodyCom.IsClosed())
        Console.WriteLine($"volume {bodyCom.Volume():0.##}, sphericity {bodyCom.Sphericity():0.###}");
    
    // Scale-invariant feature vector for a classifier
    double[] vector = bodyCom.GetVector();
    int schema = bodyCom.VectorSchemaVersion();
    
    // Per-face descriptor
    using var faceInfoCom = analyzerCom.BuildFaceInfo(faceCom, 0.001);
    if (faceInfoCom.SurfaceKind() == TCamApiSurfaceKind.skCylinder)
        Console.WriteLine($"cylinder R≈{faceInfoCom.MinCurvatureRadius():0.###}");
    

    Always check VectorSchemaVersion before feeding GetVector() to a trained model — the vector's meaning and order are versioned, and a classifier trained on one version must not consume another.

    Pass every face of the solid to BuildBodyInfo. Closedness, boundary-edge count and volume are derived from the set you hand in — a partial set silently reports as an open shell with Volume = 0.

    Curvature radii return MaxFloat (not infinity, not an error) where there is no finite curved direction — a planar face, or the straight direction of a cylinder or cone. Guard for that before using them in arithmetic.

    Volume, Sphericity and the volume-based inertia are meaningless for an open face set: volume and sphericity read 0, and inertia falls back to surface-area weighting.

    GetDistanceHistogram() is estimated from a fixed-size sample of the tessellation, so identical shapes agree closely rather than bit-exactly — compare with a tolerance.

    Helpers: ShapeAnalyzerHelper, ShapeFaceInfoHelper, ShapeBodyInfoHelper (the last two wrap the indexed VectorValue / DistanceHistogramBin properties as plain double[]).


    8. ICamApiSurface — underlying surface

    Each ICamApiFace owns one ICamApiSurface. The primary use is to obtain the NURBS representation.

    using var surfaceCom = faceCom.Surface();
    using var nurbsCom = surfaceCom.InvokeAndWrap(s => s.GetNurbsForm());
    

    ICamApiNurbsSurface

    A two-dimensional B-spline surface.

    Property Description
    Degree[i] Polynomial degree in direction i (0=U, 1=V)
    IsClosed[i] Closed flag in direction i
    IsRational Rational (NURBS) vs. non-rational (B-spline)
    KnotCount[i] Number of knots in direction i
    Knot[i][j] j-th knot in direction i
    CPCount[i] Control point count in direction i
    ControlPoint[i][j] Control point at (i,j) as TST3DPoint
    ControlWeight[i][j] Weight at (i,j) (only meaningful when IsRational = true)

    9. ICamApiCurve and ICamApiCurve5D

    ICamApiCurve

    A parametric 3D curve with knot-point access and arc-length operations.

    Member Description
    QntP Number of knot points
    KnotPoint[i] i-th knot point (TST3DPoint)
    TMin, TMax Parameter range
    FullLen Total arc length
    IsClosed Closed curve flag
    Box Bounding box (TST3DBox)
    Get_Point(t) Evaluate position at parameter t
    Get_UnitTangent(t, isForward) Normalized tangent at t
    Get_Len(t1, t2) Arc length between t1 and t2
    FindNearestPoint(p, t1, t2) Parameter of closest point on [t1,t2] to p
    LenToParameter(len) Convert arc length to parameter
    MakeStepByLen(t, step, out residual) Walk step distance from t; returns new parameter
    SavePartToReceiver(receiver, t1, t2, tol) Discretise [t1,t2] into a polyline
    Inverse() Reverse curve direction in-place

    Sampling a curve as a polyline

    // Implement ICamApiAbstractCurveReceiver to collect points
    class PolylineReceiver : ICamApiAbstractCurveReceiver
    {
        public List<TST3DPoint> Points = new();
        public void StartCurve3D(TST3DPoint p) => Points.Add(p);
        public void CutTo3D(TST3DPoint p) => Points.Add(p);
        public void StopCurve(bool isClosed) { }
    }
    
    var receiver = new PolylineReceiver();
    curveCom.Invoke(c => c.SavePartToReceiver(receiver, c.TMin, c.TMax, 0.01));
    

    NURBS form of a curve

    using var nurbsCom = curveCom.InvokeAndWrap(c =>
        ((ICamApiAbstractCurve)c).GetNurbsForm());
    // nurbsCom is ICamApiAbstractNurbsCurve
    

    ICamApiAbstractNurbsCurve

    Property Description
    Degree Polynomial degree
    Is3d True if 3D, false if 2D (UV)
    IsRational True for NURBS, false for B-spline
    KnotCount Total knot count
    Knot[i] i-th knot value
    CPCount Control point count
    CP[i] i-th control point (TST3DPoint)
    CW[i] i-th weight

    ICamApiCurve5D

    An optional interface, also implemented by curve objects, that exposes 5-axis tool-axis information.

    curveCom.Invoke(c =>
    {
        if (c is ICamApiCurve5D curve5D)
        {
            var pt5D = curve5D.Get_Point5D(t); // TST5DPoint — position + tool axis
        }
    });
    

    ICamApiCurveArcsReceiver

    An extended receiver that can accept arc segments in addition to line segments. Implement both ICamApiAbstractCurveReceiver and ICamApiCurveArcsReceiver to receive full arc-accurate output.

    Method Description
    ArcTo3D(pc, ep, R) Arc from current point to ep with signed radius R (positive = CCW)
    AddCircle(pc, R) Full 360-degree circle at center pc

    10. ICamApiMesh — triangulated representation

    A triangulated mesh obtained from ICamApiFace.GetMesh(tol) or ICamApiFaceList.GetMesh(...).

    Method Description
    GetVertexCount() Total number of vertices
    GetTriangleCount() Total number of triangles
    GetVertex(i) TST3DPoint for the i-th vertex
    GetTriangle(i) TST3IPoint — three vertex indices for the i-th triangle
    GetTriangleNormal(i) TST3DPoint — outward normal for the i-th triangle
    using var meshCom = faceCom.GetMesh(0.05); // tolerance in model units
    
    int vtxCount  = meshCom.GetVertexCount();
    int triCount  = meshCom.GetTriangleCount();
    
    for (int i = 0; i < triCount; i++)
    {
        var tri = meshCom.GetTriangle(i);    // .X .Y .Z are indices into the vertex array
        var n   = meshCom.GetTriangleNormal(i);
        var v0  = meshCom.GetVertex(tri.X);
    }
    

    Helper class: MeshHelper in CAMAPI.DotnetHelper.


    11. ICamApiCoordinateSystem

    Coordinate systems appear in the geometry tree as nodes with EntityType == etCS. They can also be managed through a dedicated ICamApiListCoordinateSystem.

    ICamApiCoordinateSystem properties

    Property Type Access Description
    Name string R Unique name
    Parent ICamApiCoordinateSystem R Parent CS (null = global)
    Matrix TST3DMatrix R/W 4×4 matrix relative to global CS
    Color int R/W Display color
    using var csCom = ComWrapper.Create(csEntity.Matrix);
    var origin = csCom.Matrix().vT;   // translation part
    

    ICamApiListCoordinateSystem

    Manages a collection of named coordinate systems. Typically obtained from the active project.

    Method Description
    Count Number of CS in the list
    CoordinateSystem[index] Access by zero-based index
    Add(name, matrix, parentName) Add a new CS
    Remove(name) Remove a CS by name
    GetByName(name) Find a CS by name
    GetActive The currently active CS
    SetActive(name) Make a CS active
    // listCsCom is ComWrapper<ICamApiListCoordinateSystem>
    listCsCom.Invoke(list =>
    {
        list.Add("MyCS", matrix, "", out var status);
        list.SetActive("MyCS", out status);
    });
    

    Helper class: CoordinateSystemHelper in CAMAPI.DotnetHelper.


    12. ICAMAPIGeometryImporter — importing files

    The importer is obtained from the active project or from ICAMAPIGeomLibrary.CreateGeometryImporter().

    // Obtain from project (most common in extensions)
    using var importerCom = activeProjectCom.InvokeAndWrap(p => p.GeomImporter);
    

    ICAMAPIGeometryImporter members

    Member Description
    GeometryModel R/W — the target ICAMAPIGeometryModel
    ImportFile(filePath, targetFolder, showDialog) Import; targetFolder="" → current folder; showDialog=true → shows import options UI

    Basic import

    using var importerCom = activeProjectCom.InvokeAndWrap(p => p.GeomImporter);
    importerCom.ImportFile(@"C:\models\part.igs", "", false);
    
    // After import, the imported root node is available via ActiveNode
    var importedName = modelCom.Invoke(m => m.ActiveNode.FullName);
    

    Import with dialog

    Pass showDialog: true to let the user configure format-specific options:

    importerCom.ImportFile(filePath, "", showDialog: true);
    

    Using GeomImporterHelper

    // Set a different target model (standalone usage)
    importerCom.SetGeometryModel(anotherModelCom);
    importerCom.ImportFile(filePath, "SubFolder", false);
    

    Helper class: GeometryImporterHelper in CAMAPI.DotnetHelper.

    See full example: Geometry/ExtensionUtilityGeometryImporterNet/project/main/ExtensionGeometryImporter.cs

    Direct access (IDL): ImportFile returns a TResultStatus by value. The helper throws on rsError.


    13. ICamApiGeomPicker — interactive picking

    The geometry picker shows a modal dialog that lets the user select nodes from the geometry tree. Results are delivered asynchronously via a callback.

    The picker is created as a system extension (not through ICAMAPIGeomLibrary):

    using var geomPickerCom = SystemExtensionFactory.CreateExtension<ICamApiGeomPicker>(
        "Extension.GeomPicker");
    

    ICamApiGeomPicker members

    Member Description
    AvailableEntityTypes Bitmask of TGeometryEntityTypeFlag values indicating which entity types the user may select
    OnClose An ICamApiGeomPickerOnClose callback implementation
    Show() Display the picker dialog (non-blocking; result arrives via callback)
    Title R/W — window title of the dialog; empty string keeps the default
    ButtonCount Number of customizable buttons
    ButtonType[i] Role of the button at index i — TCamApiGeomPickerButton (gpbOk, gpbCancel, gpbClear, gpbDelete)
    ButtonCaption[i], ButtonVisible[i], ButtonEnabled[i], ButtonHint[i] R/W per-button caption / visibility / enabled state / tooltip

    Customizing the dialog

    Set Title and tweak buttons before Show(). GeomPickerHelper finds a button by its role, so you don't index by position:

    geomPickerCom.SetTitle("Pick faces to protect");
    geomPickerCom.SetButtonCaption(TCamApiGeomPickerButton.gpbOk, "Protect");
    geomPickerCom.SetButtonVisible(TCamApiGeomPickerButton.gpbClear, false);
    geomPickerCom.SetButtonHint(TCamApiGeomPickerButton.gpbDelete, "Remove from list");
    

    Helper: GeomPickerHelper — Title/SetTitle, ButtonCount, ButtonType, FindButton, and Set{Caption,Visible,Enabled,Hint} overloads keyed by index or by TCamApiGeomPickerButton.

    ICamApiGeomPickerOnClose

    Implement this interface to receive the user's selection:

    class MyOnClose : ICamApiGeomPickerOnClose
    {
        public void OnConfirm(IListString selectedItems)
        {
            // selectedItems contains the FullName strings of selected nodes
            for (int i = 0; i < selectedItems.Count(); i++)
                Console.WriteLine(selectedItems.Get(i));
        }
    
        public void OnCancel()
        {
            Console.WriteLine("User cancelled");
        }
    }
    

    Full picker usage

    if (geomPickerCom.IsNull)
        throw new Exception("Could not create picker");
    
    geomPickerCom.Invoke(picker =>
    {
        // Allow faces and edges
        picker.AvailableEntityTypes = (ushort)(
            TGeometryEntityTypeFlag.etfFace | TGeometryEntityTypeFlag.etfEdge);
    
        picker.OnClose = new MyOnClose();
        picker.Show();
    });
    
    // The extension must implement IExtensionLazyUnloadable and not unload
    // until OnConfirm or OnCancel has been called.
    

    See full example: Geometry/ExtensionUtilityGeometryPickerNet/project/main/ExtensionGeometryPicker.cs

    Note: because Show() is non-blocking, the hosting extension must implement IExtensionLazyUnloadable and set CanUnload = true only inside the callback.


    14. IExtensionGeomModelNodePopup — context menu

    This extension point lets an extension add items to the right-click context menu that appears when the user right-clicks a node in the 3D model tree.

    Registration

    Implement IExtensionGeomModelNodePopup in your extension class:

    public class MyPopupExtension : IExtension, IExtensionGeomModelNodePopup
    {
        public IExtensionInfo? Info { get; set; }
    
        public void Build(IExtensionGeomModelNodePopupBuildContext context,
                          out TResultStatus resultStatus)
        {
            resultStatus = default;
            context.NodePopup.AddItem(
                "MyAction",          // unique identifier
                "Do something",      // visible caption (empty string = separator)
                enabled: true,
                new MyOnClicked(),
                out resultStatus);
        }
    }
    

    IExtensionGeomModelNodePopupBuildContext

    Property Description
    SelectedNode The node that was right-clicked
    ActiveProject The currently active project
    NodePopup The ICamApiGeomModelNodePopup to add items to

    ICamApiGeomModelNodePopup

    Method Description
    AddItem(name, caption, enabled, onClicked) Add a menu item
    GetItems() Returns IListString of item names in order
    GetItem(name) Find an existing item by name
    Clear() Remove all items

    Handling a click

    public class MyOnClicked : ICamApiGeomModelNodePopupItemOnClicked
    {
        public void OnItemClicked(
            IExtensionGeomModelNodePopupItemOnClickedContext context,
            out TResultStatus resultStatus)
        {
            resultStatus = default;
            using var nodeCom = ComWrapper.Create(context.SelectedNode);
            Console.WriteLine("Clicked: " + nodeCom.FullName());
        }
    }
    

    See full example: GeometryModelNodePopup/NodeFullNameAlert/project/main/


    15. ICMAPITurnGeneratrixExtractor — lathe generatrix

    Extracts the generatrix (profile) curve from a geometry node representing a turned part. Obtained from ICAMAPIGeomLibrary.CreateTurnGeneratrixExtractor().

    Properties

    Property Type Description
    Receiver ICamApiAbstractCurveReceiver Receiver that collects the output polyline
    Tolerance double Chord tolerance for generatrix approximation
    SewTolerance double Tolerance for joining adjacent curve segments
    NeedJoinCurves bool Whether to merge adjacent segments into one curve
    TurnAxis TST5DPoint Rotation axis (position + direction)
    NeedCloseToAxis bool Whether to extend the profile to the rotation axis

    Usage

    using var extractorCom = geomLibCom.CreateTurnGeneratrixExtractor();
    extractorCom.Invoke(e =>
    {
        e.Tolerance = 0.01;
        e.TurnAxis = myAxis;
        e.Receiver = myReceiver;
    });
    geomNodeCom.Invoke(node =>
        extractorCom.Invoke(e => e.MakeGeneratrixForNode(node, out var status)));
    

    16. Export helpers

    ICamApiFacesToTriangulatedFilesConverter

    Converts a ICamApiFaceList to STL or OSD without requiring node selection. Obtained from ICAMAPIGeomLibrary.CreateFacesToTriangulatedFilesConverter().

    Member Description
    Tolerance Chord tolerance for tessellation
    Color Color to embed in output file
    SaveFacesToSTL(faces, fileName) Write faces to STL
    SaveFacesToOSD(faces, fileName) Write faces to OSD
    using var converterCom = geomLibCom.InvokeAndWrap(lib =>
        lib.CreateFacesToTriangulatedFilesConverter());
    
    converterCom.Invoke(c => c.Tolerance = 0.05);
    faceListCom.Invoke(list =>
        converterCom.Invoke(c => c.SaveFacesToSTL(list, @"C:\out\part.stl", out var status)));
    

    17. Entity type reference

    TCAMAPIGeometryEntityType enum

    Value Name Specialised interface
    0 etGroup — (container node only)
    1 etPoint —
    2 etCurve ICamApiCurveGeometryEntity → ICamApiCurve
    3 etMesh —
    4 etFace ICamApiFaceGeometryEntity → ICamApiFace
    5 etVertex —
    6 etEdge —
    7 etCS ICamApiCSGeometryEntity (matrix only)
    8 etPmi — (PMI annotation)
    9 etView — (saved view)

    TGeometryEntityTypeFlag bitmask

    Used in ICamApiGeomPicker.AvailableEntityTypes:

    Flag Value Meaning
    etfGroup 1
    etfPoint 2
    etfCurve 4
    etfMesh 8
    etfFace 16
    etfVertex 32
    etfEdge 64
    etfCS 128
    etfPmi 256
    etfView 512

    18. ICamApiGeometryModelSketcher — creating primitives

    The sketcher creates simple geometry (points, lines, polylines, splines) in the Job geometry sub-tree — the same content a user would draw by hand. It is not a separate object: the geometry model itself implements ICamApiGeometryModelSketcher, so obtain it by QueryInterface from the model wrapper.

    using var modelCom = new ComWrapper<ICAMAPIGeometryModel>(activeProject.CAMAPIGeomModel);
    using var sketcherCom = modelCom.InvokeAndWrap(m => m as ICamApiGeometryModelSketcher);
    

    Helper class: GeometryModelSketcherHelper. Every method returns the created ICAMAPIGeometryTreeNode (dispose it) and throws on engine error.

    Helper method Creates
    AddPoint(x, y, z) A single point
    AddLine(p1, p2) A line segment between two TST3DPoint
    AddNormalLine(origin, endPoint) A "normal line" anchored at origin, ending at endPoint
    StartPolyline() Begins a polyline — returns an ICamApiSpatialCurveBuilder
    StartSpline() Begins a smooth spline — returns an ICamApiSpatialCurveBuilder

    Sketching in a local coordinate system

    By default all coordinates are Global. To sketch in a local CS instead — on a machined face, in a workpiece CS — there are two ways:

    TargetCS sets a sticky CS applied to every subsequent AddPoint, AddLine, AddNormalLine, StartPolyline and StartSpline. The matrix maps sketch-local coordinates to world, the same convention as ICamApiCoordinateSystem.Matrix; identity means Global. SetTargetCS accepts either a raw TST3DMatrix or a ComWrapper<ICamApiCoordinateSystem>.

    sketcherCom.SetTargetCS(csCom);                          // everything below is CS-local
    using (var ptCom = sketcherCom.AddPoint(0, 0, 0)) { }    // origin of that CS
    

    One-shot variants — pass the CS as an extra last argument and TargetCS is neither read nor changed. Each sketch method has two such overloads, taking a TST3DMatrix or a ComWrapper<ICamApiCoordinateSystem>:

    Helper overload Creates
    AddPoint(x, y, z, cs) Point in cs
    AddLine(p1, p2, cs) Line in cs
    AddNormalLine(origin, endPoint, cs) Normal line in cs
    StartPolyline(cs) Polyline builder whose knots are in cs
    StartSpline(cs) Spline builder whose knots are in cs
    using var ptCom = sketcherCom.AddPoint(0, 0, 0, csCom);   // TargetCS untouched
    

    The underlying IDL methods are AddPointInCS, AddLineInCS, AddNormalLineInCS, StartPolylineInCS, StartSplineInCS — the helpers expose them as overloads of the plain names instead.

    TargetCS is sticky state on the sketcher, which is the geometry model itself — so it stays until you set it back. Prefer the one-shot overloads for occasional geometry, or restore the previous matrix (GetTargetCS()) when you are done.

    Multi-knot curves — ICamApiSpatialCurveBuilder

    StartPolyline / StartSpline return a builder; append knots, then Finish() to commit the curve into the tree and receive its node. Helper class: SpatialCurveBuilderHelper.

    Helper method Description
    AddKnot(p) Append a knot TST3DPoint (throws on error)
    Finish() Commit accumulated knots; returns the new node. The builder is exhausted afterwards
    TryAddKnot(p, out status) / TryFinish(out status) Non-throwing variants exposing the raw TResultStatus
    using var sketcherCom = modelCom.InvokeAndWrap(m => m as ICamApiGeometryModelSketcher);
    
    // Simple primitives
    using (var ptCom = sketcherCom.AddPoint(0, 0, 0)) { }
    using (var lineCom = sketcherCom.AddLine(
        new TST3DPoint { X = 0, Y = 0, Z = 0 },
        new TST3DPoint { X = 50, Y = 0, Z = 0 })) { }
    
    // A polyline built from several knots
    using var builderCom = sketcherCom.StartPolyline();
    builderCom.AddKnot(new TST3DPoint { X = 0,  Y = 0,  Z = 0 });
    builderCom.AddKnot(new TST3DPoint { X = 50, Y = 0,  Z = 0 });
    builderCom.AddKnot(new TST3DPoint { X = 50, Y = 50, Z = 0 });
    using var polylineNodeCom = builderCom.Finish();
    

    19. ICamApiPointSnapper — snapping points to faces

    The point snapper projects measured 3D points onto the nearest position on a set of faces — the building block for probing / measurement alignment. It is a singleton system extension, obtained through its helper (no direct CAMAPI.TechSolvers reference needed).

    using var snapperCom = PointSnapperHelper.GetSingleton(); // ICamApiPointSnapper
    

    Helper class: PointSnapperHelper. The snapper hands out two lightweight mutable collections:

    Helper method Returns Description
    CreateFaceList() ComWrapper<ICamApiFaceListBuilder> An empty, mutable face list builder to fill with target faces
    CreatePointList() ComWrapper<ICamApiPoint3DList> An empty point list
    FindNearestOnFaces(faces, points, tolerance) TST3DPoint[] For each input point, the nearest position on any face — result [i] corresponds to input [i]. faces is an ICamApiFaceList
    FindNearestOnFacesRaw(...) ComWrapper<ICamApiPoint3DList> Same, but returns the COM list (caller owns it)

    ICamApiPoint3DList (helper Point3DListHelper): Count(), Item(i), Add(point). ICamApiFaceListBuilder (helper FaceListBuilderHelper) accumulates faces via AddFace(face) / AddRange(faceList) and produces the immutable ICamApiFaceList snapshot FindNearestOnFaces expects with Build().

    using var snapperCom = PointSnapperHelper.GetSingleton();
    
    using var facesBuilderCom = snapperCom.CreateFaceList();
    facesBuilderCom.AddRange(selectedFacesCom);   // e.g. modelCom.GetFaceListOfSelected()
    using var facesCom = facesBuilderCom.Build();  // immutable ICamApiFaceList snapshot
    
    using var pointsCom = snapperCom.CreatePointList();
    pointsCom.Add(new TST3DPoint { X = 10.2, Y = 4.8, Z = 0.1 });
    pointsCom.Add(new TST3DPoint { X = 22.0, Y = 4.9, Z = 0.0 });
    
    TST3DPoint[] snapped = snapperCom.FindNearestOnFaces(facesCom, pointsCom, tolerance: 0.5);
    // snapped[i] is the closest on-surface position to the i-th measured point
    

    Direct access (IDL): ICamApiPointSnapper, ICamApiFaceListBuilder, and ICamApiPoint3DList live in CAMAPI.TechSolvers. FindNearestOnFaces carries an out TResultStatus that the helper checks for you.

    In this article