/// <summary> /// Returns an array of objects that intersect with the specified bounds, if any. Otherwise returns an empty array. See also: IsColliding. /// </summary> /// <param name="checkBounds">Bounds to check. Passing by ref as it improve performance with structs.</param> /// <param name="result">List result.</param> /// <returns>Objects that intersect with the specified bounds.</returns> public void GetColliding(ref HLBoundingBoxXYZ checkBounds, List <T> result, Filter <T> flt) { // Are the input bounds at least partially in this node? if (!bounds.Intersects(checkBounds)) { return; } // Check against any objects in this node for (int i = 0; i < objects.Count; i++) { if (objects[i].Bounds.Intersects(checkBounds)) { result.Add(objects[i].Obj); } } // Check children if (children != null) { for (int i = 0; i < 8; i++) { children[i].GetColliding(ref checkBounds, result, flt); } } }
/* * /// <summary> * /// Get the total amount of objects in this node and all its children, grandchildren etc. Useful for debugging. * /// </summary> * /// <param name="startingNum">Used by recursive calls to add to the previous total.</param> * /// <returns>Total objects in this node and its children, grandchildren etc.</returns> * public int GetTotalObjects(int startingNum = 0) { * int totalObjects = startingNum + objects.Count; * if (children != null) { * for (int i = 0; i < 8; i++) { * totalObjects += children[i].GetTotalObjects(); * } * } * return totalObjects; * } */ // #### PRIVATE METHODS #### /// <summary> /// Set values for this node. /// </summary> /// <param name="baseLengthVal">Length of this node, not taking looseness into account.</param> /// <param name="minSizeVal">Minimum size of nodes in this octree.</param> /// <param name="loosenessVal">Multiplier for baseLengthVal to get the actual size.</param> /// <param name="centerVal">Centre position of this node.</param> void SetValues(float baseLengthVal, float minSizeVal, float loosenessVal, XYZ centerVal) { BaseLength = baseLengthVal; minSize = minSizeVal; looseness = loosenessVal; Center = centerVal; adjLength = looseness * baseLengthVal; // Create the bounding box. var size = new XYZ(adjLength, adjLength, adjLength); bounds = new HLBoundingBoxXYZ(Center, size); float quarter = BaseLength / 4f; float childActualLength = (BaseLength / 2) * looseness; var childActualSize = new XYZ(childActualLength, childActualLength, childActualLength); childBounds = new HLBoundingBoxXYZ[8]; childBounds[0] = new HLBoundingBoxXYZ(Center + new XYZ(-quarter, quarter, -quarter), childActualSize); childBounds[1] = new HLBoundingBoxXYZ(Center + new XYZ(quarter, quarter, -quarter), childActualSize); childBounds[2] = new HLBoundingBoxXYZ(Center + new XYZ(-quarter, quarter, quarter), childActualSize); childBounds[3] = new HLBoundingBoxXYZ(Center + new XYZ(quarter, quarter, quarter), childActualSize); childBounds[4] = new HLBoundingBoxXYZ(Center + new XYZ(-quarter, -quarter, -quarter), childActualSize); childBounds[5] = new HLBoundingBoxXYZ(Center + new XYZ(quarter, -quarter, -quarter), childActualSize); childBounds[6] = new HLBoundingBoxXYZ(Center + new XYZ(-quarter, -quarter, quarter), childActualSize); childBounds[7] = new HLBoundingBoxXYZ(Center + new XYZ(quarter, -quarter, quarter), childActualSize); }
/// <summary> /// Check if the specified bounds intersect with anything in the tree. See also: GetColliding. /// </summary> /// <param name="checkBounds">Bounds to check.</param> /// <returns>True if there was a collision.</returns> public bool IsColliding(ref HLBoundingBoxXYZ checkBounds, Filter <T> flt) { // Are the input bounds at least partially in this node? if (!GeoUtils.DoBoxesIntersect(bounds, checkBounds, 0)) { return(false); } // Check against any objects in this node for (int i = 0; i < objects.Count; i++) { var obj = objects[i]; if ((flt == null || flt.Invoke(obj.Obj)) && GeoUtils.DoBoxesIntersect(objects[i].Bounds, checkBounds, 0)) { return(true); } } // Check children if (children != null) { for (int i = 0; i < 8; i++) { if (children[i].IsColliding(ref checkBounds, flt)) { return(true); } } } return(false); }
/// <summary> /// Private counterpart to the public Add method. /// </summary> /// <param name="obj">Object to add.</param> /// <param name="objBounds">3D bounding box around the object.</param> void SubAdd(T obj, HLBoundingBoxXYZ objBounds) { // We know it fits at this level if we've got this far // Just add if few objects are here, or children would be below min size if (objects.Count < numObjectsAllowed || (BaseLength / 2) < minSize) { OctreeObject newObj = new OctreeObject { Obj = obj, Bounds = objBounds }; //Debug.Log("ADD " + obj.name + " to depth " + depth); objects.Add(newObj); } else { // Fits at this level, but we can go deeper. Would it fit there? // Create the 8 children int bestFitChild; if (children == null) { Split(); if (children == null) { // Debug.Log("Child creation failed for an unknown reason. Early exit."); return; } // Now that we have the new children, see if this node's existing objects would fit there for (int i = objects.Count - 1; i >= 0; i--) { OctreeObject existingObj = objects[i]; // Find which child the object is closest to based on where the // object's center is located in relation to the octree's center. bestFitChild = BestFitChild(existingObj.Bounds); // Does it fit? if (Encapsulates(children[bestFitChild].bounds, existingObj.Bounds)) { children[bestFitChild].SubAdd(existingObj.Obj, existingObj.Bounds); // Go a level deeper objects.Remove(existingObj); // Remove from here } } } // Now handle the new object we're adding now bestFitChild = BestFitChild(objBounds); if (Encapsulates(children[bestFitChild].bounds, objBounds)) { children[bestFitChild].SubAdd(obj, objBounds); } else { OctreeObject newObj = new OctreeObject { Obj = obj, Bounds = objBounds }; //Debug.Log("ADD " + obj.name + " to depth " + depth); objects.Add(newObj); } } }
// #### PUBLIC METHODS #### /// <summary> /// Add an object. /// </summary> /// <param name="obj">Object to add.</param> /// <param name="objBounds">3D bounding box around the object.</param> /// <returns>True if the object fits entirely within this node.</returns> public bool Add(T obj, HLBoundingBoxXYZ objBounds) { if (!Encapsulates(bounds, objBounds)) { return(false); } SubAdd(obj, objBounds); return(true); }
/// <summary> /// Check if the specified bounds intersect with anything in the tree. See also: GetColliding. /// </summary> /// <param name="checkBounds">bounds to check.</param> /// <returns>True if there was a collision.</returns> public bool IsColliding(HLBoundingBoxXYZ checkBounds, Filter <T> flt = null) { //#if UNITY_EDITOR // For debugging //AddCollisionCheck(checkBounds); //#endif return(rootNode.IsColliding(ref checkBounds, flt)); }
/// <summary> /// Returns an array of objects that intersect with the specified bounds, if any. Otherwise returns an empty array. See also: IsColliding. /// </summary> /// <param name="checkBounds">bounds to check.</param> /// <returns>Objects that intersect with the specified bounds.</returns> public T[] GetColliding(HLBoundingBoxXYZ checkBounds, Filter <T> flt = null) { //#if UNITY_EDITOR // For debugging //AddCollisionCheck(checkBounds); //#endif List <T> collidingWith = new List <T>(); rootNode.GetColliding(ref checkBounds, collidingWith, flt); return(collidingWith.ToArray()); }
// #### PUBLIC METHODS #### /// <summary> /// Add an object. /// </summary> /// <param name="obj">Object to add.</param> /// <param name="objBounds">3D bounding box around the object.</param> public void Add(T obj, HLBoundingBoxXYZ objBounds) { // Add object or expand the octree until it can be added int count = 0; // Safety check against infinite/excessive growth while (!rootNode.Add(obj, objBounds)) { Grow(objBounds.MidPoint - rootNode.Center); if (++count > 20) { //Debug.LogError("Aborted Add operation as it seemed to be going on forever (" + (count - 1) + ") attempts at growing the octree."); return; } } Count++; }
/// <summary> /// Return objects that are within maxDistance of the specified ray. /// </summary> /// <param name="ray">The ray.</param> /// <param name="maxDistance">Maximum distance from the ray to consider.</param> /// <param name="result">List result.</param> /// <returns>Objects within range.</returns> public void GetNearby(ref XYZ point, ref float maxDistance, List <T> result) { // Does the ray hit this node at all? // Note: Expanding the bounds is not exactly the same as a real distance check, but it's fast. // TODO: Does someone have a fast AND accurate formula to do this check? //bounds.ExpandToContain(new XYZ(maxDistance * 2, maxDistance * 2, maxDistance * 2)); HLBoundingBoxXYZ d = new HLBoundingBoxXYZ(bounds.MidPoint, bounds.Size); d.ExpandToContain(new XYZ(bounds.Max.X + maxDistance, bounds.Max.Y + maxDistance, bounds.Max.Z + maxDistance)); d.ExpandToContain(new XYZ(bounds.Min.X - maxDistance, bounds.Min.Y - maxDistance, bounds.Min.Z - maxDistance)); bool intersected = d.Contains(point); d.Size = actualBoundsSize; if (!intersected) { return; } // Check against any objects in this node for (int i = 0; i < objects.Count; i++) { if (System.Math.Abs(objects[i].Pos.DistanceTo(point)) <= maxDistance) { result.Add(objects[i].Obj); } } // Check children if (children != null) { for (int i = 0; i < 8; i++) { children[i].GetNearby(ref point, ref maxDistance, result); } } }
/* * /// <summary> * /// Get the total amount of objects in this node and all its children, grandchildren etc. Useful for debugging. * /// </summary> * /// <param name="startingNum">Used by recursive calls to add to the previous total.</param> * /// <returns>Total objects in this node and its children, grandchildren etc.</returns> * public int GetTotalObjects(int startingNum = 0) { * int totalObjects = startingNum + objects.Count; * if (children != null) { * for (int i = 0; i < 8; i++) { * totalObjects += children[i].GetTotalObjects(); * } * } * return totalObjects; * } */ // #### PRIVATE METHODS #### /// <summary> /// Set values for this node. /// </summary> /// <param name="baseLengthVal">Length of this node, not taking looseness into account.</param> /// <param name="minSizeVal">Minimum size of nodes in this octree.</param> /// <param name="centerVal">Centre position of this node.</param> void SetValues(float baseLengthVal, float minSizeVal, XYZ centerVal) { SideLength = baseLengthVal; minSize = minSizeVal; Center = centerVal; // Create the bounding box. actualBoundsSize = new XYZ(SideLength, SideLength, SideLength); bounds = new HLBoundingBoxXYZ(Center, actualBoundsSize); float quarter = SideLength / 4f; float childActualLength = SideLength / 2; var childActualSize = new XYZ(childActualLength, childActualLength, childActualLength); childBounds = new HLBoundingBoxXYZ[8]; childBounds[0] = new HLBoundingBoxXYZ(Center + new XYZ(-quarter, quarter, -quarter), childActualSize); childBounds[1] = new HLBoundingBoxXYZ(Center + new XYZ(quarter, quarter, -quarter), childActualSize); childBounds[2] = new HLBoundingBoxXYZ(Center + new XYZ(-quarter, quarter, quarter), childActualSize); childBounds[3] = new HLBoundingBoxXYZ(Center + new XYZ(quarter, quarter, quarter), childActualSize); childBounds[4] = new HLBoundingBoxXYZ(Center + new XYZ(-quarter, -quarter, -quarter), childActualSize); childBounds[5] = new HLBoundingBoxXYZ(Center + new XYZ(quarter, -quarter, -quarter), childActualSize); childBounds[6] = new HLBoundingBoxXYZ(Center + new XYZ(-quarter, -quarter, quarter), childActualSize); childBounds[7] = new HLBoundingBoxXYZ(Center + new XYZ(quarter, -quarter, quarter), childActualSize); }
/// <summary> /// Returns an axis aligned box which for extents of only visible geometry /// Solid.GetBoundingBox method includes planes and non-visible geometry /// </summary> /// <param name="geoObj"></param> /// <param name="parentTransform"></param> /// <returns></returns> public static HLBoundingBoxXYZ GetGeoBoundingBox(Solid geoObj, Transform parentTransform) { HLBoundingBoxXYZ box = null; var sld = geoObj as Solid; foreach (var edge in sld.Edges.OfType <Edge>()) { foreach (var pt in edge.Tessellate()) { var ptx = parentTransform.OfPoint(pt); if (box == null) { box = new HLBoundingBoxXYZ(ptx, new XYZ(0.01, 0.01, 0.01)); } else { box.ExpandToContain(ptx); } } } return(box); }
public virtual void ParseFrom(Element elm, MEPRevitGraphWriter writer) { var scannedElements = writer.Cache.ParsedElements; var cpTree = writer.Cache.connectorsCache; var geoTree = writer.Cache.geoCache; var maxDepth = writer.Cache.MaxDepth; var graph = writer.Graph; var elmStack = new Stack <Tuple <Element, XYZ> >(); elmStack.Push(new Tuple <Element, XYZ>(elm, null)); var previouseConnections = new HashSet <Connector>(); while (elmStack.Count >= 1) { var currentElementAndpt = elmStack.Pop(); var currentElement = currentElementAndpt.Item1; var orgPoint = currentElementAndpt.Item2; scannedElements.Add(currentElement.Id.IntegerValue); ConnectorManager currentCm = Utils.MEPUtils.GetConnectionManager(currentElement); if (currentCm == null) { continue; } var connStack = new Stack <Connector>(); foreach (var conn in currentCm.Connectors.OfType <Connector>().Where(cn => cn.ConnectorType == ConnectorType.End || cn.ConnectorType == ConnectorType.Curve))// && !previouseConnections.Any(pc => pc.IsConnectedTo(cn)))) { connStack.Push(conn); } XYZ nextOrigin = null; while (connStack.Count >= 1) { var currentConn = connStack.Pop(); Connector nextConnector = null; Element nextElement = null; nextOrigin = currentConn.Origin; MEPRevitEdge edge = null; var meps = GetSectionForConnctor(currentConn); var edgeType = MEPGraph.Model.MEPEdgeTypes.FLOWS_TO; switch (currentConn.Domain) { case Autodesk.Revit.DB.Domain.DomainCableTrayConduit: edgeType = MEPGraph.Model.MEPEdgeTypes.CABLETRAY_FLOW_TO; break; case Autodesk.Revit.DB.Domain.DomainElectrical: edgeType = MEPGraph.Model.MEPEdgeTypes.ELECTRICAL_FLOW_TO; break; case Autodesk.Revit.DB.Domain.DomainPiping: edgeType = MEPGraph.Model.MEPEdgeTypes.HYDRONIC_FLOW_TO; break; case Autodesk.Revit.DB.Domain.DomainHvac: edgeType = MEPGraph.Model.MEPEdgeTypes.AIR_FLOW_TO; break; case Autodesk.Revit.DB.Domain.DomainUndefined: edgeType = MEPGraph.Model.MEPEdgeTypes.FLOWS_TO; break; } if (currentConn.IsConnected) { //var ci = currentConn.GetMEPConnectorInfo(); //connection search nextConnector = currentConn.AllRefs.OfType <Connector>().Where(cn => (cn.ConnectorType == ConnectorType.End || cn.ConnectorType == ConnectorType.Curve) && cn.IsConnectedTo(currentConn) && cn.Owner.Id.IntegerValue != currentElement.Id.IntegerValue).FirstOrDefault(); if (nextConnector != null) { previouseConnections.Add(currentConn); edge = graph.AddConnection(currentConn, nextConnector, MEPPathConnectionType.Phyiscal, edgeType); nextElement = nextConnector.Owner; } } if (nextConnector == null) { //geometry search var connPoint = currentConn.Origin; var nearPoints = cpTree.GetNearby(connPoint, 1F); var nearPointsOrdered = nearPoints.Where(el => el.OriginatingElement.IntegerValue != currentElement.Id.IntegerValue).OrderBy(d => d.Point.DistanceTo(connPoint)).ToList(); var nearest = nearPointsOrdered.FirstOrDefault(); if (nearest != null) { var distance = nearest.Point.DistanceTo(connPoint); if (distance < 0.01F) { var nearElm = elm.Document.GetElement(nearest.OriginatingElement); if (nearElm != null) { var mepm = MEPUtils.GetConnectionManager(nearElm); #if REVIT2016 nextConnector = (mepm != null) ? mepm.Connectors.OfType <Connector>().Where(cn => (cn.ConnectorType == ConnectorType.End || cn.ConnectorType == ConnectorType.Curve) && cn.Id == nearest.ConnectorIndex).FirstOrDefault() : null; #else throw new Exception("Only supported in Revit 2016 onwards"); #endif if (nextConnector != null) { previouseConnections.Add(currentConn); edge = graph.AddConnection(currentConn, nextConnector, MEPPathConnectionType.Proximity, edgeType); nextElement = nextConnector.Owner; //nextOrigin = nextConnector.Origin; } } } } if (nextConnector == null && geoTree != null) { //todo: curve - point intersection check var colBox = new HLBoundingBoxXYZ(connPoint, new XYZ(0.1, 0.1, 0.1)); var nearbyCurves = geoTree.GetColliding(colBox); var nearbyCurve = nearbyCurves.OfType <CurveGeometrySegment>().Where(nb => nb.OriginatingElement.IntegerValue != currentConn.Owner.Id.IntegerValue).OrderBy(nd => nd.Geometry.Distance(connPoint)).FirstOrDefault(); if (nearbyCurve != null) { var distance = nearbyCurve.Geometry.Distance(connPoint); //check if connector is sitting near or with extent of the mepcurve var tolerence = 0.01; if (distance < tolerence || nearbyCurve.Radius > 0.001 && distance <= (nearbyCurve.Radius + tolerence) || nearbyCurve.Width > 0.001 && distance <= (nearbyCurve.Width + tolerence) || nearbyCurve.Height > 0.001 && distance <= (nearbyCurve.Height + tolerence)) { //how to add this connection to the graph with no next connector?? var orgElme = elm.Document.GetElement(nearbyCurve.OriginatingElement); edge = graph.AddConnection(currentConn, connPoint, orgElme, MEPPathConnectionType.ProximityNoConnector, edgeType); nextElement = orgElme; //nextOrigin = connPoint; } } } } if (edge != null && edge.Length < 0.1) { if (meps != null) { edge.Length = meps.TotalCurveLength; edge.SetWeight("Roughness", meps.Roughness); edge.SetWeight("ReynoldsNumber", meps.ReynoldsNumber); edge.SetWeight("Velocity", meps.Velocity); edge.SetWeight("VelocityPressure", meps.VelocityPressure); edge.SetWeight("Flow", meps.Flow); } else if (orgPoint != null && nextOrigin != null) { var length = Math.Abs(orgPoint.DistanceTo(nextOrigin)); //very crude way to get length, should really be using MEPSection if (length >= 0.1) { edge.Length = length; } } } ScanSpacesFromElement(currentConn, graph, scannedElements, nextElement == null); var currentDepth = elmStack.Count; if (nextElement != null && (maxDepth == -1 || currentDepth < maxDepth) && !scannedElements.Contains(nextElement.Id.IntegerValue))// && !graph.ContainsElement(nextElement)) { elmStack.Push(new Tuple <Element, XYZ>(nextElement, nextOrigin)); //ScanFromElement(elm, graph, cpTree, maxDepth, currentDepth++); } } //check curve for nearby unconnected points if (cpTree != null && currentElement is MEPCurve) { //point - curve intersection check var curveElm = currentElement as MEPCurve; var locCurve = curveElm.Location as LocationCurve; var curve = locCurve.Curve; var nearbyCurve = new CurveGeometrySegment(curve, currentElement); var P1 = curve.GetEndPoint(0); var P2 = curve.GetEndPoint(1); var curveAsLine = curve is Line ? curve as Line : Line.CreateBound(P1, P2); var nearbyCurves = cpTree.GetNearby(curveAsLine, (float)nearbyCurve.MaxDimension); foreach (var cb in nearbyCurves) { if (cb.OriginatingElement.IntegerValue == currentElement.Id.IntegerValue) { continue; } if (P1.DistanceTo(cb.Point) < 0.01) { continue; } if (P2.DistanceTo(cb.Point) < 0.01) { continue; } var tolerence = 0.01; var cnpoint = curveAsLine.Project(cb.Point); var distance = cnpoint.Distance; //distance check is a crude way to check this element is sitting in the duct/pipe, could be nearby but intended to be connected. if (distance < (nearbyCurve.MaxDimension / 2) + tolerence) { var orgElme = elm.Document.GetElement(cb.OriginatingElement); if (orgElme == null) { continue; } var cmgr = MEPUtils.GetConnectionManager(orgElme); if (cmgr == null) { continue; } #if REVIT2016 var nc = cmgr.Connectors.OfType <Connector>().FirstOrDefault(cn => cn.Id == cb.ConnectorIndex); #else Connector nc = null; #endif if (nc == null || nc.IsConnected) { continue; } //further check that direction of point intersects curve var prjInX = nc.CoordinateSystem.OfPoint(new XYZ(0, 0, 5)); var prjInnX = nc.CoordinateSystem.OfPoint(new XYZ(0, 0, -5)); var prjLine = Line.CreateBound(prjInnX, prjInX); var ix = curveAsLine.Intersect(prjLine); if (ix != SetComparisonResult.Overlap) { continue; } var edge = graph.AddConnection(nc, cnpoint.XYZPoint, currentElement, MEPPathConnectionType.ProximityNoConnector, MEPGraph.Model.MEPEdgeTypes.FLOWS_TO); if (orgPoint != null) { edge.Length = Math.Abs(orgPoint.DistanceTo(cnpoint.XYZPoint)); } if (!scannedElements.Contains(orgElme.Id.IntegerValue) && !elmStack.Any(el => el.Item1 == orgElme))// && !graph.ContainsElement(nextElement)) { elmStack.Push(new Tuple <Element, XYZ>(orgElme, cnpoint.XYZPoint)); } } } } ScanSpacesFromElement(currentElement, graph, scannedElements); } }
/// <summary> /// Checks if outerBounds encapsulates the given point. /// </summary> /// <param name="outerBounds">Outer bounds.</param> /// <param name="point">Point.</param> /// <returns>True if innerBounds is fully encapsulated by outerBounds.</returns> static bool Encapsulates(HLBoundingBoxXYZ outerBounds, XYZ point) { return(outerBounds.Contains(point)); }
public void ParseFrom(Element elm, MEPRevitGraphWriter writer) { var space = elm as Autodesk.Revit.DB.Mechanical.Space; if (space == null) { return; } if (space.Volume < 0.5) { return; } var scannedElements = writer.Cache.ParsedElements; var cpTree = writer.Cache.connectorsCache; var geoTree = writer.Cache.geoCache; var maxDepth = writer.Cache.MaxDepth; var graph = writer.Graph; var lvl = space.Level; if (lvl != null) { graph.AddConnection(elm, lvl, MEPPathConnectionType.Proximity, MEPEdgeTypes.IS_ON); } //get elements in the space //get areas with edges and add to walls var sbopt = new SpatialElementBoundaryOptions(); sbopt.SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Finish; sbopt.StoreFreeBoundaryFaces = true; //var geoOpt = new Options(); //var spgeo = space.get_Geometry(geoOpt); //get nearby spaces by extending bb of space //foreach each face of space geometry //split into uv gripd //construct line from each point //get intersect with faces on nearby faces //if it hits increment area count for direction var doc = space.Document; SpatialElementGeometryCalculator sg = new SpatialElementGeometryCalculator(doc, sbopt); var spgets = sg.CalculateSpatialElementGeometry(space); var spgeo = spgets.GetGeometry(); //var spbb = spgeo.GetBoundingBox(); var spbb = elm.get_BoundingBox(null); //var spbbhl = new HLBoundingBoxXYZ(spbb); //spbbhl.Size = spbbhl.Size + new XYZ(2, 2, 4); //get faces var uvdensity = 0.75; var maxDistance = 4D; //var nearbyFaces = nearbySpaces.OfType<SolidGeometrySegment>().Where(spc => spc.OriginatingElement != space.Id).SelectMany(sp => sp.Geometry.Faces.OfType<Face>()); var rayIncidents = new HashSet <FaceIntersectRay>(); //get all the faces in the geometry var spfaces = spgeo.Faces.OfType <Face>().ToList(); foreach (var gface in spfaces) { //extract the faces which bound with other elements (Walls, floors, ceilings, windows etc) var sfaceInfos = spgets.GetBoundaryFaceInfo(gface); foreach (var sfaceInfo in sfaceInfos) { //get the geo face and element of this bounding face var sface = sfaceInfo.GetSubface(); var elmId = sfaceInfo.SpatialBoundaryElement; var lelm = GetElementFromLinkedElement(elmId, doc); var docIdent = string.Empty; //if (lelm == null) continue; //ignore this face if it doesn't resolve to a valid element //find the bounding uv box so we can work out a grid of points var fbb = sface.GetBoundingBox(); var uExt = uvdensity; // (fbb.Max.U - fbb.Min.U) / uvdensity; var vExt = uvdensity; // (fbb.Max.V - fbb.Min.V) / uvdensity; var u = fbb.Min.U; var v = fbb.Min.V; //var sb = new GeoLib.C2DPolygon(); //construct grid for ray tracing Stack <UV> gridPoints = new Stack <UV>(); while (u <= fbb.Max.U) { v = fbb.Min.V; while (v <= fbb.Max.V) { var uvp = new UV(u, v); v += uvdensity; if (!sface.IsInside(uvp)) { continue; //only include points that are actually on this face } gridPoints.Push(uvp); } u += uvdensity; } var nerbyCheckCats = new int[] { }; IList <ElementId> hostElms = new List <ElementId>(); if (lelm != null && !(lelm is HostObject)) { var n = lelm; } if (lelm != null) { docIdent = DocUtils.GetDocumentIdent(lelm.Document); //get cutting elemtns if it's a wall so we can find door and windows if (lelm is HostObject) { var whost = lelm as HostObject; hostElms = whost.FindInserts(true, true, true, true); //build oct tree of hostelems so we can quickly ray trace them later foreach (var hostElm in hostElms) { //ignoring any link and transform for now var ehl = whost.Document.GetElement(hostElm); writer.Cache.geoCacheWriter.AddElement(ehl, true); } } //we need the nearby check to find the cut out elements nerbyCheckCats = new int[] { (int)BuiltInCategory.OST_Doors, (int)BuiltInCategory.OST_Windows }; } else { nerbyCheckCats = new int[] { (int)BuiltInCategory.OST_Doors, (int)BuiltInCategory.OST_Windows, (int)BuiltInCategory.OST_Floors, (int)BuiltInCategory.OST_Walls, (int)BuiltInCategory.OST_Roofs }; //we need the nearby check to find the bounding element switch (sfaceInfo.SubfaceType) { case SubfaceType.Bottom: nerbyCheckCats = new int[] { (int)BuiltInCategory.OST_Floors }; break; case SubfaceType.Top: nerbyCheckCats = new int[] { (int)BuiltInCategory.OST_Roofs, (int)BuiltInCategory.OST_Floors, (int)BuiltInCategory.OST_Windows }; break; case SubfaceType.Side: nerbyCheckCats = new int[] { (int)BuiltInCategory.OST_Doors, (int)BuiltInCategory.OST_Windows, (int)BuiltInCategory.OST_Walls }; break; } } //option 1 - brute force ray trace //option 2 - construct 2d polygon from edges of each face, translate each face into the same plane, then boolean intersect, get area of each intersect //calc space boundaries at midpoint? Face optLastIntermeidateFace = null; SolidGeometrySegment optLastIntermeidateSegment = null; Face optLastHitFace = null; SolidGeometrySegment optLastHitSegment = null; var arWeight = sface.Area / gridPoints.Count; while (gridPoints.Count > 0) { var pt = gridPoints.Pop(); var rayIncident = new FaceIntersectRay(); var nv = sface.ComputeNormal(pt).Normalize(); //var mx = sface.ComputeSecondDerivatives(pt).MixedDerivative.Normalize(); rayIncident.SourceElement = space; rayIncident.SourceFace = sface; rayIncident.SourceUV = pt; rayIncident.RayVecotor = nv; rayIncident.IntermediatDocIdent = docIdent; rayIncident.IntermeidateElement = lelm != null ? lelm.Id : ElementId.InvalidElementId; rayIncident.AreaWeight = arWeight; rayIncident.SubFaceType = sfaceInfo.SubfaceType; rayIncidents.Add(rayIncident); var sp = sface.Evaluate(pt); rayIncident.SourceXYZ = sp; var ray = Line.CreateBound(sp, sp + nv * 4); var rayBB = new HLBoundingBoxXYZ(sp, (sp + nv * 4), true); //rayBB.Size = rayBB.Size + new XYZ(0.5, 0.5, 0.5); //Plane geoPlane = Plane.c(sp, sp + nv * 5, sp + nv * 5 + (mx * 0.2)); //SketchPlane skPlane = SketchPlane.Create(doc, geoPlane); //doc.Create.NewModelCurve(ray, skPlane); //check cache for hit on otherside, if there is one nearby on this face we can ignore it as we're not including both sides //var nearbyrayHits = writer.Cache.rayhitCache.GetNearby(ray, 0.4F); //var validSimilarHit = nearbyrayHits.FirstOrDefault(rh => rh.HittingSegment != null && rh.HittingSegment.OriginatingElement == space.Id && rh.IntermeidateElement == rayIncident.IntermeidateElement); //if (validSimilarHit != null && sface.IsInside(validSimilarHit.HittingUV)) //{ // rayIncident.Ignore = true; // log.Info("Got hit on other side, ignoring"); // continue; // } if (optLastIntermeidateFace != null) { IntersectionResultArray issRes = null; var issGeoHit = getIntersect(pt, optLastIntermeidateFace, sface, maxDistance, out issRes, out double distance, nv, doc); if (issGeoHit) { rayIncident.IntermediatDocIdent = optLastIntermeidateSegment.OriginatingDocIdent; rayIncident.IntermeidateElement = optLastIntermeidateSegment.OriginatingElement; } else { optLastIntermeidateFace = null; optLastIntermeidateSegment = null; } } GeometrySegment[] nearbyElements = null; if (optLastIntermeidateFace == null) { nearbyElements = geoTree.GetColliding(rayBB); var nearbyCutoutElements = nearbyElements.Where(iel => (hostElms.Count == 0 || hostElms.Contains(iel.OriginatingElement)) && nerbyCheckCats.Contains(iel.OriginatingElementCategory.IntegerValue)) .OfType <SolidGeometrySegment>(); IntersectionResultArray isRes = null; bool isGeoHit = false; foreach (var extSegment in nearbyCutoutElements) { foreach (var extFace in extSegment.Geometry.Faces.OfType <Face>()) { isGeoHit = getIntersect(pt, extFace, sface, maxDistance, out isRes, out double distance, nv, doc); if (isGeoHit) { rayIncident.IntermediatDocIdent = extSegment.OriginatingDocIdent; rayIncident.IntermeidateElement = extSegment.OriginatingElement; optLastIntermeidateFace = extFace; optLastIntermeidateSegment = extSegment; break; } } if (isGeoHit) { break; } } } if (optLastHitFace != null) { var isHit = getIntersect(pt, optLastHitFace, sface, maxDistance, out var isRe, out double distance, nv, doc); var isRes = isRe; //project point onto other face instead? var srcXYZ = sface.Evaluate(pt); var otXYZRes = optLastHitFace.Project(srcXYZ); if (isHit) { var itx = isRes.OfType <IntersectionResult>().FirstOrDefault(); rayIncident.HittingFace = optLastHitFace; rayIncident.HittingSegment = optLastHitSegment; rayIncident.HittingUV = itx.UVPoint; rayIncident.HittingXYZ = itx.XYZPoint; rayIncident.Distance = distance; nv = sface.ComputeNormal(pt).Normalize(); continue; //shortcut if we find a hit on the same face again } else { optLastHitFace = null; optLastHitSegment = null; } } if (nearbyElements == null) { nearbyElements = geoTree.GetColliding(rayBB, (ob) => { return(ob.OriginatingElementCategory.IntegerValue == (int)BuiltInCategory.OST_MEPSpaces); }); } //BoundingBoxIntersectsFilter bif = new BoundingBoxIntersectsFilter(new Outline(sp - new XYZ(0.1, 0.1, 0.1), (sp + nv * 2) + new XYZ(0.1, 0.1, 0.1))); //var sfv = new FilteredElementCollector(space.Document); //var sepl = sfv.WherePasses(bif).ToElements(); var nearbySpaces = nearbyElements.Where(ne => ne.OriginatingElementCategory.IntegerValue == (int)BuiltInCategory.OST_MEPSpaces).OfType <SolidGeometrySegment>().Distinct().ToList(); //find the extents of this face which face faces on other nearby faces (whaaat?) //check each face of each nearby space for intersect with ray foreach (var nearSpace in nearbySpaces) { var isHit = false; foreach (var otFace in nearSpace.Geometry.Faces.OfType <Face>()) { isHit = getIntersect(pt, otFace, sface, maxDistance, out var isRe, out double distance, nv, doc); var isRes = isRe; //project point onto other face instead? var srcXYZ = sface.Evaluate(pt); var otXYZRes = otFace.Project(srcXYZ); if (isHit) { if (nearSpace.OriginatingElement.IntegerValue != elm.Id.IntegerValue) { var itx = isRes.OfType <IntersectionResult>().FirstOrDefault(); rayIncident.HittingFace = otFace; rayIncident.HittingSegment = nearSpace; rayIncident.HittingUV = itx.UVPoint; rayIncident.HittingXYZ = itx.XYZPoint; rayIncident.Distance = distance; nv = sface.ComputeNormal(pt).Normalize(); //optimization: check this face again first for the next ray check, since it's likely to be another hit optLastHitFace = otFace; optLastHitSegment = nearSpace; rayIncident.Ignore = false; } else { if (distance < 0.1) { isHit = false; //looks like we hit our own face, ouch! } rayIncident.Ignore = true; } break; } } if (isHit) { break; } } } } /* * space * face * intermediate element (Wall/window/door) * face (space) * face * * * add connection * this space -> section -> other space * wall ------------^ */ } //var ec = new elmComparer(); var srcNode = graph.AddElement(space); double minIncluedArea = 4; VectorBucketiser vbw = new VectorBucketiser(8, 5); var includeRays = rayIncidents.Where(r => !r.Ignore); var outsideNode = graph.Nodes.FirstOrDefault(n => n.Name == "Outside"); var groundNode = graph.Nodes.FirstOrDefault(n => n.Name == "Ground"); //group by the intermediate element (wall/floor/etc) foreach (var docGroup in includeRays.GroupBy(ri => ri.IntermediatDocIdent))//, ec)) { var sdoc = DocUtils.GetDocument(docGroup.Key, elm.Document.Application); foreach (var intermediateElemGroup in docGroup.GroupBy(ri => ri.IntermeidateElement.IntegerValue))//, ec)) { var selmid = new ElementId(intermediateElemGroup.Key); Element selm = sdoc != null?sdoc.GetElement(selmid) : null; //group similar vectors into buckets foreach (var rayVectorBuckets in intermediateElemGroup.GroupBy(ri => vbw.GetBucket(ri.RayVecotor))) { var rg = rayVectorBuckets.ToList(); var gs = rg.GroupBy(vr => vr.HittingSegment == null ? null : vr.HittingSegment.OriginatingElement).ToList(); //group each vector and intermediate element by the element it hits foreach (var orgElmGroup in gs) { //find a section already matching this section //actually easier to treat each path as separate sections //var edNodes = graph.GetEdges(intermediateElemGroup.Key).Where(ed => ed.NextNode.AsAbstractNode.Name == "Surface" //&& ed.NextNode.Connections.Any(cn => cn.NextNode.OriginId == spNode.OriginId)).Where(ed => ed.; var apporxIntersect = orgElmGroup.Sum(et => et.AreaWeight); var vector = orgElmGroup.First().RayVecotor; if (apporxIntersect < minIncluedArea) { continue; } var direction = VectorBucketiser.GetZeroClamppedPoint(orgElmGroup.First().RayVecotor); //should be rayVectorGroup.Key.AverageVector, but not yet implemented; MEPRevitNode spNode = null; if (orgElmGroup.Key != null) { var otherSpace = doc.GetElement(orgElmGroup.Key); spNode = graph.AddElement(otherSpace); } else { if (selm != null && (selm.Name.ToLower().Contains("exterior") || selm is Autodesk.Revit.DB.Opening || selm.Name.ToLower().Contains("window") || selm is Autodesk.Revit.DB.RoofBase)) { if (outsideNode == null) { outsideNode = new MEPRevitNode("Outside", "Boundary", "OutsideBoundary", new MEPGraph.Model.Environment()); } spNode = outsideNode; } else if (selm != null && (selm.Name.ToLower().Contains("floor") || selm is Autodesk.Revit.DB.Floor)) { if (groundNode == null) { groundNode = new MEPRevitNode("Ground", "Boundary", "GroundBoundary", new MEPGraph.Model.Environment()); } spNode = groundNode; } else { spNode = new MEPRevitNode("Void", "Boundary", "OtherBoundary", new MEPGraph.Model.VoidVolume()); continue; //ignore void boundaries for now } } var sectionN = graph.NewSection(selm, MEPGraph.Model.MEPEdgeTypes.IS_ON); if (selm == null) { var emptyBondary = new MEPRevitNode(); emptyBondary.AsAbstractNode.Name = "OpenBoundary"; var cl = graph.AddConnection(emptyBondary, sectionN, MEPPathConnectionType.SectionOf, MEPGraph.Model.MEPEdgeTypes.IS_ON); cl.AsNodeEdge.ExtendedProperties.Add("rvid", intermediateElemGroup.Key); } sectionN.AsAbstractNode.Name = "Surface"; var edgesf = graph.AddConnection(srcNode, sectionN, MEPPathConnectionType.Analytical, MEPGraph.Model.MEPEdgeTypes.BOUNDED_BY); //total up intersecting area var sampleIntersect = orgElmGroup.First(); edgesf.SetWeight("Area", apporxIntersect); //edgesf.SetWeight("Direction", new HoareLea.MEPGraph.Model.MEPPoint(direction.X, direction.Y, direction.Z)); edgesf.SetWeight("DirectionX", direction.X); edgesf.SetWeight("DirectionY", direction.Y); edgesf.SetWeight("DirectionZ", direction.Z); edgesf.SetWeight("SubFaceType", (int)sampleIntersect.SubFaceType); /* * HLBoundingBoxXYZ bb = new HLBoundingBoxXYZ(); * foreach (var et in orgElmGroup) * { * if (et.HittingXYZ != null) * { * bb.ExpandToContain(et.HittingXYZ); * } * } * if (!bb.IsInvalid) * { * sectionN.BoundingBox = bb; * var avgCenterPoint = bb.MidPoint; * var size = bb.Size; * sectionN.SetProperty("OriginX", avgCenterPoint.X); * sectionN.SetProperty("OriginY", avgCenterPoint.Y); * sectionN.SetProperty("OriginZ", avgCenterPoint.Z); * sectionN.SetProperty("SizeX", size.X); * sectionN.SetProperty("SizeY", size.Y); * sectionN.SetProperty("SizeZ", size.Z); * }*/ var edgest = graph.AddConnection(sectionN, spNode, MEPPathConnectionType.Analytical, MEPGraph.Model.MEPEdgeTypes.BOUNDED_BY); var directionn = direction;//.Negate(); edgest.SetWeight("Area", apporxIntersect); edgest.SetWeight("DirectionX", directionn.X); edgest.SetWeight("DirectionY", directionn.Y); edgest.SetWeight("DirectionZ", directionn.Z); edgest.SetWeight("SubFaceType", (int)sampleIntersect.SubFaceType); } } } } }
/// <summary> /// Find which child node this object would be most likely to fit in. /// </summary> /// <param name="objBounds">The object's bounds.</param> /// <returns>One of the eight child octants.</returns> int BestFitChild(HLBoundingBoxXYZ objBounds) { return((objBounds.MidPoint.X <= Center.X ? 0 : 1) + (objBounds.MidPoint.Y >= Center.Y ? 0 : 4) + (objBounds.MidPoint.Z <= Center.Z ? 0 : 2)); }
/// <summary> /// Checks if outerBounds encapsulates innerBounds. /// </summary> /// <param name="outerBounds">Outer bounds.</param> /// <param name="innerBounds">Inner bounds.</param> /// <returns>True if innerBounds is fully encapsulated by outerBounds.</returns> static bool Encapsulates(HLBoundingBoxXYZ outerBounds, HLBoundingBoxXYZ innerBounds) { return(outerBounds.Contains(innerBounds.Min) && outerBounds.Contains(innerBounds.Max)); }
public static bool DoBoxesIntersect(HLBoundingBoxXYZ r1, HLBoundingBoxXYZ r2, double tolerance) { return(r1.Min.X <= r2.Max.X && r1.Max.X >= r2.Min.X && r1.Min.Y <= r2.Max.Y && r1.Max.Y >= r2.Min.Y && r1.Min.Z <= r2.Max.Z && r1.Max.Z >= r2.Min.Z); }