NX UG二次开发 《Getting Started with NX Open》 第五章 概念与架构 C#

(原为英文版,翻译为机器自动翻译,原代码为vb.net,本人翻译成C#,因个人能力有限,有错误之处请指出。)

本章介绍NX Open的总体结构和一些基本原则。标准的NX Open参考指南告诉您如何调用NX Open中可用的数千个函数中的任何一个,但许多人发现很难看到“全貌”,因此他们不知道从何开始。本章介绍NX Open编程背后的概念模型,以便更容易找到所需的函数。

  • NX开放的级别

NX的编程接口经过多年的发展。尽管较新的API已经取代了早期版本,并且不再进行增强,但早期版本仍然受到支持,并且仍然有效。这些较旧的工具包括一个名为“User Function”或“UFUNC”的API,该API旨在支持用Fortran或C语言编写的应用程序。User Function C API的名称随后被更改,现在称为NX Open C API,有时仅称为Open C API。按照今天的标准,这个API是过时的,但它非常丰富,文档相当充分,并且仍然被广泛使用。NXOpen.NET API的很大一部分(称为NXOpen.UF命名空间)实际上是通过围绕NXOpenC函数构建“包装器”而创建的。NXOpen.UF函数不在记录的日志中使用,因此很容易忘记它们,但它们非常有用。新的NX Open.NET函数直接构建在内部NX函数之上,因此它们绕过NX Open C层。SNAP层构建在NX Open.NET之上。

NX UG二次开发 《Getting Started with NX Open》 第五章 概念与架构 C#_第1张图片

  • 关于NXOpen.UF的更多信息

如上所述,NXOpen中有许多有用的功能。UF命名空间。例如:

  • NXOpen.UF.Curve:使用曲线的许多函数
  • NXOpen.UF.Disp:显示功能(颜色、网格、视图名称等)
  • NXOpen.UF.Draw:使用图形和工程视图的功能
  • NXOpen.UF.Drf:  用于处理绘图对象(如标注)的函数
  • NXOpen.UF.UFEval:关于曲线和曲面(点、切线、法线等)的信息
  • NXOpen.UF.UFModl:大量零件建模功能
  • NXOpen.UF.UFPath:使用NC刀具路径的功能
  • NXOpen.UF.UFSf:用于处理有限元模型(节点、元素、网格)的函数

您可以将这些函数与较新的函数一起使用,但混合需要一些简单的转换,如本章稍后标题为“对象和标记”的部分所述。

  • NX开放继承层次结构

与大多数现代软件系统一样,NX对象类以分层结构排列,较低级别的项继承自较高级别的项。有数百种不同的对象类型,因此整个画面很难理解(甚至很难绘制)。下面的简化图向我们展示了从层次结构顶部向下到一些简单常用对象的路径。

NXRemotableObject

    TaggedObject

         NXObject

                 DisplayableObject

                        Body

                        DatumAxis

                        DatumPlane

                        Edge

                        Face

                        FacetedBody

                       Sketch

                       SmartObject

                              Point

                              Curve

                                   Line

                                   Conic

                                            Arc

                                            Ellipse

                                  Spline

                            CoordinateSystem

                            Axis

                            Direction

                            Plane

                            Scalar

                           Xform

                Feature

                      BodyFeature

                               Sphere

                NXMatrix

                Expression

                BasePart

                          Part

     Builder

               FourPointSurfaceBuilder

               FeatureBuilder

                        BooleanBuilder

                        SphereBuilder

    BaseSession

              Session

TaggedObjectCollection

        BaseFeatureCollection

        PartCollection

        FeatureCollection

MathUtils

因此,我们看到Point是一种“SmartObject”,它是一种”DisplayableObject“,等等。稍后将给出详细信息,但简单地说,以下是更重要的对象类型的角色:

RemotableObject (可删除对象)

用于首选项集合,也是“UF”类的基础

TaggedObject (标记的对象)

用于对象列表、选择和“构建器”(稍后描述)

NXObject (NX对象)

用于零件对象,以及位于NX零件文件中但未显示的对象(视图、布局、表达式、灯光等)。NXObjects具有名称和其他非图形属性。

DisplayableObject (可显示对象)

包括用户熟悉的大多数对象类型。注释、实体、刻面实体、基准对象、CAE对象等。可显示对象具有颜色、字体和其他外观属性。请注意,NX功能不是可显示的对象。

SmartObject (智能对象)

包括点、曲线和在实现关联性时用作其他对象组件的某些对象类型。

  • 会话和部分

典型的NX对象(我们在这里讨论的对象)位于零件文件中。因此,如果我们想要创建一个新对象,我们首先要做的就是确定将在其中创建该对象的零件文件。通常,您需要在当前工作部件中创建新对象,因此所需代码为:

NXOpen.Session mySession = NXOpen.Session.GetSession(); //获取当前NX会话

Dim parts NXOpen.PartCollection = mySession.Parts; //获得当前会话零件集  

NXOpen.Part workPart = parts.Work; //获得工作部件

如您所见,我们首先通过调用GetSession函数获取当前NX会话对象。每个会话对象都有一个名为“Parts”的PartCollection对象,这是我们在第二行代码中获得的。然后我们从这个PartCollection中获取工作部件。当然,一如既往,我们可以通过将ImportsNXOpen放在代码文件的顶部来减少键入。

除了工作部分之外,还有其他有用的对象,您可能希望在程序开始时初始化这些对象。示例有显示部分、“UI”对象、“Display”对象、UFSession对象等。因此,您将在许多NX Open程序的顶部附近看到这样的代码:

NXOpen.Session theSession = NXOpen.Session.GetSession();
NXOpen.Part theWorkPart = theSession.Parts.Work;
NXOpen.Part theDisplayPart = parts.Display;
NXOpen.UF.UFSession theUfSession = NXOpen.UF.UFSession.GetUFSession();
NXOpen.DisplayManager theDisplay = theSession.DisplayManager;
NXOpen.UI theUI = NXOpen.UI.GetUI();
  • 对象和标记

如前所述,NXOpen中有许多有用的功能。UF命名空间。但是NXOpen。UF函数不使用前面描述的对象类型;他们使用对象“标签”。典型代码如下:

double[] coords = new double[] {1.5, 2.5, 7};

NXOpen.Tag pointTag= Tag.Null;

theUfSession.Curve.CreatePoint(coords, pointTag);

theUfSession.Obj.SetLayer(pointTag, 30);

注意CreatePoint函数如何不返回NXOpen.Point对象,它返回NXOpen。它创建的点的标记(pointTag)。然后,每当我们想在后续代码中引用这一点时,我们都使用这个标记。例如,在最后一行代码中,pointTag用作SetLayer函数的输入。

我们可以将此与使用更新的基于对象的函数执行相同操作的代码进行对比:

NXOpen.Point3d coordsPt = new NXOpen.Point3d(1.5, 2.5, 7);

NXOpen.Point myPoint = theWorkPart.Points.CreatePoint(coordsPt);

myPoint.Layer = 30;

当然,有时您需要混合使用NXOpen。UF函数和较新的函数,因此了解对象和标记之间的关系非常重要。在一个方向上,对应关系非常简单:如果您有一个名为myObject的NX对象,那么就是myObject。标签给你它的标签。因此,我们可以这样做:

NXOpen.Point myPoint = theWorkPart.Points.CreatePoint(coordsPt);

NXOpen.Tag pointTag = myPoint.Tag;

theUfSession.Obj.SetLayer(pointTag, 30);

在相反的方向(从标记到对象),过程稍微复杂一些:

NXOpen.Tag pointTag;

theUfSession.Curve.CreatePoint(coords, pointTag);

NXOpen.TaggedObject obj = NXOpen.Utilities.NXObjectManager.Get(pointTag);

NXOpen.Point myPoint = (NXOpen.Point) obj;

myPoint.Layer = 30;

如您所见,调用NXObjectManager。Get函数为我们提供了NXOpen.TaggedObject,然后我们将其转换为NXOpen.Point类型。在实践中,您可能会通过使用隐式强制转换来缩短此时间,如下所示:

Dim myPoint NXOpen.Point = NXOpen.Utilities.NXObjectManager.Get(pointTag);
  • Factory对象

在NX Open中,通常不会通过调用构造函数来创建对象。相反,您使用“create”函数,该函数是某个“factory”对象的成员函数。“工厂”概念在软件工程领域是众所周知的——就像在现实生活中一样,工厂是一个生产新产品的地方。不同类型的对象使用不同类型的工厂。通常,您可以从NXOpen获取合适的工厂对象。零件对象(通常是工作零件)或NXOpen。会话对象。例如,假设我们想在名为myPart的零件中创建一个点。相关的工厂对象是名为myPart.Points的PointCollection对象。因此,CreatePoint函数可以在PointCollection类中找到,您可以按如下方式使用它来创建点:

Point3d coords = new Point3d(3, 5, 0); //定义点的坐标

PointCollection points = myPart.Points; //获取myPart的PointCollection

Point p1 = points.CreatePoint(coords); //创建点
 

有时,工厂对象提供直接创建对象的函数,如上面的示例所示。其他时候,工厂对象提供了创建“构建器”对象的函数,如下一节所述。下表显示了工厂对象的一些常见示例、如何获取这些工厂对象的实例以及它们提供的创建函数类型:

Factory对象类型 

Factory对象实例

创建函数示例

PointCollection(点合集)

BasePart.Points

(基准部件.点)

CreatePoint(创建点)

 CreateQuadrantPoint(创建象限点)

CurveCollection(曲线合集)

BasePart.Curves

(基本部件.曲线)

CreateLine (创建直线)

CreateArc(创建圆弧)

CreateEllipse(创建椭圆)

Features.FeatureCollection

(功能合集)

 Part.Features

(部件.特征)

 CreateSphereBuilder (创建球)CreateDatumPlaneBuilder(创建面)

 CreateExtrudeBuilder  (创建挤出生成器)

CreateStudioSplineBuilder(创建曲线)

Annotations.AnnotationManager(注释管理器)

Part.Annotations

(零件.注释)

CreateNote(创建注释)

 CreateLabel(创建标签)

CreateDraftingNoteBuilder(创建草稿注释生成器)

CAE.NodeElementManager(CAE.Node元素管理器)

CAE.BaseFEModel.NodeElementMgr

CreateNodeCreateBuilder (创建节点创建生成器)CreateElementCreateBuilder(创建元素创建生成器)

CAM.OperationCollection
(CAM.操作集合)

CAM.CAMSetup.CAMOperationCollection

CreateHoleMakingBuilder CreateFaceMillingBuilder

CAM.NCGroupCollection.(CAM.NC组集合)

 CAM.CAMSetup.CAMGroupCollection

CreateMillToolBuilder CreateDrillSpotfaceToolBuilder

DexManager

Session.DexManager

CreateIgesImporter

(创建Iges导入程序)

CreateStep203Creator

(创建Step203创建者)

绘图管理器

 PlotManager

BasePart.PlotManager

CreateCgmBuilder

CreatePrintBuilder

下面是一些显示工厂对象及其简单创建函数的进一步示例:

NXOpen.Point3d coords = new NXOpen.Point3d(3, 5, 9);
NXOpen.PointCollection pointFactory = workPart.Points;
object myPoint = pointFactory.CreatePoint(coords);
NXOpen.Point3d p1 = new NXOpen.Point3d(1, 6, 5);
NXOpen.Point3d p2 = new NXOpen.Point3d(3, 2, 7);
NXOpen.CurveCollection curveFactory = workPart.Curves;
object myLine = curveFactory.CreateLine(p1, p2);
string[] text = new string[] {"Height", "Diameter", "Cost"};
NXOpen.Point3d origin = new NXOpen.Point3d(3, 8, 0);
NXOpen.AxisOrientation horiz = NXOpen.AxisOrientation.Horizontal;
NXOpen.Annotations.AnnotationManager noteFactory;
noteFactory = workPart.Annotations;
object myNote = noteFactory.CreateNote(new string[] {"Height", "Diameter", "Cost"}, origin, horiz, null, null);

在这段代码中,我们明确定义了工厂对象,以强调它们所扮演的角色。但是,在典型的代码中,它们不会被明确提及,您只需编写:

NXOpen.Point3d coords = new NXOpen.Point3d(3, 5, 9);
object myPoint = workPart.Points.CreatePoint(coords);
NXOpen.Point3d p1 = new NXOpen.Point3d(1, 6, 5);
NXOpen.Point3d p2 = new NXOpen.Point3d(3, 2, 7);
object myLine = workPart.Curves.CreateLine(p1, p2);
string[] text = new string[] {"Height", "Diameter", "Cost"};
NXOpen.Point3d origin = new NXOpen.Point3d(3, 8, 0);
NXOpen.AxisOrientation horiz = NXOpen.AxisOrientation.Horizontal;
object myNote = workPart.Annotations.CreateNote(new string[] {"Height", "Diameter", "Cost"}, origin, horiz, null, null);
  • 对象集合

在许多情况下,上一节中描述的工厂对象的名称中包含单词“collection”。这是因为,除了其对象创建职责之外,工厂对象还为我们提供了一种在零件文件中循环使用特定类型对象的方法。如果要在对象之间循环,对每个对象执行一些操作,这很有用。例如,要对给定零件文件(myPart)中的所有点执行某些操作,可以编写:

foreach (Point pt in myPart.Points)
{
	// Do something with pt
}

更正式地说,PointCollection类实现了IEnumerable接口,这意味着它是一个可以使用For Each循环循环的项集合。

  • 生成器模式

我们在上面看到了各种工厂对象如何提供函数,如CreatePoint、CreateLine和CreateNote,这些函数允许我们直接创建简单对象。然而,如果我们试图以这种方式处理更复杂的对象,我们将需要具有大量输入参数的创建函数。为了避免这种复杂性,我们首先创建一个“构建器”对象,然后使用它来创建所需的对象。例如,以下是构建球体特征的代码:

NXOpen.Features.Sphere nullSphere = null;
NXOpen.Features.SphereBuilder mySphereBuilder;
mySphereBuilder = workPart.Features.CreateSphereBuilder(nullSphere);
mySphereBuilder.Type = < whatever you want>;
mySphereBuilder.CenterPoint = < whatever you want>;
mySphereBuilder.Diameter.Value = < whatever you want>;
mySphereBuilder.BooleanOption.Type = < whatever you want>;
NXOpen.NXObject myObject = mySphereBuilder.Commit();
mySphereBuilder.Destroy();

该代码使用所谓的“生成器模式”,这是一种用于创建复杂对象的著名软件工程技术。一般方法是

  • 创建“生成器”对象行[3]
  • 根据需要修改其属性-行[4]、[5]、[6]、[7]
  • 提交”生成器以创建新的对象行[8]

因此,正如我们所看到的,除了Sphere类之外,还有一个相应的SphereBuilder类和一个名为CreateSphereBuilder的函数,该函数生成一个基本的SphereBuilder对象。NX开放参考指南将帮助您找到所需的类和函数。明确地

  • Sphere类将您引向SphereBuilder类
  • SphereBuilder类将您引用CreateSphereBuilder函数
  • CreateSphereBuilder函数表示它由工厂类“FeatureCollection”提供
  • FeatureCollection类告诉您从Part对象(例如workPart.Features)获取实例

实际上,您可以将CreateSphereBuilder函数用于创建或编辑目的:如果您输入现有的Sphere对象,那么Commit方法将编辑该球体;如果您输入Nothing,那么Commit方法将创建一个新的球体对象,如上面的代码所示。

在这个简单的例子中,需要设置的各种生成器属性的含义非常明显。但是,每当您对生成器属性的含义有疑问时,您可以查看交互式NX中相应的功能创建对话框。您将看到生成器属性与对话框中显示的选项密切相关。

NX UG二次开发 《Getting Started with NX Open》 第五章 概念与架构 C#_第2张图片

 Commit函数返回NXOpen.NXObject,在大多数情况下,它不会立即有用。在进一步使用对象之前,通常必须强制转换为更特定的类型(在上面的示例中为NXOpen.Features.Sphere)。“feature”对象的生成器也有一个CommitFeature方法。这将返回一个非常普通的NXOpen.Features.Feature对象,因此在许多情况下仍然需要强制转换。您可以显式执行强制转换,也可以使用赋值语句隐式执行,如下所示:

NXOpen.Features.SphereBuilder builder;

//两个步骤:提交和显式转换
NXOpen.NXObject myObject = builder.Commit();
 NXOpen.Features.Sphere mySphere1 = (NXOpen.Features.Sphere)myObject;

//一步:隐式提交和强制转换
NXOpen.Features.Sphere mySphere2 = (NXOpen.Features.Sphere)builder.CommitFeature();

在某些情况下,Commit和CommitFeature方法都返回Nothing,您必须使用GetCommittedObjects函数来获取创建的对象。

  • 通过日志记录探索NX Open

NX开放API非常丰富和深入——它有数千个可用功能。这种丰富性有时会使您很难找到所需的功能。幸运的是,如果您知道如何在NX中使用相应的交互函数,日志记录工具会告诉您要使用哪些NX Open函数,甚至还会为您编写一些示例代码。选择“开发人员”选项卡 杂志 录制以开始录制,运行所需的一系列步骤,然后选择“开发人员”选项卡 杂志 停止录制。日志生成的代码很冗长,通常很难阅读。但这是值得坚持的,因为隐藏在代码中的是一个示例调用,它向您展示了所需的函数。您可以通过选择

文件”选项卡 偏好 用户界面 工具 杂志可用的选项有C#、C++、Java、Python和Visual Basic。

  • FindObject”问题

当您使用日志作为应用程序的起点时,需要做的一件事就是删除日志生成的“FindObject”调用。本节告诉您如何做到这一点。

日志记录您在录制过程中执行的确切事件。如果您在录制过程中选择了一个对象,并对其执行了一些操作,则日志实际上会记录该对象的名称。因此,当您回放日志时,操作将再次应用于同一个命名对象。这几乎肯定不是您想要的-您可能希望对某个新选择的对象进行操作,而不是对您在日志记录期间选择的对象。通常,在重放日志时,具有原始记录名称的对象甚至不存在,因此您会收到错误消息。

为了进一步澄清,让我们举一个具体的例子。假设您的模型中有两个对象-两个球体,分别名为SPHERE(23)和SPHERE)(24)。如果您记录了一个日志,其中选择了模型中的所有对象,然后将其留空,那么日志中记录的内容将如下所示:

 DisplayableObject[] objects1 = new DisplayableObject[2];

