private IEnumerable <IfcProduct> GetCandidatesFromNode(IfcProduct original, XbimOctree <IfcProduct> node) { if (original != null && node != null) { //content which if from other models var nodeContent = node.Content().Where(nc => (nc.ModelOf != original.ModelOf) && (nc.GetType() == original.GetType())); var prodBBox = XbimRect3D.Empty; foreach (var candidate in nodeContent) { //check BBoxes for equality var contBBox = _prodBBsB[candidate]; prodBBox = _prodBBsA[original]; if (XbimAABBoxAnalyser.AlmostEqual(contBBox, prodBBox, _precision)) { if (!_processedFromB.Contains(candidate)) { _processedFromB.Add(candidate); } yield return(candidate); } //cope with the situation when product's bbox is on the border and //it's equivalent from the second model might have end up on the higher level var borderDirections = BBoxBorderDirections(node.Bounds, prodBBox); var parents = GetParentsInDirections(borderDirections, node); foreach (var parent in parents) { foreach (var item in GetCandidatesFromNode(original, parent))//recursion { if (!_processedFromB.Contains(candidate)) { _processedFromB.Add(candidate); } yield return(candidate); } } } } }
/// <summary> /// Octree is created in the constructor. This method of spatial /// indexing should speed up spatial queries by doing simple /// computations on indexed geometries. /// </summary> /// <param name="model">Building model</param> public XbimSpatialAnalyser(XbimModel model) { _model = model; _bboxAnalyser = new XbimAABBoxAnalyser(model, _prodBBs); _semanticAnalyser = new XbimSemanticAnalyser(model); //generate geometry if there is no in the model if (model.GeometriesCount == 0) { //create geometry var m3D = new Xbim3DModelContext(model); m3D.CreateContext(true); } //initialize octree with all the objects var prods = model.Instances.OfType <IfcProduct>(); //Stopwatch sw = new Stopwatch(); //var report = new StringWriter(); //report.WriteLine("{0,-15}, {1,-40}, {2,5}, {3,5}", "Type", "Product name", "Geometry", "BBox"); //we need to preprocess all the products first to get world size. Will keep results to avoid repetition. XbimRect3D worldBB = XbimRect3D.Empty; foreach (var prod in prods) { //bounding boxes are lightweight and are produced when geometry is created at first place //sw.Start(); var geom = prod.Geometry3D(); var trans = prod.ObjectPlacement.ToMatrix3D(); //sw.Stop(); //var geomGeneration = sw.ElapsedMilliseconds; if (geom != null && geom.FirstOrDefault() != null) { //get or cast to BBox //sw.Restart(); var bb = geom.GetAxisAlignedBoundingBox(); bb = new XbimRect3D(trans.Transform(bb.Min), trans.Transform(bb.Max)); //sw.Stop(); //var gettingBbox = sw.ElapsedMilliseconds; //report.WriteLine("{0,-15}, {1,-40}, {2,5}, {3,5}", prod.GetType().Name, prod.Name, geomGeneration, gettingBbox); //add every BBox to the world to get the size and position of the world _prodBBs.Add(prod, bb); if (!double.IsNaN(bb.SizeX)) { worldBB.Union(bb); } //Debug.WriteLine("{0,-45} {1,10:F5} {2,10:F5} {3,10:F5} {4,10:F5} {5,10:F5} {6,10:F5}", // prod.Name, bb.X, bb.Y, bb.Z, bb.SizeX, bb.SizeY, bb.SizeZ); } } //Debug.WriteLine("{0,-45} {1,10:F5} {2,10:F5} {3,10:F5} {4,10:F5} {5,10:F5} {6,10:F5}", // "World", worldBB.X, worldBB.Y, worldBB.Z, worldBB.SizeX, worldBB.SizeY, worldBB.SizeZ); //create octree //target size must depend on the units of the model var meter = (float)model.ModelFactors.OneMetre; var size = Math.Max(Math.Max(worldBB.SizeX, worldBB.SizeY), worldBB.SizeZ) + meter / 2f; var shift = meter / 4f; var position = worldBB.Location + new XbimVector3D() { X = size / 2f - shift, Y = size / 2f - shift, Z = size / 2f - shift }; _tree = new XbimOctree <IfcProduct>(size, meter, 1f, position); //sw.Restart(); //add every product to the world foreach (var item in _prodBBs) { _tree.Add(item.Key, item.Value); } //sw.Stop(); //report.WriteLine("Generation of octree containing {0} products {1}", prods.Count(), sw.ElapsedMilliseconds); }