/// <summary> /// Allocate each geometry into appropriate tile /// </summary> /// <param name="geometries">Dictionary that contains geometries</param> /// <param name="tiles">Dictionary that contains tiles</param> /// <param name="maxElementPerTile">Maximum number of elements in each tile</param> public static void AllocateGeometryToTile(ref Dictionary <string, GeometryStore> geometries, ref Dictionary <XbimRect3D, List <GeometryStore> > tiles, int maxElementPerTile) { // allocate element to appropriate tile according to intersect volume foreach (GeometryStore gs in geometries.Values) { XbimRect3D candicateTile = XbimRect3D.Empty; double intersectVolume = double.MinValue; foreach (XbimRect3D tile in tiles.Keys) { double volume = GeometryStore.Intersect(gs.BoundBox, tile); if (volume > intersectVolume) { candicateTile = tile; intersectVolume = volume; } } if (!candicateTile.Equals(XbimRect3D.Empty)) { tiles[candicateTile].Add(gs); } } foreach (XbimRect3D bbox in tiles.Keys.ToList()) { // remove empty tile if (tiles[bbox].Count == 0) { tiles.Remove(bbox); } else if (tiles[bbox].Count > maxElementPerTile && bbox.SizeX > 1 && bbox.SizeY > 1 && bbox.SizeZ > 1) // retile a tile(tile size should be large than 1) { Dictionary <XbimRect3D, List <GeometryStore> > subTiles = new Dictionary <XbimRect3D, List <GeometryStore> >(); SplitBoundingBox(bbox, ref subTiles, bbox.SizeX / 2.0, bbox.SizeY / 2.0, bbox.SizeZ / 2.0); Dictionary <string, GeometryStore> elements = new Dictionary <string, GeometryStore>(); foreach (GeometryStore gs in tiles[bbox]) { elements.Add(gs.GlobalId, gs); } AllocateGeometryToTile(ref elements, ref subTiles, maxElementPerTile); tiles.Remove(bbox); foreach (XbimRect3D subBbox in subTiles.Keys) { tiles.Add(subBbox, subTiles[subBbox]); } } } }
/// <summary> /// Extract Geometries from IFC file. /// </summary> /// <param name="model">IfcStore model.</param> /// <param name="geometries">Geometries data.</param> /// <param name="modelBoundingBox">Bounding box of the ifc model.</param> public static void ExtractGeometries(IfcStore model, ref Dictionary <string, GeometryStore> geometries, ref XbimRect3D modelBoundingBox) { // context is used to extract geometry data var context = new Xbim3DModelContext(model); var meter = context.Model.ModelFactors.OneMeter; context.CreateContext(); // start to extract geometry data var instances = context.ShapeInstances(); XbimColourMap colorMap = new XbimColourMap(); foreach (var instance in instances) // each instance is a mesh { MeshStore meshStore = new MeshStore(); // get the color of this mesh var ss = model.Instances[instance.StyleLabel] as IIfcSurfaceStyle; XbimColour color = XbimColour.DefaultColour; if (ss != null) { var texture = XbimTexture.Create(ss); color = texture.ColourMap.FirstOrDefault(); } else { var styleId = instance.StyleLabel > 0 ? instance.StyleLabel : instance.IfcTypeId; var theType = model.Metadata.GetType((short)styleId); color = colorMap[theType.Name]; } meshStore.Color = string.Format("{0},{1},{2},{3}", (int)(color.Red * 255), (int)(color.Green * 255), (int)(color.Blue * 255), color.Alpha * 255); var geometry = context.ShapeGeometry(instance); var data = (geometry as IXbimShapeGeometryData).ShapeData; // multiple geometry may belong to one product, like frame and glass belong to a ifcwindow var product = model.Instances[instance.IfcProductLabel] as IIfcProduct; if (!geometries.ContainsKey(product.GlobalId)) { geometries.Add(product.GlobalId, new GeometryStore(product.GlobalId, product.Name)); } var geometryStore = geometries[product.GlobalId]; // reading the real geometry data using (var stream = new MemoryStream(data)) { using (var reader = new BinaryReader(stream)) { // triangle the instance and transform it to the correct position var mesh = reader.ReadShapeTriangulation(); mesh = mesh.Transform(instance.Transformation); // geometry data var verticesData = new List <double>(); var normalsData = new List <double>(); var indexesData = new List <int>(); // bouding box for instance. double minX = double.MaxValue, minY = double.MaxValue, minZ = double.MaxValue, maxX = double.MinValue, maxY = double.MinValue, maxZ = double.MinValue; var faces = mesh.Faces as List <XbimFaceTriangulation>; foreach (var face in faces) { foreach (var indice in face.Indices) { indexesData.Add(indice); } foreach (var normal in face.Normals) { normalsData.Add(normal.Normal.X); normalsData.Add(normal.Normal.Y); normalsData.Add(normal.Normal.Z); } } var vertices = mesh.Vertices as List <XbimPoint3D>; foreach (var point in vertices) { double x = point.X / meter, y = point.Y / meter, z = point.Z / meter; verticesData.Add(x); // convert to meter verticesData.Add(y); verticesData.Add(z); // update bounding box minX = Math.Min(minX, x); minY = Math.Min(minY, y); minZ = Math.Min(minZ, z); maxX = Math.Max(maxX, x); maxY = Math.Max(maxY, y); maxZ = Math.Max(maxZ, z); } if (minX == double.MaxValue || minY == double.MaxValue || minZ == double.MaxValue || maxX == double.MinValue || maxY == double.MinValue || maxZ == double.MinValue) { throw new Exception("Invalid Boundingbox"); } // do not trust instance.BoundingBox, it seems to be wrong. XbimRect3D bbox = new XbimRect3D(minX, minY, minZ, maxX - minX, maxY - minY, maxZ - minZ); // update boundingbox of current geometryStore geometryStore.BoundBox = GeometryStore.Union(geometryStore.BoundBox, bbox); // update boundingbox of ifc model modelBoundingBox = GeometryStore.Union(modelBoundingBox, bbox); meshStore.Vertexes = verticesData; meshStore.Normals = normalsData; meshStore.Indexes = indexesData; } } geometryStore.Meshes.Add(meshStore); } }