Body body1 = (Body)(workPart.Bodies.FindObject("SPHERE(23)"));

objects1[0] = body1;

Body  body2 = (Body)(workPart.Bodies.FindObject("SPHERE(24)"));

objects1[1] = body2;

theSession.DisplayManager.BlankObjects(objects1);

如果您回放此代码,它将再次尝试清空SPHERE(23)和SPHERE)(24),这可能是无用的。当你回放日志时,SPHERE(23)和SPHERE)很有可能不存在,即使它们存在,也不太可能是你想要空白的对象。显然,我们需要去掉“FindObject”调用,并添加一些逻辑,以便更好地定义要清空的对象集。有几种可能的情况:

  • 也许我们想清空一些由应用程序前面的代码创建的对象
  • 也许我们想在应用程序运行时清空用户选择的一些对象
  • 也许我们想清空模型中的所有对象,或者所有具有特定特征的对象

第一个很简单:如果我们在自己的代码中创建了对象,那么我们可能会将它们分配给程序变量,并且它们很容易识别:

Body myBall0 = Sphere(1, 2, 1, 5).Body;
Body myBall1 = Sphere(1, 4, 3, 7).Body;
DisplayableObject[] objects1 = new DisplayableObject[2];
objects1[0] = myBall0;
objects1[1] = myBall1;
theSession.DisplayManager.BlankObjects(objects1);
 

