NX二次开发C#- 实现最小包围盒 代码逻辑选实体→包裹几何体提取平面 / 直线边→生成候选坐标系→计算各坐标系下包围盒体积→取体积最小坐标系创方块using System; using System.Collections.Generic; using System.Linq; using NXOpen; using NXOpen.UF; namespace NXMinBoundingBox { public static class MinBoxUtil { #region 全局常量 public const double Tol 0.01; public const double MaxValue 99999999.99; public static UFSession UfSession UFSession.GetUFSession(); public static Session TheSession Session.GetSession(); public static Part WorkPart TheSession.Parts.Work; #endregion #region 公差工具 /// summary获取建模距离公差/summary public static double GetDisTol() { double tol 0.0001; UfSession.Modl.AskDistanceTolerance(out tol); return tol; } /// summary设置建模距离公差/summary public static bool SetDisTol(double tol) { int errFlag -1; UfSession.Modl.SetDistanceTolerance(tol, out errFlag); return errFlag 0; } /// summary判断数值接近0/summary public static bool IsZero(double val) { return Math.Abs(val) Tol; } #endregion #region 体积计算 public static double CalVolume(double[] lengthWidthHeight) { return lengthWidthHeight[0] * lengthWidthHeight[1] * lengthWidthHeight[2]; } public static double GetMinVolume(double[] lengthWidthHeight) { double stand 10000.0; double vol CalVolume(lengthWidthHeight); if (vol stand) vol / stand; return vol; } /// summary获取三轴最大、最小边长/summary public static void GetMaxMinEdge(double[] edges, out double maxEdge, out double minEdge) { Listdouble tmpVec edges.ToList(); tmpVec.Sort(); minEdge tmpVec[0]; maxEdge tmpVec[2]; } #endregion #region 选择实体对话框 /// summary选择过滤初始化回调/summary public static int InitProc(UFUI.Selection select, IntPtr userData) { int numTriples 1; UFUI.MaskTriple[] maskTriples new UFUI.MaskTriple[] { new UFUI.MaskTriple(UFConstants.UF_solid_type, UFConstants.UF_solid_face_subtype, UFUI.SelFeature.Body) }; int errorCode UfSession.Ui.SetSelMask(select, UFUI.SelMaskMode.ClearAndEnableSpecific, numTriples, maskTriples); return errorCode 0 ? 1 : 0; } /// summary弹出选择框获取选中实体tag/summary public static ListTag GetSelectedBodies() { string message 请选择实体; string title 选择对象; int scope UFUI.SelScope.WorkPart; int response; int count; Tag[] objects; double[] cursor new double[3]; Tag view Tag.Null; UfSession.Ui.SelectWithClassDialog(message, title, scope, InitProc, IntPtr.Zero, out response, out count, out objects, cursor, ref view); ListTag selectedBodies new ListTag(); if (objects ! null count 0) selectedBodies.AddRange(objects); return selectedBodies; } #endregion #region WCS/坐标系矩阵变换 /// summary获取当前WCS tag/summary public static Tag GetWCS() { Tag csysId Tag.Null; UfSession.Csys.AskWcs(out csysId); return csysId; } /// summary读取坐标系原点9阶矩阵/summary public static void AskCsysInfo(Tag csysId, double[] origin, double[] mtx) { Tag matrixId Tag.Null; UfSession.Csys.AskCsysInfo(csysId, out matrixId, origin); UfSession.Csys.AskMatrixValues(matrixId, mtx); } /// summaryACS绝对坐标转局部CS坐标/summary public static void MapPointFromAcsToCS(double[] csOrg, double[] csMtx, double[] pnt) { UfSession.Vec3.Sub(pnt, csOrg, pnt); UfSession.Mtx3.VecMultiply(pnt, csMtx, pnt); } /// summary局部CS坐标转回ACS绝对坐标/summary public static void MapPointFromCsToAcs(double[] csOrg, double[] csMtx, double[] pnt) { UfSession.Mtx3.VecMultiplyTranspose(pnt, csMtx, pnt); UfSession.Vec3.Add(pnt, csOrg, pnt); } /// summarydouble[9] 转 Matrix3x3/summary public static Matrix3x3 TransArrayToMat(double[] mtx) { return new Matrix3x3( mtx[0], mtx[1], mtx[2], mtx[3], mtx[4], mtx[5], mtx[6], mtx[7], mtx[8]); } /// summaryMatrix3x3 转 double[9]/summary public static void TransMatToArray(Matrix3x3 mat, double[] mtx) { mtx[0] mat.Xx; mtx[1] mat.Xy; mtx[2] mat.Xz; mtx[3] mat.Yx; mtx[4] mat.Yy; mtx[5] mat.Yz; mtx[6] mat.Zx; mtx[7] mat.Zy; mtx[8] mat.Zz; } #endregion #region 包围盒计算 /// summary /// 输入自定义坐标系批量实体计算包围盒 /// outputAcsMin/MaxCorner绝对坐标最小最大角点 /// outputWcsLenWidthHeight自定义坐标系下长宽高 /// /summary public static bool AskBoundingBox(double[] inputOri, double[] inputMat, ListTag inputObjects, double[] outputAcsMinCorner, double[] outputAcsMaxCorner, double[] outputWcsLengthWidthHeight) { if (inputObjects.Count 0) return false; Tag oriWcs GetWCS(); Point3d originPt new Point3d(inputOri[0], inputOri[1], inputOri[2]); Matrix3x3 mat33 TransArrayToMat(inputMat); WorkPart.WCS.SetOriginAndMatrix(originPt, mat33); Tag wcsTag GetWCS(); double[] objsBoxInWCS new double[6]; bool firstObj true; foreach (Tag objTag in inputObjects) { double[] minCorner new double[3]; double[][] dir new double[3][] { new double[3], new double[3], new double[3] }; double[] dis new double[3]; UfSession.Modl.AskBoundingBoxExact(objTag, wcsTag, minCorner, dir, dis); if (IsZero(Math.Abs(dis[0]) Math.Abs(dis[1]) Math.Abs(dis[2]))) continue; double[] minPoint new double[3] { minCorner[0], minCorner[1], minCorner[2] }; double[] maxPoint new double[3]; maxPoint[0] minCorner[0] dir[0][0] * dis[0] dir[1][0] * dis[1] dir[2][0] * dis[2]; maxPoint[1] minCorner[1] dir[0][1] * dis[0] dir[1][1] * dis[1] dir[2][1] * dis[2]; maxPoint[2] minCorner[2] dir[0][2] * dis[0] dir[1][2] * dis[1] dir[2][2] * dis[2]; MapPointFromAcsToCS(inputOri, inputMat, minPoint); MapPointFromAcsToCS(inputOri, inputMat, maxPoint); double[] tmpObjBox { minPoint[0], minPoint[1], minPoint[2], maxPoint[0], maxPoint[1], maxPoint[2] }; if (firstObj) { Array.Copy(tmpObjBox, objsBoxInWCS, 6); firstObj false; } else { objsBoxInWCS[0] Math.Min(objsBoxInWCS[0], Math.Min(tmpObjBox[0], tmpObjBox[3])); objsBoxInWCS[1] Math.Min(objsBoxInWCS[1], Math.Min(tmpObjBox[1], tmpObjBox[4])); objsBoxInWCS[2] Math.Min(objsBoxInWCS[2], Math.Min(tmpObjBox[2], tmpObjBox[5])); objsBoxInWCS[3] Math.Max(objsBoxInWCS[3], Math.Max(tmpObjBox[0], tmpObjBox[3])); objsBoxInWCS[4] Math.Max(objsBoxInWCS[4], Math.Max(tmpObjBox[1], tmpObjBox[4])); objsBoxInWCS[5] Math.Max(objsBoxInWCS[5], Math.Max(tmpObjBox[2], tmpObjBox[5])); } } // 计算自定义坐标系长宽高 outputWcsLengthWidthHeight[0] Math.Abs(objsBoxInWCS[3] - objsBoxInWCS[0]); outputWcsLengthWidthHeight[1] Math.Abs(objsBoxInWCS[4] - objsBoxInWCS[1]); outputWcsLengthWidthHeight[2] Math.Abs(objsBoxInWCS[5] - objsBoxInWCS[2]); // 转回绝对坐标 double[] minPtAcs { objsBoxInWCS[0], objsBoxInWCS[1], objsBoxInWCS[2] }; double[] maxPtAcs { objsBoxInWCS[3], objsBoxInWCS[4], objsBoxInWCS[5] }; MapPointFromCsToAcs(inputOri, inputMat, minPtAcs); MapPointFromCsToAcs(inputOri, maxPtAcs); Array.Copy(minPtAcs, outputAcsMinCorner, 3); Array.Copy(maxPtAcs, outputAcsMaxCorner, 3); // 恢复原始WCS UfSession.Csys.SetWcs(oriWcs); return true; } #endregion #region 体/面/边遍历工具 /// summary获取实体所有面tag/summary public static ListTag GetBodyFaces(Tag body) { ListTag faces new ListTag(); UfSession.Modl.CreateList(out UFLISTPTR faceList); UfSession.Modl.AskBodyFaces(body, faceList); UfSession.Modl.AskListCount(faceList, out int count); for (int i 0; i count; i) { UfSession.Modl.AskListItem(faceList, i, out Tag fTag); faces.Add(fTag); } UfSession.Modl.DeleteList(ref faceList); return faces; } /// summary获取面所有边tag/summary public static ListTag GetFaceEdges(Tag face) { ListTag edges new ListTag(); UfSession.Modl.CreateList(out UFLISTPTR edgeList); UfSession.Modl.AskFaceEdges(face, edgeList); UfSession.Modl.AskListCount(edgeList, out int count); for (int i 0; i count; i) { UfSession.Modl.AskListItem(edgeList, i, out Tag eTag); edges.Add(eTag); } UfSession.Modl.DeleteList(ref edgeList); return edges; } /// summary判断是否平面/summary public static bool IsPlanarFace(Tag face) { UfSession.Modl.AskFaceType(face, out int type); return type UFModlFaceType.Planar; } /// summary获取平面中心法向/summary public static bool GetFaceNormal(Tag face, double[] normal) { UfSession.Modl.AskFaceType(face, out int type); if (type ! UFModlFaceType.Planar) return false; double[] uvMinMax new double[4]; UfSession.Modl.AskFaceUvMinmax(face, uvMinMax); double[] uvCenter new double[2] { (uvMinMax[0] uvMinMax[1]) / 2, (uvMinMax[2] uvMinMax[3]) / 2 }; double[] centerPnt new double[3]; double[] u1 new double[3], v1 new double[3], u2 new double[3], v2 new double[3]; double[] radii new double[2]; UfSession.Modl.AskFaceProps(face, uvCenter, centerPnt, u1, v1, u2, v2, normal, radii); return true; } /// summary判断直线边/summary public static bool IsLinearEdge(Tag edge) { UfSession.Modl.AskEdgeType(edge, out int edgeType); return edgeType UFModlEdgeType.Linear; } /// summary获取直线边起点终点/summary public static bool GetLinearEdgeEndPnt(Tag edge, double[] startPnt, double[] endPnt) { if (!IsLinearEdge(edge)) return false; double[] tangent new double[3], pNorm new double[3], bNorm new double[3]; double torsion 0, radCur 0; UfSession.Modl.AskCurveProps(edge, 0, startPnt, tangent, pNorm, bNorm, ref torsion, ref radCur); UfSession.Modl.AskCurveProps(edge, 1, endPnt, tangent, pNorm, bNorm, ref torsion, ref radCur); return true; } /// summary由直线边面法向构建局部坐标系矩阵/summary public static bool GetLinearEdgeMtx(Tag edge, double[] faceNormal, double[] mtx) { double[] startPnt new double[3], endPnt new double[3]; if (!GetLinearEdgeEndPnt(edge, startPnt, endPnt)) return false; double[] linearDir { endPnt[0] - startPnt[0], endPnt[1] - startPnt[1], endPnt[2] - startPnt[2] }; double magnitude 0; double[] unitDir new double[3]; UfSession.Vec3.Unitize(linearDir, Tol, out magnitude, unitDir); UfSession.Vec3.IsParallel(unitDir, faceNormal, Tol, out int isParallel); if (isParallel ! 0) return false; int err UfSession.Mtx3.Initialize(unitDir, faceNormal, mtx); return err 0; } /// summary判断矩阵是否已有平行/垂直匹配坐标系/summary public static bool IsMatVecHasMatInParallelOrPerpendicularStyle(ListMatrix3x3 mats, Matrix3x3 mat) { double[] cmpX { mat.Xx, mat.Xy, mat.Xz }; double[] cmpY { mat.Yx, mat.Yy, mat.Yz }; foreach (Matrix3x3 m in mats) { double[] tX { m.Xx, m.Xy, m.Xz }; double[] tY { m.Yx, m.Yy, m.Yz }; UfSession.Vec3.IsParallel(tX, cmpX, Tol, out int xxPara); UfSession.Vec3.IsParallel(tY, cmpY, Tol, out int yyPara); UfSession.Vec3.IsParallel(tX, cmpY, Tol, out int xyPara); UfSession.Vec3.IsParallel(tY, cmpX, Tol, out int yxPara); UfSession.Vec3.IsPerpendicular(tX, cmpX, Tol, out int xxPerp); UfSession.Vec3.IsPerpendicular(tY, cmpY, Tol, out int yyPerp); UfSession.Vec3.IsPerpendicular(tX, cmpY, Tol, out int xyPerp); UfSession.Vec3.IsPerpendicular(tY, cmpX, Tol, out int yxPerp); if ((xxPara ! 0 yyPara ! 0) || (xyPara ! 0 yxPara ! 0) || (xxPara ! 0 yyPerp ! 0) || (xyPara ! 0 yxPerp ! 0) || (yyPara ! 0 xxPerp ! 0) || (yxPara ! 0 xyPerp ! 0)) { return true; } } return false; } #endregion #region 包裹几何体 创建方块 /// summary创建包裹几何体/summary public static bool CreateWrapGeometry(ListTag bodies, double disTol, out Tag resultBody) { resultBody Tag.Null; if (bodies.Count 0) return false; UFModlWrapGeom wrapData new UFModlWrapGeom(); wrapData.CloseGap UFModlWrapGap.CloseNone; wrapData.DistTol disTol; wrapData.AddOffset 0; wrapData.SplitOffset 0; wrapData.NumGeoms bodies.Count; wrapData.Geometry bodies.ToArray(); wrapData.NumSplits 0; wrapData.Splits null; Tag featureTag Tag.Null; int err UfSession.Modl.CreateWrapGeom(ref wrapData, out featureTag); if (err 0) { UfSession.Modl.AskFeatBody(featureTag, out resultBody); } return resultBody ! Tag.Null; } /// summary按指定坐标系创建方块/summary public static Tag CreateBlock(double[] inputOri, double[] inputMat, double[] inputAcsMinCorner, double[] edges) { Tag oriWcs GetWCS(); Point3d originPt new Point3d(inputOri[0], inputOri[1], inputOri[2]); Matrix3x3 mat33 TransArrayToMat(inputMat); WorkPart.WCS.SetOriginAndMatrix(originPt, mat33); string[] edgeStr { edges[0].ToString(f6), edges[1].ToString(f6), edges[2].ToString(f6) }; Tag obj Tag.Null; UfSession.Modl.CreateBlock1(UFConstants.UF_NULLSIGN, inputAcsMinCorner, edgeStr, out obj); UfSession.Modl.AskFeatBody(obj, out obj); UfSession.Csys.SetWcs(oriWcs); return obj; } #endregion #region 主逻辑生成最小包围盒方块 public static bool CreateSolidBodyMinBoxBlock(ListTag solidBodies) { if (solidBodies.Count 0) return false; Tag originWcs GetWCS(); double[] orgOrigin new double[3], orgMat new double[9]; AskCsysInfo(originWcs, orgOrigin, orgMat); ListMatrix3x3 calcMats new ListMatrix3x3(); double minVolume 0; double[] origin new double[3], matrix new double[9]; double[] minCorner new double[3], maxCorner new double[3], distances new double[3]; AskCsysInfo(originWcs, origin, matrix); Matrix3x3 mat TransArrayToMat(matrix); calcMats.Add(mat); // 初始WCS包围盒 AskBoundingBox(origin, matrix, solidBodies, minCorner, maxCorner, distances); minVolume GetMinVolume(distances); GetMaxMinEdge(distances, out double maxEdgeLen, out double minEdgeLen); double disTol minEdgeLen / 120; // 创建包裹几何体提取特征面边 if (!CreateWrapGeometry(solidBodies, disTol, out Tag auxBody)) return false; ListTag faces GetBodyFaces(auxBody); foreach (Tag face in faces) { ListTag edges GetFaceEdges(face); if (edges.Count 3) continue; if (!GetFaceNormal(face, out double[] faceNormal)) continue; foreach (Tag edge in edges) { double[] tmpMtx new double[9]; if (!GetLinearEdgeMtx(edge, faceNormal, tmpMtx)) continue; Matrix3x3 tmpMat TransArrayToMat(tmpMtx); if (IsMatVecHasMatInParallelOrPerpendicularStyle(calcMats, tmpMat)) continue; AskBoundingBox(origin, tmpMtx, solidBodies, minCorner, maxCorner, distances); double tmpVol GetMinVolume(distances); if (!IsZero(tmpVol) tmpVol minVolume) { minVolume tmpVol; calcMats.Add(tmpMat); } } } // 使用最优坐标系创建方块 Matrix3x3 bestMat calcMats.Last(); double[] bestMatArr new double[9]; TransMatToArray(bestMat, bestMatArr); AskBoundingBox(origin, bestMatArr, solidBodies, minCorner, maxCorner, distances); Tag block CreateBlock(origin, bestMatArr, minCorner, distances); // 删除辅助包裹体 if (auxBody ! Tag.Null) UfSession.Obj.DeleteObject(auxBody); return block ! Tag.Null; } #endregion #region 入口测试函数 public static void MinBoxTest() { ListTag bodies GetSelectedBodies(); CreateSolidBodyMinBoxBlock(bodies); } #endregion } }