Exemple #1
0
 public virtual void ParseFrom(ICollection <Element> elms, MEPRevitGraphWriter writer)
 {
     foreach (var elm in elms)
     {
         ParseFrom(elm, writer);
     }
 }
        public override void ParseFrom(Element elm, MEPRevitGraphWriter writer)
        {
            var fi = elm as FamilyInstance;

            if (fi == null)
            {
                return;
            }

            var elmHost = fi.Host;

            Transform tsx = Transform.Identity;

            if (elmHost is RevitLinkInstance)
            {
                RevitLinkInstance rl = elmHost as RevitLinkInstance;
                var ldoc             = rl.GetLinkDocument();
                var sRef             = fi.HostFace.ConvertToStableRepresentation(elm.Document);
                //dcdb8503-82b6-4a11-a408-754c3e8e7289-0026baca:0:RVTLINK:3791865:0:INSTANCE:3769400:94:SURFACE
                string[] refTokens = sRef.Split(':');
                var      extIdst   = refTokens[3]; //6ths index contains the elementID in host document!
                var      extId     = new ElementId(int.Parse(extIdst));

                var lelmHost = ldoc.GetElement(extId);
                if (lelmHost != null)
                {
                    writer.Graph.AddConnection(elm, lelmHost, MEPPathConnectionType.Analytical, Model.MEPEdgeTypes.SENSING);
                }

                tsx     = rl.GetTotalTransform().Inverse;
                elmHost = lelmHost;
            }
            else if (elmHost != null && !(elmHost is ReferencePlane))
            {
                writer.Graph.AddConnection(elm, elmHost, MEPPathConnectionType.Analytical, Model.MEPEdgeTypes.SENSING);
            }
            else if (fi.Space != null)
            {
                writer.Graph.AddConnection(elm, fi.Space, MEPPathConnectionType.Analytical, Model.MEPEdgeTypes.SENSING);
            }

            //find space, level, etc of host element.
            if (elmHost != null && !(elmHost is ReferencePlane))
            {
                base.ScanSpacesFromElement(elmHost, writer.Graph, writer.Cache.ParsedElements, tsx, elm.Document);
            }
        }
        public override void ParseFrom(Element elm, MEPRevitGraphWriter writer)
        {
            //connect db panels to their circuits
            if (elm is FamilyInstance)
            {
                var fi     = elm as FamilyInstance;
                var sysElm = fi.MEPModel;
                if (sysElm != null && sysElm.ElectricalSystems != null)
                {
                    var panleSystems = sysElm.ElectricalSystems.OfType <Autodesk.Revit.DB.Electrical.ElectricalSystem>().Where(sy => sy.BaseEquipment != null && sy.BaseEquipment.Id == elm.Id);

                    foreach (var sys in panleSystems)
                    {
                        writer.Graph.AddConnection(elm, sys, MEPPathConnectionType.Analytical, MEPGraph.Model.MEPEdgeTypes.ELECTRICAL_FLOW_TO);

                        try
                        {
                            var elmsINSys = sys.Elements.OfType <FamilyInstance>();

                            foreach (var emms in elmsINSys)
                            {
                                writer.Graph.AddConnection(sys, emms, MEPPathConnectionType.Analytical, MEPGraph.Model.MEPEdgeTypes.ELECTRICAL_FLOW_TO);

                                ParseFrom(emms, writer);
                            }
                        }
                        catch
                        {
                            //log exception
                        }
                    }
                }
            }

            base.ParseFrom(elm, writer);
        }
Exemple #4
0
 public void FinalizeGraph(MEPRevitGraphWriter writer)
 {
 }
Exemple #5
0
 public void InitializeGraph(MEPRevitGraphWriter writer)
 {
 }
Exemple #6
0
        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);
            }
        }
Exemple #7
0
 public void FinalizeGraph(MEPRevitGraphWriter writer)
 {
     //remove duplicate paths
 }
Exemple #8
0
        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);
                        }
                    }
                }
            }
        }