对于第二种情况,我们需要在代码中添加一个选择步骤,如第15章所述,然后在回放日志时清空用户选择的对象。类似于:

Dim cue = "Please select the objects to be blanked" 
Dim dialog As Selection.Dialog = Selection.SelectObjects(cue) 
Dim result As Selection.Result = dialog.Show 
If result.Response <> NXOpen.Selection.Response.Cancel 
Then theSession.DisplayManager.BlankObjects(result.Objects) 
End If

'上述代码不知如何翻译为C#

对于第三种情况(清空所有具有特定特征的对象),我们需要循环遍历模型中的所有对象,找到符合我们标准的对象,然后将其传递给BlankObjects函数。有关在零件文件中循环浏览对象的信息,请参见第15章的最后一节。

  • 混合SNAPNX打开

正如我们所看到的,NX Open功能提供了巨大的功能和灵活性。还有一个NX API叫做SNAP,其函数通常更容易查找和理解。因此,在某些情况下,您可能需要同时使用SNAP和NX Open函数。您可以将SNAP函数用于简单的通用操作,将NX Open函数用于更复杂的操作。为此,您可能需要将SNAP对象转换为NX Open对象,反之亦然。我们已尽力使这些转换尽可能方便,以便SNAP和NX Open代码能够和平和谐地共存。

