RK3568-Android15-强制横竖屏-默认App应用界面显示方向-屏幕显示方向跟随Sensor旋转
2026/7/19 14:11:01
代码逻辑:选实体→包裹几何体提取平面 / 直线边→生成候选坐标系→计算各坐标系下包围盒体积→取体积最小坐标系创方块
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) { List<double> 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 List<Tag> 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); List<Tag> selectedBodies = new List<Tag>(); 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); } /// <summary>ACS绝对坐标转局部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); } /// <summary>double[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]); } /// <summary>Matrix3x3 转 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, List<Tag> 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 List<Tag> GetBodyFaces(Tag body) { List<Tag> faces = new List<Tag>(); 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 List<Tag> GetFaceEdges(Tag face) { List<Tag> edges = new List<Tag>(); 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(List<Matrix3x3> 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(List<Tag> 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(List<Tag> solidBodies) { if (solidBodies.Count == 0) return false; Tag originWcs = GetWCS(); double[] orgOrigin = new double[3], orgMat = new double[9]; AskCsysInfo(originWcs, orgOrigin, orgMat); List<Matrix3x3> calcMats = new List<Matrix3x3>(); 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; List<Tag> faces = GetBodyFaces(auxBody); foreach (Tag face in faces) { List<Tag> 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() { List<Tag> bodies = GetSelectedBodies(); CreateSolidBodyMinBoxBlock(bodies); } #endregion } }