< Summary

Information
Class: Morpho25.Geometry.Building
Assembly: Morpho25
File(s): D:\a\Morpho\Morpho\project\Morpho\Morpho25\Geometry\Building.cs
Line coverage
23%
Covered lines: 56
Uncovered lines: 185
Coverable lines: 241
Total lines: 413
Line coverage: 23.2%
Branch coverage
20%
Covered branches: 24
Total branches: 118
Branch coverage: 20.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

D:\a\Morpho\Morpho\project\Morpho\Morpho25\Geometry\Building.cs

#LineLine coverage
 1using Morpho25.Utility;
 2using MorphoGeometry;
 3using Newtonsoft.Json;
 4using System;
 5using System.Collections.Generic;
 6using System.ComponentModel;
 7using System.ComponentModel.DataAnnotations;
 8using System.Linq;
 9using System.Threading.Tasks;
 10
 11namespace Morpho25.Geometry
 12{
 13    [DisplayName("Building")]
 14    /// <summary>
 15    /// Building class.
 16    /// </summary>
 17    public class Building : Entity, IEquatable<Building>
 18    {
 19        [DisplayName("Name")]
 20        [Description("Name of the building group")]
 21        [JsonProperty("name")]
 22        /// <summary>
 23        /// Name of the building.
 24        /// </summary>
 325        public override string Name { get; }
 26
 27        [DisplayName("Geometry")]
 28        [Description("Solid geometry")]
 29        [JsonProperty("geometry", Required = Required.Always)]
 30        /// <summary>
 31        /// Geometry of the building.
 32        /// </summary>
 633        public FaceGroup Geometry { get; set; }
 34
 35        [JsonIgnore]
 36        /// <summary>
 37        /// 2D Matrix from the top.
 38        /// </summary>
 039        public Matrix2d TopMatrix { get; private set; }
 40        [JsonIgnore]
 41        /// <summary>
 42        /// 2D Matrix from the bottom.
 43        /// </summary>
 044        public Matrix2d BottomMatrix { get; private set; }
 45        [JsonIgnore]
 46        /// <summary>
 47        /// 2D Matrix with building ID.
 48        /// </summary>
 049        public Matrix2d IDmatrix { get; private set; }
 50        [JsonIgnore]
 51        /// <summary>
 52        /// Collection of string based on Pixel and ID.
 53        /// </summary>
 054        public List<string> BuildingIDrows { get; private set; }
 55        [JsonIgnore]
 56        /// <summary>
 57        /// Collection of string based on Pixel and wall/roof materials.
 58        /// </summary>
 059        public List<string> BuildingWallRows { get; private set; }
 60        [JsonIgnore]
 61        /// <summary>
 62        /// Collection of string based on Pixel and green wall/ green roof materials.
 63        /// </summary>
 064        public List<string> BuildingGreenWallRows { get; private set; }
 65
 66        [DisplayName("Material")]
 67        [Description("Facade and roof materials")]
 68        [JsonProperty("material")]
 69        /// <summary>
 70        /// Material of the building.
 71        /// </summary>
 72        public override Material Material
 73        {
 974            get { return _material; }
 75            protected set
 376            {
 377                if (value.IDs.Length != 4)
 078                    throw new ArgumentOutOfRangeException(
 079                          $"{nameof(value)} should contain 4 materials. Use 'Building.CreateMaterial' method");
 80
 381                _material = value;
 382            }
 83
 84        }
 85
 86        [DisplayName("Observe BPS")]
 87        [Description("Export BPS output")]
 88        [JsonProperty("observeBPS")]
 89        /// <summary>
 90        /// Enable Building BPS output
 91        /// </summary>
 192        public bool ObserveBPS { get; }
 93
 94        [JsonConstructor]
 95        /// <summary>
 96        /// Create a building.
 97        /// </summary>
 98        /// <param name="geometry">Geometry of the building.</param>
 99        /// <param name="id">Numerical ID.</param>
 100        /// <param name="material">Material of the building.</param>
 101        /// <param name="name">Optional name.</param>
 102        /// <param name="observeBPS">Enable BPS calculation.</param>
 3103        public Building(FaceGroup geometry,
 3104            int id,
 3105            Material material = null,
 3106            string name = null,
 3107            bool observeBPS = false)
 3108        {
 3109            ID = id;
 3110            Geometry = geometry;
 3111            Material = material ?? CreateMaterial(null, null, null, null);
 3112            Name = name ?? "Building";
 3113            ObserveBPS = observeBPS;
 3114        }
 115
 116        public void SetMatrix(Grid grid)
 0117        {
 0118            Matrix2d topMatrix = new Matrix2d(grid.Size.NumX, grid.Size.NumY, "0");
 0119            Matrix2d bottomMatrix = new Matrix2d(grid.Size.NumX, grid.Size.NumY, "0");
 0120            Matrix2d idMatrix = new Matrix2d(grid.Size.NumX, grid.Size.NumY, "0");
 121
 0122            List<Ray> rays = EnvimetUtility.GetRayFromFacegroupBbox(grid, Geometry);
 123
 0124            IEnumerable<Vector> intersectionTop = EnvimetUtility.Raycasting2D(rays, Geometry, true);
 0125            IEnumerable<Vector> intersectionBottom = EnvimetUtility.Raycasting2D(rays, Geometry, false);
 126
 0127            SetMatrix(intersectionTop, grid, topMatrix, "");
 0128            SetMatrix(intersectionBottom, grid, bottomMatrix, "");
 0129            SetMatrix(intersectionTop, grid, idMatrix, ID.ToString());
 130
 0131            TopMatrix = topMatrix;
 0132            BottomMatrix = bottomMatrix;
 0133            IDmatrix = idMatrix;
 0134        }
 135
 136        public string Serialize()
 1137        {
 1138            return JsonConvert.SerializeObject(this);
 1139        }
 140
 141        public static Building Deserialize(string json)
 1142        {
 143            try
 1144            {
 1145                return JsonConvert.DeserializeObject<Building>(json);
 146            }
 0147            catch (Exception e)
 0148            {
 0149                throw new Exception(e.Message);
 150            }
 1151        }
 152
 153        public bool Equals(Building other)
 1154        {
 1155            if (other == null)
 0156                return false;
 157
 1158            if (other != null
 1159                && other.ID == this.ID
 1160                && other.Name == this.Name
 1161                && other.Material == this.Material
 1162                && other.Geometry == this.Geometry)
 1163                return true;
 164            else
 0165                return false;
 1166        }
 167
 168        public override bool Equals(Object obj)
 14169        {
 14170            if (obj == null)
 0171                return false;
 172
 14173            var buildObj = obj as Building;
 14174            if (buildObj == null)
 14175                return false;
 176            else
 0177                return Equals(buildObj);
 14178        }
 179
 180        public override int GetHashCode()
 0181        {
 182            unchecked
 0183            {
 0184                int hash = 17;
 0185                hash = hash * 23 + ID.GetHashCode();
 0186                hash = hash * 23 + Name.GetHashCode();
 0187                hash = hash * 23 + Material.GetHashCode();
 0188                hash = hash * 23 + Geometry.GetHashCode();
 0189                return hash;
 190            }
 0191        }
 192
 193        public static bool operator ==(Building build1, Building build2)
 16194        {
 16195            if (((object)build1) == null || ((object)build2) == null)
 16196                return Object.Equals(build1, build2);
 197
 0198            return build1.Equals(build2);
 16199        }
 200
 201        public static bool operator !=(Building build1, Building build2)
 1202        {
 1203            return !(build1 == build2);
 1204        }
 205
 206        private IEnumerable<string> GetBuildingRows(List<Pixel> pixels)
 0207        {
 208            //pixels = pixels
 209            //    .OrderBy(_ => _.I)
 210            //    .OrderBy(_ => _.J)
 211            //    .OrderBy(_ => _.K)
 212            //    .ToList();
 0213            foreach (var px in pixels)
 0214            {
 0215                yield return String.Format("{0},{1},{2},{3},{4}", px.I, px.J, px.K, 1, ID);
 0216            }
 0217        }
 218
 219        private IEnumerable<string> GetBuildingRows(List<Pixel> pixels,
 220            Grid grid, string wall, string roof)
 0221        {
 0222            var nullPx = new Pixel(0, 0, 0);
 0223            var one = 1;
 224
 225            // Matrix with default values
 0226            var matrix = new Matrix3d<string[]>(grid.Size.NumX + one,
 0227                grid.Size.NumY + one, grid.SequenceZ.Count() + one);
 228
 0229            for (int i = 0; i < matrix.GetLengthX(); i++)
 0230                for (int j = 0; j < matrix.GetLengthY(); j++)
 0231                    for (int k = 0; k < matrix.GetLengthZ(); k++)
 0232                        matrix[i, j, k] = new string[] { null, null, null };
 233
 0234            Parallel.For(0, pixels.Count, i =>
 0235            {
 0236                var line = string.Empty;
 0237                var px = pixels[i];
 0238
 0239                var li = pixels.FirstOrDefault(_ => _.I == px.I - 1 && _.J == px.J && _.K == px.K);
 0240                var lj = pixels.FirstOrDefault(_ => _.I == px.I && _.J == px.J - 1 && _.K == px.K);
 0241                var bk = pixels.FirstOrDefault(_ => _.I == px.I && _.J == px.J && _.K == px.K - 1);
 0242                var ri = pixels.FirstOrDefault(_ => _.I == px.I + 1 && _.J == px.J && _.K == px.K);
 0243                var rj = pixels.FirstOrDefault(_ => _.I == px.I && _.J == px.J + 1 && _.K == px.K);
 0244                var tk = pixels.FirstOrDefault(_ => _.I == px.I && _.J == px.J && _.K == px.K + 1);
 0245
 0246                var wallMat = (wall != Material.DEFAULT_GREEN_WALL) ? wall : null;
 0247                var roofMat = (roof != Material.DEFAULT_GREEN_ROOF) ? roof : null;
 0248
 0249                // Limits
 0250                var rlx = px.I + 1 < grid.Size.NumX;
 0251                var rly = px.J + 1 < grid.Size.NumY;
 0252
 0253                if (li == nullPx) matrix[px.I, px.J, px.K][0] = wallMat;
 0254                if (lj == nullPx) matrix[px.I, px.J, px.K][1] = wallMat;
 0255                if (bk == nullPx) matrix[px.I, px.J, px.K][2] = roofMat;
 0256                if (rlx && ri == nullPx) matrix[px.I + 1, px.J, px.K][0] = wallMat;
 0257                if (rly && rj == nullPx) matrix[px.I, px.J + 1, px.K][1] = wallMat;
 0258                if (tk == nullPx) matrix[px.I, px.J, px.K + 1][2] = roofMat;
 0259            });
 0260            var rows = EnvimetUtility.GetStringRows(matrix);
 0261            return rows;
 0262        }
 263
 264        private static void ShiftBuildings(Grid grid, List<Pixel> pixels,
 265            List<Pixel> terrainPixels)
 0266        {
 0267            var zLimit = grid.Size.NumZ - 1;
 268            // Envimet Spaces behavior
 0269            if (terrainPixels.Any())
 0270            {
 0271                Parallel.For(0, pixels.Count, i =>
 0272                {
 0273                    var offset = 0;
 0274                    var pixel = new Pixel(0, 0, 0);
 0275                    var query = terrainPixels
 0276                        .Where(_ => _.I == pixels[i].I && _.J == pixels[i].J);
 0277                    if (query.Any()) offset = query.Select(_ => _.K).Max();
 0278                    var x = pixels[i].I;
 0279                    var y = pixels[i].J;
 0280                    var z = pixels[i].K + offset;
 0281
 0282
 0283                    pixel.I = x;
 0284                    pixel.J = y;
 0285                    pixel.K = (z >= zLimit) ? zLimit : z;
 0286                    pixels[i] = pixel;
 0287                });
 0288            }
 0289        }
 290
 291        private static void ShiftBuildings(Grid grid, List<Pixel> pixels,
 292            List<Pixel> terrainPixels, int offset = 0)
 0293        {
 0294            var zLimit = grid.Size.NumZ - 1;
 0295            if (terrainPixels.Any())
 0296            {
 0297                Parallel.For(0, pixels.Count, i =>
 0298                {
 0299                    var pixel = new Pixel(0, 0, 0);
 0300                    var x = pixels[i].I;
 0301                    var y = pixels[i].J;
 0302                    var z = pixels[i].K + offset;
 0303
 0304                    pixel.I = x;
 0305                    pixel.J = y;
 0306                    pixel.K = (z >= zLimit) ? zLimit : z;
 0307                    pixels[i] = pixel;
 0308                });
 0309            };
 0310        }
 311
 312        private IEnumerable<Pixel> GetPixels(Grid grid)
 0313        {
 0314            List<Ray> rays = EnvimetUtility.GetRayFromFacegroupBbox(grid, Geometry);
 0315            var intersections = EnvimetUtility.Raycasting3D(rays, Geometry, false, false);
 0316            var centroids = EnvimetUtility.GetCentroids(grid, intersections);
 0317            var pixels = centroids.Select(_ => _.ToPixel(grid)).ToList();
 318
 0319            return pixels;
 0320        }
 321
 322        /// <summary>
 323        /// Generate model 3D. It should run after generating buildings and terrains.
 324        /// </summary>
 325        /// <param name="grid">Morpho Grid.</param>
 326        /// <param name="terrainPixels">Pixels of the terrain.</param>
 327        public void SetMatrix3d(Grid grid,
 328            List<Pixel> terrainPixels = null,
 329            bool shiftEachVoxel = false)
 0330        {
 0331            Reset3Dmatrix();
 332
 0333            var pixels = GetPixels(grid).ToList();
 0334            if (!pixels.Any()) return;
 335
 0336            if (terrainPixels != null)
 0337            {
 0338                var tPixels = FilterPixels(terrainPixels, pixels);
 0339                if (shiftEachVoxel)
 0340                {
 0341                    ShiftBuildings(grid, pixels, tPixels);
 0342                }
 343                else
 0344                {
 0345                    var offset = 0;
 0346                    if (tPixels.Any())
 0347                    {
 0348                        var groups = tPixels.GroupBy(_ => new { I = _.I, J = _.J });
 0349                        offset = groups.Select(_ => _.Select(i => i.K).Max()).Min();
 0350                    }
 0351                    ShiftBuildings(grid, pixels, tPixels, offset);
 0352                }
 0353            }
 354
 0355            BuildingIDrows.AddRange(GetBuildingRows(pixels));
 0356            var wallDB = GetBuildingRows(pixels, grid,
 0357                Material.IDs[0], Material.IDs[1]);
 0358            BuildingWallRows.AddRange(wallDB);
 359
 0360            if (Material.IDs[2] != Material.DEFAULT_GREEN_WALL ||
 0361                Material.IDs[3] != Material.DEFAULT_GREEN_WALL)
 0362            {
 0363                var greeningsDB = GetBuildingRows(pixels, grid,
 0364                    Material.IDs[0], Material.IDs[1]);
 0365                BuildingGreenWallRows.AddRange(greeningsDB);
 0366            }
 0367        }
 368
 369        private List<Pixel> FilterPixels(List<Pixel> terrainPixels,
 370            List<Pixel> pixels)
 0371        {
 0372            var allI = pixels.Select(_ => _.I).Distinct().ToList();
 0373            var allj = pixels.Select(_ => _.J).Distinct().ToList();
 374
 0375            var tPixels = new List<Pixel>();
 0376            foreach (var pixel in terrainPixels)
 0377            {
 0378                if (!allI.Contains(pixel.I)) continue;
 0379                if (!allj.Contains(pixel.J)) continue;
 0380                tPixels.Add(pixel);
 0381            }
 382
 0383            return tPixels;
 0384        }
 385
 386        private void Reset3Dmatrix()
 0387        {
 0388            BuildingWallRows = new List<string>();
 0389            BuildingGreenWallRows = new List<string>();
 0390            BuildingIDrows = new List<string>();
 0391        }
 392
 393        /// <summary>
 394        /// String representation of a building.
 395        /// </summary>
 396        /// <returns>String representation.</returns>
 397        public override string ToString()
 0398        {
 0399            string name = (Name != " ") ? Name : "Building";
 0400            return String.Format("{0}::{1}::{2}", name, ID, String.Join(",", Material.IDs));
 0401        }
 402
 403        public static Material CreateMaterial(string wallCode, string roofCode, string greenWallCode, string greenRoofCo
 2404        {
 2405            string wall = wallCode ?? Material.DEFAULT_WALL;
 2406            string roof = roofCode ?? Material.DEFAULT_ROOF;
 2407            string greenWall = greenWallCode ?? Material.DEFAULT_GREEN_WALL;
 2408            string greenRoof = greenRoofCode ?? Material.DEFAULT_GREEN_ROOF;
 409
 2410            return new Material(new string[] { wall, roof, greenWall, greenRoof });
 2411        }
 412    }
 413}