SNAP对象只是对应NX Open对象的简单包装器-例如,SNAP.NX.Spline对象只是封装NXOpen.Spline的包装器,而SNAP.NX.Sphere是NXOpen.Features.Sphere对象的包装器等等。因此,如果您有NXOpen对象,可以“包装”它以创建SNAP.NX对象。在另一个方向上,如果您有Snap.NX对象,则可以“展开”它以获得它所包含的NXOpen对象。有一些隐藏的“隐式”转换可以为您进行包装和展开,所以通常情况下,这些转换只需要做一些工作而不需要额外的努力。例如:

Snap.NX.Point snapPoint = Snap.Create.Point(3, 5, 9);
NXOpen.Session session = NXOpen.Session.GetSession(); 
session.Information.DisplayPointDetails(snapPoint);
NXOpen.Point3d pt = snapPoint.Position;
 

在第三行中,我们将一个Snap.NX.Point对象传递给一个期望接收NXOpen.Point的函数。但是隐式转换是在幕后调用的,而函数调用只是按预期工作。类似地,在第四行中,我们将Snap.Position对象指定给NXOpen.Point3d对象,这也是有效的。

然而,有时隐式转换不起作用,您需要做一些更明确的事情。例如,如果要使用NXOpen成员函数或属性,则必须首先从SNAP对象中获取NXOpen对象。例如,假设我们有一个名为snapSphere的Snap.NX.Sphere对象,我们编写了以下代码:

