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> /// Creates the element in the model /// </summary> /// <param name="doc"></param> public void CreateLocal(StreamMessage message) { //insert at position? //find space it's in? //find family symbol/type? _dispatcher.QueueAction((uiapp) => { var doc = uiapp.ActiveUIDocument.Document; var node = message.After.AsNode(); var nodeElement = doc.GetElement(node.Id); using (var updatetx = new Transaction(doc, "Update from stream")) { updatetx.Start("Update from stream"); if (node.Labels.Contains("FanCoilUnit")) { if (nodeElement == null) { //create node // //get the space it's in var query = @" query($fcuid:ID!){ FanCoilUnit (Id:$fcuid){ Id ModelElements{ Id UniqueId } Space{ Id } ConnectedTo{ Id Apparent_Load ModelElements{ Id UniqueId } } } }"; var vars = new Dictionary <string, object>(); vars.Add("fcuid", node.Id); var res = _client.ExecuteQuery(query, vars); if (res.FanCoilUnit != null) { var fcu = res.FanCoilUnit[0]; Element modelElm = null; foreach (var modelelemet in fcu.ModelElements) { var id = modelelemet.UniqueId.Value; modelElm = doc.GetElement((string)id); if (modelElm != null) { break; } } if (fcu.Space != null) { var spaceId = fcu.Space.Id.Value; //find space var spaceElm = doc.GetElement((string)spaceId) as SpatialElement; if (spaceElm != null) { //find center MEPRevitNode rv = new MEPRevitNode(spaceElm); var sbopt = new SpatialElementBoundaryOptions(); sbopt.SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Finish; sbopt.StoreFreeBoundaryFaces = true; SpatialElementGeometryCalculator sg = new SpatialElementGeometryCalculator(doc, sbopt); var spgets = sg.CalculateSpatialElementGeometry(spaceElm); var spgeo = spgets.GetGeometry(); var center = spgeo.ComputeCentroid(); var bbox = spgeo.GetBoundingBox(); var heightOfUnit = 1; var randomOffset = (bbox.Max.X - bbox.Min.X) / 2 * (new Random().NextDouble() - 0.5); var inspoint = new XYZ(center.X + randomOffset, center.Y, bbox.Max.Z - heightOfUnit); FilteredElementCollector famFilter = new FilteredElementCollector(doc); //this is static for this example. We will need to discover the type and pull the family //from a family library service. string familyName = "HL_FanCoilUnit_HotandChilledWaterSoffit3HorizontalRoundConnections"; Family foundFam = famFilter.OfClass(typeof(Family)).ToElements().OfType <Family>().FirstOrDefault(fm => fm.Name == familyName); var fcuSymbol = doc.GetElement(foundFam.GetFamilySymbolIds().FirstOrDefault()) as FamilySymbol; var newElm = doc.Create.NewFamilyInstance(inspoint, fcuSymbol, Autodesk.Revit.DB.Structure.StructuralType.NonStructural); //var props = MEPGraphUtils.GetNodePropsWithElementProps(new Client.Model.ModelElement(), newElm); //var newnode = _client.Push("ModelElement", props); //_client.Relate(new PendingNode(node), newnode, Client.Model.MEPEdgeTypes.REALIZED_BY, null); var nodeparamers = node.ExtendedProperties; foreach (var param in newElm.Parameters.OfType <Parameter>()) { if (param.IsReadOnly) { continue; } var hp = new HLRevitParameter(param); var paramName = Utils.GetGraphQLCompatibleFieldName(param.Definition.Name); if (nodeparamers.ContainsKey(paramName)) { var incommingValue = nodeparamers[paramName]; var currentValue = MEPGraphUtils.RevitToGraphValue(hp); if (incommingValue != currentValue) { hp.Value = incommingValue; } } } if (fcu.ConnectedTo != null) { string fcuName = "FCU"; Family foundfcuFam = famFilter.OfClass(typeof(Family)).ToElements().OfType <Family>().FirstOrDefault(fm => fm.Name == fcuName); var fcueSymbol = doc.GetElement(foundFam.GetFamilySymbolIds().FirstOrDefault()) as FamilySymbol; var fcuEinspt = inspoint + new XYZ(1, 0, 0); var newFuceElm = doc.Create.NewFamilyInstance(fcuEinspt, fcuSymbol, Autodesk.Revit.DB.Structure.StructuralType.NonStructural); foreach (var param in newFuceElm.Parameters.OfType <Parameter>()) { if (param.IsReadOnly) { continue; } var hp = new HLRevitParameter(param); var paramName = Utils.GetGraphQLCompatibleFieldName(param.Definition.Name); if (fcu.ConnectedTo[paramName] != null) { var incommingValue = fcu.ConnectedTo[paramName].Value; var currentValue = MEPGraphUtils.RevitToGraphValue(hp); if (incommingValue != currentValue) { hp.Value = incommingValue; } } } } } } } } } updatetx.Commit(); } }); }