/// <summary> /// Testing a point inside an optimized type of polyhedron, i.e. cuboid. /// This optimized version is very fast since it only compares a position compared to a AABB. It is designed for testing a point inside an Octree cell /// </summary> /// <param name="cuboidPolyH">Caller is resposible to ensure that the Polyhedron is really a cuboid</param> /// <param name="aPoint">a point</param> /// <returns></returns> public static bool insideCuboid(Polyhedron cuboidPolyH, Point3D aPoint) { if ((aPoint.X >= cuboidPolyH.boundingBox.LLB.X && aPoint.X <= cuboidPolyH.boundingBox.URT.X) && (aPoint.Y >= cuboidPolyH.boundingBox.LLB.Y && aPoint.Y <= cuboidPolyH.boundingBox.URT.Y) && (aPoint.Z >= cuboidPolyH.boundingBox.LLB.Z && aPoint.Z <= cuboidPolyH.boundingBox.URT.Z) ) { return(true); } else { return(false); } }
/// <summary> /// Testing a point inside a polyhedron is similar with the 2D version of a point inside a polygon by testing intersection between a ray starting from the point. /// If the number of intersection is odd the point is inside. In 3D the intersection is against the Faces /// </summary> /// <param name="polyH"></param> /// <param name="aPoint"></param> /// <returns></returns> public static bool inside(Polyhedron polyH, Point3D aPoint) { double extent = 0; if (Octree.WorldBB == null) { extent = Point3D.distance(polyH.boundingBox.LLB, polyH.boundingBox.URT); } else { extent = Octree.WorldBB.extent; } // define a ray using linesegment from the point toward and along +X-axis with 2*the extent of the World BB to ensure ray is always long enough LineSegment3D ray = new LineSegment3D(aPoint, new Point3D(aPoint.X + 2 * extent, aPoint.Y, aPoint.Z)); List <Face3D> reducedList = Face3D.inclFacesBeyondAxis(polyH.Faces, new Plane3D(aPoint, new Vector3D(1.0, 0.0, 0.0))); // beyond YZ plane List <Point3D> corners = new List <Point3D>(); corners.Add(aPoint); corners.Add(aPoint); BoundingBox3D bound = new BoundingBox3D(corners); if (reducedList.Count > 0) { reducedList = Face3D.exclFacesOutsideOfBound(reducedList, bound, 0x011); // reduce list on Y and Z both direction } if (reducedList.Count == 0) { return(false); // no faces left, either they are all on the left or they are all on the right } int iCount = 0; for (int i = 0; i < reducedList.Count; i++) { List <Point3D> intPts = new List <Point3D>(); if (Face3D.intersect(reducedList[i], ray, out intPts)) { iCount++; } } if ((iCount % 2) == 1) { return(true); } return(false); }
/// <summary> /// To determine that a line segment is inside (completely inside) of a polyhedron, it must satisfy the following: /// 1. both end points are inside the polyhedron /// 2. There no intersection between the segment and the polyhedron /// </summary> /// <param name="polyH"></param> /// <param name="lineS"></param> /// <returns></returns> public static bool inside(Polyhedron polyH, LineSegment3D lineS) { // reducing the face candidate list is less expensive than inside test, do it first Point3D leftX = new Point3D(); Point3D rightX = new Point3D(); leftX.X = lineS.startPoint.X < lineS.endPoint.X ? lineS.startPoint.X : lineS.endPoint.X; rightX.X = lineS.startPoint.X < lineS.endPoint.X ? lineS.endPoint.X : lineS.startPoint.X; List <Face3D> reducedList = Face3D.inclFacesBeyondAxis(polyH.Faces, new Plane3D(leftX, new Vector3D(1.0, 0.0, 0.0))); // reducedList = Face3D.exclFacesBeyondAxis(reducedList, rightX); // cannot remove this otherwise inside test for StartPoint may not be correct!!! List <Point3D> corners = new List <Point3D>(); corners.Add(lineS.startPoint); corners.Add(lineS.endPoint); BoundingBox3D bound = new BoundingBox3D(corners); if (reducedList.Count > 0) { reducedList = Face3D.exclFacesOutsideOfBound(reducedList, bound, 0x011); // reduce list on Y and Z both direction } if (reducedList.Count == 0) { return(false); // no faces left, either they are all on the left or they are all on the right } // inside test for both segment ends. Test one by one so that we can exit when any one of them are not inside if (!inside(polyH, lineS.startPoint)) { return(false); } if (!inside(polyH, lineS.endPoint)) { return(false); } // Now test whether there is any intersection. If there is, the segment is not completely inside for (int i = 0; i < reducedList.Count; i++) { List <Point3D> iPoints = new List <Point3D>(); if (Face3D.intersect(reducedList[i], lineS, out iPoints)) { return(false); } } return(true); }
public static void Process(OctreeNode node, Polyhedron polyH) { // Do octree traversal here (from the leaves that are the result of the previous step if (node._children.Count() == 0) //leaf node { if (node._flag != PolyhedronIntersectEnum.Disjoint) { Process(node, polyH, polyH.Faces); // Process the leaf node if it is not Disjoint } } else { foreach (OctreeNode chldNode in node._children) // recursive to get all the leaf nodes { Process(chldNode, polyH); } } }
/// <summary> /// Compute Octree for a Polyhedron /// </summary> /// <param name="elementID"></param> /// <param name="polyH"></param> /// <param name="forUserDict"></param> public void ComputeOctree(string elementID, Polyhedron polyH, bool forUserDict) { // Make sure ElementID string is 22 character long for correct encoding/decoding if (elementID.Length < 22) { elementID = elementID.PadLeft(22, '0'); } ElementID eidNo = new ElementID(elementID); Tuple <UInt64, UInt64> elementIDNo = eidNo.ElementIDNo; OctreeNode theTree = new OctreeNode(); // Do it in steps: // 1. Find the smallest containing cell based on the PolyH BB, it it to quickly eliminate the irrelevant cells very quickly theTree.nodeCellID = OctreeNodeProcess.getSmallestContainingCell(polyH); theTree._depth = theTree.nodeCellID.Level; // 2. Perform subdivision using the BB first: quick division since there is no expensive intersection. It leaves all the leaves based on BB OctreeNodeProcess.ProcessBB(theTree, polyH.boundingBox); // 3. Evaluate each leaf nodes for further subdivision using the actual polyhedron (the original algorithm) OctreeNodeProcess.Process(theTree, polyH); List <CellID64> collCellID; List <int> collBorderFlag; OctreeNodeProcess.collectCellIDs(theTree, out collCellID, out collBorderFlag); for (int i = 0; i < collCellID.Count; ++i) { if (forUserDict) { insertDataToUserDict(elementIDNo, collCellID[i], collBorderFlag[i], false); } else { //insertDataToDictDB(elementID, collCellID[i]); insertDataToDict(elementIDNo, collCellID[i]); } } }
/// <summary> /// Test intersection between a polyhedron and a face. There is optmization applied for axis-aligned face (useful for Octree cells as they are all axis aligned) /// </summary> /// <param name="polyH">The Polyhedron</param> /// <param name="face">The face to test the intersection</param> /// <returns>true=intersected; false otherwise</returns> public static bool intersect(Polyhedron polyH, Face3D face) { List <Face3D> faceList = new List <Face3D>(); BoundingBox3D bound = new BoundingBox3D(face.vertices); faceList = Face3D.exclFacesOutsideOfBound(polyH.Faces, bound, 0x111); if (faceList.Count == 0) { return(false); // There is no face remaining to test, return false } for (int i = 0; i < faceList.Count; i++) { FaceIntersectEnum mode; LineSegment3D intL = new LineSegment3D(new Point3D(), new Point3D()); bool status = Face3D.intersect(face, faceList[i], out intL, out mode); if (status == true) { return(true); // return true as soon as an intersection is detected } } return(false); }
/// <summary> /// Testing intersection between a line segment and a polyhedron only. It stops at the first intersection /// </summary> /// <param name="polyH">polyhedron</param> /// <param name="lineS">Line segment</param> /// <returns></returns> public static bool intersect(Polyhedron polyH, LineSegment3D lineS) { List <Point3D> corners = new List <Point3D>(); corners.Add(lineS.startPoint); corners.Add(lineS.endPoint); BoundingBox3D bound = new BoundingBox3D(corners); List <Face3D> reducedList = Face3D.exclFacesOutsideOfBound(polyH.Faces, bound, 0x111); if (reducedList.Count == 0) { return(false); // no faces left, either they are all on the left or they are all on the right } // Now test whether there is any intersection. for (int i = 0; i < reducedList.Count; i++) { List <Point3D> iPoints = new List <Point3D>(); if (Face3D.intersect(reducedList[i], lineS, out iPoints)) { return(true); } } return(false); }
public static void Process(OctreeNode node, Polyhedron _polyH, List <Face3D> polyHF) { // 3rd step. Subdivide the cells collected by the step 2 and operate on them with the actual polyhedron to get the detail if (node._depth < Octree.MaxDepth) { int disjointCount = 0; int insideCount = 0; Split(node); List <int> childToRemove = new List <int>(); List <int> childToTraverse = new List <int>(); List <Face3D> faceList; faceList = Face3D.exclFacesOutsideOfBound(polyHF, node.nodeCellCuboid.cuboidPolyhedron.boundingBox, 0x111); if (faceList.Count == 0) { // No face inside this cuboid left, no intersection nor completely enclosing the polyH. node._flag = PolyhedronIntersectEnum.Disjoint; node._children.Clear(); return; } for (int i = 0; i < node._children.Count; i++) { OctreeNode childNode = node._children[i]; //PolyhedronIntersectEnum intS = childNode.Process(polyH); if (Polyhedron.intersect(childNode.nodeCellCuboid.cuboidPolyhedron, faceList)) { childToTraverse.Add(i); childNode._flag = PolyhedronIntersectEnum.Intersect; childNode.nodeCellID.setBorderCell(); #if (DBG_OCTREE) if (childNode._depth >= _dbgDepth) { BIMRLCommon refCommon = new BIMRLCommon(); string dbgFile = "c:\\temp\\octree\\" + childNode.nodeCellID.ToString() + " - intersect polyH.x3d"; BIMRLExportSDOToX3D x3d = new BIMRLExportSDOToX3D(refCommon, dbgFile); x3d.drawCellInX3d(childNode.nodeCellID.ToString()); // draw the cell x3d.exportFacesToX3D(faceList); x3d.endExportToX3D(); } #endif continue; } // If doesn't intersect (passes the check above), either it is fully contained, full contains or disjoint // To optimize the operation, we will use a single sampling point instead of checking the entire polyhedron since a single point can tell if a polyhedron is inside the other one //if (Polyhedron.inside(childNode.nodeCellCuboid.cuboidPolyhedron, polyH)) //// No need to check this since the previous step (no 1) would have removed the fullycontaining cells // Fully contains check only valid if the parent is fully contains, if intersect, it should never be full contains //if (node._flag == PolyhedronIntersectEnum.FullyContains) //{ // if (Polyhedron.insideCuboid(childNode.nodeCellCuboid.cuboidPolyhedron, faceList[0].vertices[0])) // { // // if polyH is entirely inside the cuboid, we will set this for further split (the same as intersection // childToTraverse.Add(i); // We will remove the node if it is disjoint, otherwise it will continue splitting until the condition met // childNode._flag = PolyhedronIntersectEnum.FullyContains; // childNode.nodeCellID.setBorderCell(); // continue; // } //} //if (Polyhedron.inside(polyH, childNode.nodeCellCuboid.cuboidPolyhedron)) if (Polyhedron.inside(_polyH, childNode.nodeCellCuboid.cuboidPolyhedron.Vertices[3])) { childNode._flag = PolyhedronIntersectEnum.Inside; insideCount++; #if (DBG_OCTREE) if (childNode._depth >= _dbgDepth) { BIMRLCommon refCommon = new BIMRLCommon(); string dbgFile = "c:\\temp\\octree\\" + childNode.nodeCellID.ToString() + " - inside polyH.x3d"; BIMRLExportSDOToX3D x3d = new BIMRLExportSDOToX3D(refCommon, dbgFile); x3d.drawCellInX3d(childNode.nodeCellID.ToString()); // draw the cell x3d.exportFacesToX3D(_polyH.Faces); x3d.endExportToX3D(); } #endif continue; } // If the 2 polyH do not intersect, the cuboid does not fully contain the polyH, nor the cuboid is fully inside the polyH, it must be disjoint childNode._flag = PolyhedronIntersectEnum.Disjoint; disjointCount++; #if (DBG_OCTREE) if (childNode._depth >= _dbgDepth) { BIMRLCommon refCommon = new BIMRLCommon(); string dbgFile = "c:\\temp\\octree\\" + childNode.nodeCellID.ToString() + " - disjoint polyH.x3d"; BIMRLExportSDOToX3D x3d = new BIMRLExportSDOToX3D(refCommon, dbgFile); x3d.drawCellInX3d(childNode.nodeCellID.ToString()); // draw the cell x3d.exportFacesToX3D(_polyH.Faces); x3d.endExportToX3D(); } #endif continue; // else: the cuboid is completely inside the polyH, keep } if (disjointCount == 8) { // All children are disjoint. Remove all children and set the node to Disjoint node._children.Clear(); node._flag = PolyhedronIntersectEnum.Disjoint; return; } if (insideCount == 8) { // All children are inside. Remove all children and set the node to Inside node._children.Clear(); node._flag = PolyhedronIntersectEnum.Inside; return; } if (childToTraverse.Count == 1) { OctreeNodeProcess.Process(node._children[childToTraverse[0]], _polyH, faceList); } else if (childToTraverse.Count > 1) { #if (DEBUG_NOPARALLEL) // Non - parallel option for easier debugging foreach (int i in childToTraverse) { OctreeNodeProcess.Process(node._children[i], _polyH, faceList); } #else ParallelOptions po = new ParallelOptions(); po.MaxDegreeOfParallelism = 8; Parallel.ForEach(childToTraverse, po, i => OctreeNodeProcess.Process(node._children[i], _polyH, faceList)); #endif } // If there is any disjoint, we need to keep this node as it is. This should be done after we processed all the children to be traversed!! if (disjointCount > 0 && disjointCount < 8) { return; } int countGrandChildren = 0; // If there is no disjoint, we need to check whether all children are terminal (i.e. child._children.Count == 0) foreach (OctreeNode child in node._children) { countGrandChildren += child._children.Count; } // All children are terminal and no disjoint (by implication of previous steps). Remove children if (countGrandChildren == 0) { node._children.Clear(); node._flag = PolyhedronIntersectEnum.IntersectOrInside; return; } } else { // at _depth == Octree.MaxDepth there is nothing else to do since the test has been done at the parent level and when entering this stage, the test has determined // that the cell is intersected with the polyH } return; }
public bool isInside(LineSegment3D LS) { return(Polyhedron.inside(this._polyHRep, LS)); }
public bool isInside(Face3D F) { return(Polyhedron.inside(this._polyHRep, F)); }
public bool isInside(Polyhedron PH) { return(Polyhedron.inside(this._polyHRep, PH)); }
public bool intersectWith(LineSegment3D LS) { return(Polyhedron.intersect(this._polyHRep, LS)); }
public bool intersectWith(Face3D F) { return(Polyhedron.intersect(this._polyHRep, F)); }
public bool intersectWith(Polyhedron PH2) { return(Polyhedron.intersect(this._polyHRep, PH2)); }
public static CellID64 getSmallestContainingCell(Polyhedron polyH) { return(getSmallestContainingCell(polyH.boundingBox)); }
public bool isInside(Point3D P) { return(Polyhedron.inside(this._polyHRep, P)); }
/// <summary> /// make 3D cube /// </summary> /// <param name="Origin">center</param> /// <param name="Width">Representative X-coordinate</param> /// <param name="Height">Representative Y-coordinate</param> /// <param name="Depth">Representative Z-coordinate</param> public Cuboid(Point3D Origin, double xLength, double yLength, double zLength) { origin = Origin; centroid = new Point3D(); centroid.X = origin.X + 0.5 * xLength; centroid.Y = origin.Y + 0.5 * yLength; centroid.Z = origin.Z + 0.5 * zLength; // Define list of corrdinates for the cuboid vertices to be fed to the polyhedron List <double> cuboidVerCoords = new List <double>(); // Point #1 cuboidVerCoords.Add(origin.X); cuboidVerCoords.Add(origin.Y); cuboidVerCoords.Add(origin.Z); // Point #2 cuboidVerCoords.Add(origin.X + xLength); cuboidVerCoords.Add(origin.Y); cuboidVerCoords.Add(origin.Z); // Point #3 cuboidVerCoords.Add(origin.X); cuboidVerCoords.Add(origin.Y + yLength); cuboidVerCoords.Add(origin.Z); // Point #4 cuboidVerCoords.Add(origin.X + xLength); cuboidVerCoords.Add(origin.Y + yLength); cuboidVerCoords.Add(origin.Z); // Point #5 cuboidVerCoords.Add(origin.X); cuboidVerCoords.Add(origin.Y); cuboidVerCoords.Add(origin.Z + zLength); // Point #6 cuboidVerCoords.Add(origin.X + xLength); cuboidVerCoords.Add(origin.Y); cuboidVerCoords.Add(origin.Z + zLength); // Point #7 cuboidVerCoords.Add(origin.X); cuboidVerCoords.Add(origin.Y + yLength); cuboidVerCoords.Add(origin.Z + zLength); // Point #8 cuboidVerCoords.Add(origin.X + xLength); cuboidVerCoords.Add(origin.Y + yLength); cuboidVerCoords.Add(origin.Z + zLength); // Create list of face index to the list of coordinates representing the cuboid rectangular faces (in four tupple) - there will be 6 faces in total List <int> idxFaceCoords = new List <int>(); // Face #1 - front face idxFaceCoords.Add(0 * 3); idxFaceCoords.Add(1 * 3); idxFaceCoords.Add(5 * 3); idxFaceCoords.Add(4 * 3); // Face #2 - right face idxFaceCoords.Add(1 * 3); idxFaceCoords.Add(3 * 3); idxFaceCoords.Add(7 * 3); idxFaceCoords.Add(5 * 3); // Face #3 - back face idxFaceCoords.Add(3 * 3); idxFaceCoords.Add(2 * 3); idxFaceCoords.Add(6 * 3); idxFaceCoords.Add(7 * 3); // Face #4 - left face idxFaceCoords.Add(2 * 3); idxFaceCoords.Add(0 * 3); idxFaceCoords.Add(4 * 3); idxFaceCoords.Add(6 * 3); // Face #5 - bottom face idxFaceCoords.Add(0 * 3); idxFaceCoords.Add(2 * 3); idxFaceCoords.Add(3 * 3); idxFaceCoords.Add(1 * 3); // Face #6 - top face idxFaceCoords.Add(4 * 3); idxFaceCoords.Add(5 * 3); idxFaceCoords.Add(7 * 3); idxFaceCoords.Add(6 * 3); _polyHRep = new Polyhedron(PolyhedronFaceTypeEnum.RectangularFaces, true, cuboidVerCoords, idxFaceCoords, null); }
/// <summary> /// Process Octree for a line segment /// </summary> /// <param name="_polyH"></param> /// <param name="polyHF"></param> public static void Process(OctreeNode node, LineSegment3D lineSegment) { if (node._depth < Octree.MaxDepth) { int disjointCount = 0; OctreeNodeProcess.Split(node); List <int> childToRemove = new List <int>(); List <int> childToTraverse = new List <int>(); for (int i = 0; i < node._children.Count; i++) { OctreeNode childNode = node._children[i]; if (Polyhedron.intersect(childNode.nodeCellCuboid.cuboidPolyhedron, lineSegment)) { childToTraverse.Add(i); childNode._flag = PolyhedronIntersectEnum.Intersect; childNode.nodeCellID.setBorderCell(); continue; } // If doesn't intersect (passes the check above), either it is fully contained, full contains or disjoint // To optimize the operation, we will use a single sampling point instead of checking the entire polyhedron since a single point can tell if a polyhedron is inside the other one // Fully contains check only valid if the parent is fully contains, if intersect, it should never be full contains if (node._flag == PolyhedronIntersectEnum.FullyContains) { if (Polyhedron.insideCuboid(childNode.nodeCellCuboid.cuboidPolyhedron, lineSegment.startPoint)) { // if polyH is entirely inside the cuboid, we will set this for further split (the same as intersection childToTraverse.Add(i); // We will remove the node if it is disjoint, otherwise it will continue splitting until the condition met childNode._flag = PolyhedronIntersectEnum.FullyContains; childNode.nodeCellID.setBorderCell(); continue; } } // If the Line does not intersect the cuboid, or the cuboid does not fully contain the Line, it must be disjoint childNode._flag = PolyhedronIntersectEnum.Disjoint; disjointCount++; continue; } if (disjointCount == 8) { // All children are disjoint. Remove all children and set the node to Disjoint node._children.Clear(); node._flag = PolyhedronIntersectEnum.Disjoint; return; } if (childToTraverse.Count == 1) { OctreeNodeProcess.Process(node._children[childToTraverse[0]], lineSegment); } else if (childToTraverse.Count > 1) { Parallel.ForEach(childToTraverse, i => OctreeNodeProcess.Process(node._children[i], lineSegment)); } // If there is any disjoint, we need to keep this node as it is. This should be done after we processed all the children to be traversed!! if (disjointCount > 0 && disjointCount < 8) { return; } int countGrandChildren = 0; // If there is no disjoint, we need to check whether all children are terminal (i.e. child._children.Count == 0) foreach (OctreeNode child in node._children) { countGrandChildren += child._children.Count; } // All children are terminal and no disjoint (by implication of previous steps). Remove children if (countGrandChildren == 0) { node._children.Clear(); node._flag = PolyhedronIntersectEnum.IntersectOrInside; return; } } else { // at _depth == Octree.MaxDepth there is nothing else to do since the test has been done at the parent level and when entering this stage, the test has determined // that the cell is intersected with the polyH } return; }
public void ComputeOctree(string elementID, Polyhedron polyH) { ComputeOctree(elementID, polyH, false); }