//Snap.NX.Sphere snapSphere =null;
snapSphere.HideParents(); // Fails
object version = snapSphere.TimeStamp; // Fails
 

这两行代码都将失败,因为Snap.NX.Sphere对象没有HideParents方法或TimeStamp属性。因此,要继续,必须“展开”以获得封闭的NXOpen.Feature。球体对象。

您可以通过两种不同的方式完成此操作,如下所示:

//Snap.NX.Sphere snapSphere=null;
((NXOpen.Features.Sphere)snapSphere).HideParents(); //工作,但有点笨拙
snapSphere.NXOpenSphere.HideParents();//比较好:使用NXOpenSphere属性

第一行仅使用标准的VBCType函数进行转换,第二行使用NXOpenSphere属性。第二种方法是使用属性,这是最方便的,因此有几个属性可以让您以相同的方式从SNAP对象获取NXOpen对象。例如,如果snapSphere是Snap.NX。球体对象,然后

  • snapSphere.NXOpenSphere 是封闭的 NXOpen.Features.Sphere 对象
  • snapSphere.NXOpenTag  是NXOpen.Features.Sphere 对象的NXOpen标记.             
  • snapSphere.SphereBuilder  是NXOpen.Features.Sphere 的“生成器”对象

朝着另一个方向(从NXOpen到SNAP)前进并不是那么简单。使用属性的方法不可用,因此必须调用Wrap函数从NXOpen对象创建一个新的SNAP对象,如下所示:

NXOpen.Point3d coords = new NXOpen.Point3d(3, 6, 8);
NXOpen.Part workPart = Snap.Globals.WorkPart.NXOpenPart;
NXOpen.Point nxopenPoint = workPart.Points.CreatePoint(new NXOpen.Point3d(3, 6, 8));
Snap.NX.Point snapPoint = Snap.NX.Point.Wrap(nxopenPoint.Tag); // 创建 Snap.NX.Point
Position location = snapPoint.Position; // 使用其Position属性

在第四行代码中,我们首先获得NXOpen.Point对象的标签。然后我们调用Wrap函数,它为我们提供了一个新的Snap.NX。指向“包裹”它的对象。然后,在最后一行中,我们可以使用这个新Snap.NX.Point对象的Position属性。

如上所述,Wrap函数接收NXOpen。标记为输入。因此,如果您使用的是使用标记的旧NXOpen函数,那么与SNAP的互操作性就更容易了。例如:

NXOpen.UF.UFSession theUfSession = NXOpen.UF.UFSession.GetUFSession();
NXOpen.Tag pointTag;
double[] coords = new double[] { 2, 6, 9 };
theUfSession.Curve.CreatePoint(coords, out pointTag);
Snap.NX.Point snapPoint = Snap.NX.Point.Wrap(pointTag);

你可能感兴趣的:(UGOpen,C#,c#,开发语言)