/// <summary> /// Creates Levels and LevelPerimeters from an incoming Envelope and height arguments. /// </summary> /// <param name="model">The input model.</param> /// <param name="input">The arguments to the execution.</param> /// <returns>A LevelsByEnvelopeOutputs instance containing computed results and the model with any new elements.</returns> public static SimpleLevelsByEnvelopeOutputs Execute(Dictionary <string, Model> inputModels, SimpleLevelsByEnvelopeInputs input) { inputModels.TryGetValue("Envelope", out var model); if (model == null || model.AllElementsOfType <Envelope>().Count() == 0) { throw new ArgumentException("No Envelope found."); } var envelopes = model.AllElementsOfType <Envelope>(); var envelopesOrdered = envelopes.OrderBy(e => e.Elevation); var topHeightsOrdered = envelopes.Select(e => e.Elevation + e.Height).OrderBy(e => e); var minElevation = envelopesOrdered.Select(e => e.Elevation).First(); var maxElevation = topHeightsOrdered.Last(); var minLevelHeight = 3.0; var levels = new List <Level>(); var grade = envelopesOrdered.First(e => e.Elevation + e.Height > 0).Elevation; var currentLevel = grade; var heightIndex = 0; // base and normal levels while (currentLevel < maxElevation - input.TopLevelHeight - minLevelHeight) { Console.WriteLine($"Adding Level {levels.Count + 1:0} at elevation {currentLevel}"); levels.Add(new Level(currentLevel, Guid.NewGuid(), $"Level {levels.Count + 1:0}")); currentLevel += input.BaseLevels[Math.Min(heightIndex++, input.BaseLevels.Count - 1)]; } // penthouse + roof level levels.Add(new Level(maxElevation - input.TopLevelHeight, Guid.NewGuid(), "Penthouse Level")); levels.Add(new Level(maxElevation, Guid.NewGuid(), "Roof Level")); if (minElevation < grade) { // subgrade levels currentLevel = -input.SubgradeLevelHeight; var subgradeLevelCounter = 1; while (currentLevel > minElevation + input.SubgradeLevelHeight) { levels.Add(new Level(currentLevel, Guid.NewGuid(), $"Level B{subgradeLevelCounter:0}")); currentLevel -= input.SubgradeLevelHeight; subgradeLevelCounter++; } levels.Add(new Level(minElevation, Guid.NewGuid(), $"Level B{subgradeLevelCounter:0}")); } var matl = BuiltInMaterials.Glass; matl.SpecularFactor = 0.5; matl.GlossinessFactor = 0.0; // construct level perimeters and calculate areas var levelPerimeters = new List <LevelPerimeter>(); var levelVolumes = new List <LevelVolume>(); var areaTotal = 0.0; var subGradeArea = 0.0; var aboveGradeArea = 0.0; var subgradeLevels = levels.Where(l => l.Elevation < grade); var aboveGradeLevels = levels.Where(l => l.Elevation >= grade).OrderBy(l => l.Elevation); foreach (var envelope in envelopes) { var min = envelope.Elevation; var max = envelope.Elevation + envelope.Height; var envSubGrade = subgradeLevels.Where(l => l.Elevation < max && l.Elevation >= min).ToList(); for (int i = 0; i < envSubGrade.Count(); i++) { var l = envSubGrade[i]; var levelAbove = i == 0 ? aboveGradeLevels.First() : envSubGrade[i - 1]; var subGradePerimeter = new LevelPerimeter(envelope.Profile.Area(), l.Elevation, envelope.Profile.Perimeter, Guid.NewGuid(), l.Name); levelPerimeters.Add(subGradePerimeter); subGradeArea += subGradePerimeter.Area; areaTotal += subGradePerimeter.Area; var levelHeight = levelAbove.Elevation - l.Elevation; var representation = new Representation(new SolidOperation[] { new Extrude(envelope.Profile, levelHeight, Vector3.ZAxis, false) }); var subGradeVolume = new LevelVolume(envelope.Profile, levelHeight, envelope.Profile.Area(), new Transform(0, 0, l.Elevation), matl, representation, false, Guid.NewGuid(), l.Name); levelVolumes.Add(subGradeVolume); } if (envSubGrade.Count > 0) { // if this was a subgrade envelope, let's not add anything else. continue; } // var envAboveGrade = aboveGradeLevels.Where(l => l.Elevation < max - minLevelHeight && l.Elevation >= min).ToList(); // We want to make sure we start a level at the very base of the envelope. var aboveGradeLevelsWithinEnvelope = aboveGradeLevels.Where(l => l.Elevation > min + minLevelHeight && l.Elevation < max - minLevelHeight).ToList(); var nameForMin = aboveGradeLevels.LastOrDefault(l => l.Elevation < min + minLevelHeight)?.Name ?? ""; var nameForMax = aboveGradeLevels.FirstOrDefault(l => l.Elevation > max - minLevelHeight)?.Name ?? ""; for (int i = -1; i < aboveGradeLevelsWithinEnvelope.Count(); i++) { var name = nameForMin; if (i > -1 && i < aboveGradeLevelsWithinEnvelope.Count) { name = aboveGradeLevelsWithinEnvelope[i].Name; } // else // { // name = nameForMax; // } var levelElevation = i == -1 ? min : aboveGradeLevelsWithinEnvelope[i].Elevation; var nextLevelElevation = i == aboveGradeLevelsWithinEnvelope.Count - 1 ? max : aboveGradeLevelsWithinEnvelope[i + 1].Elevation; if (nextLevelElevation > max - minLevelHeight) { nextLevelElevation = max; } levelPerimeters.Add(new LevelPerimeter(envelope.Profile.Area(), levelElevation, envelope.Profile.Perimeter, Guid.NewGuid(), name)); aboveGradeArea += envelope.Profile.Area(); areaTotal += aboveGradeArea; var levelHeight = nextLevelElevation - levelElevation; var newProfile = envelope.Profile; try { var profileOffset = envelope.Profile.Perimeter.Offset(-0.1); newProfile = new Profile(profileOffset[0], envelope.Profile.Voids, Guid.NewGuid(), "Level volume representation"); } catch { } var representation = new Representation(new SolidOperation[] { new Extrude(newProfile, levelHeight, Vector3.ZAxis, false) }); var subGradeVolume = new LevelVolume(envelope.Profile, levelHeight, envelope.Profile.Area(), new Transform(0, 0, levelElevation), matl, representation, false, Guid.NewGuid(), name); levelVolumes.Add(subGradeVolume); } // Add a roof perimeter so floors are created, but don't count roof area levelPerimeters.Add(new LevelPerimeter(envelope.Profile.Area(), max, envelope.Profile.Perimeter, Guid.NewGuid(), "Roof")); } var output = new SimpleLevelsByEnvelopeOutputs(levels.Count, areaTotal, subGradeArea, aboveGradeArea); output.Model.AddElements(levels.OrderByDescending(l => l.Elevation)); output.Model.AddElements(levelPerimeters); output.Model.AddElements(levelVolumes); foreach (var levelPerimeter in levelPerimeters) { output.Model.AddElement(new Panel(levelPerimeter.Perimeter.Project(new Plane(Vector3.Origin, Vector3.ZAxis)), matl, new Transform(0.0, 0.0, levelPerimeter.Elevation), null, false, Guid.NewGuid(), levelPerimeter.Name)); } return(output); }
/// <summary> /// Creates Levels and LevelPerimeters from an incoming Envelope and height arguments. /// </summary> /// <param name="model">The input model.</param> /// <param name="input">The arguments to the execution.</param> /// <returns>A LevelsByEnvelopeOutputs instance containing computed results and the model with any new elements.</returns> public static SimpleLevelsByEnvelopeOutputs Execute(Dictionary <string, Model> inputModels, SimpleLevelsByEnvelopeInputs input) { inputModels.TryGetValue("Envelope", out var model); if (model == null || model.AllElementsOfType <Envelope>().Count() == 0) { throw new ArgumentException("No Envelope found."); } var envelopes = model.AllElementsOfType <Envelope>(); CorrectEnvelopeHeightsAndElevations(envelopes); var envelopesOrdered = envelopes.OrderBy(e => e.Elevation); var topHeightsOrdered = envelopes.Select(e => e.Elevation + e.Height).OrderBy(e => e); var minElevation = envelopesOrdered.Select(e => e.Elevation).First(); var maxElevation = topHeightsOrdered.Last(); var minLevelHeight = 3.0; var levels = new List <Level>(); var grade = envelopesOrdered.First(e => e.Elevation + e.Height > 0).Elevation; var currentLevel = grade; var heightIndex = 0; // base and normal levels while (currentLevel < maxElevation - input.TopLevelHeight - minLevelHeight) { Console.WriteLine($"Adding Level {levels.Count + 1:0} at elevation {currentLevel}"); levels.Add(new Level(currentLevel, Guid.NewGuid(), $"Level {levels.Count + 1:0}")); currentLevel += input.BaseLevels[Math.Min(heightIndex++, input.BaseLevels.Count - 1)]; } // penthouse + roof level levels.Add(new Level(maxElevation - input.TopLevelHeight, Guid.NewGuid(), "Penthouse Level")); levels.Add(new Level(maxElevation, Guid.NewGuid(), "Roof Level")); if (minElevation < grade) { // subgrade levels currentLevel = -input.SubgradeLevelHeight; var subgradeLevelCounter = 1; while (currentLevel > minElevation + input.SubgradeLevelHeight) { levels.Add(new Level(currentLevel, Guid.NewGuid(), $"Level B{subgradeLevelCounter:0}")); currentLevel -= input.SubgradeLevelHeight; subgradeLevelCounter++; } levels.Add(new Level(minElevation, Guid.NewGuid(), $"Level B{subgradeLevelCounter:0}")); } LevelMaterial = BuiltInMaterials.Glass; LevelMaterial.SpecularFactor = 0.5; LevelMaterial.GlossinessFactor = 0.0; // construct level perimeters and calculate areas var levelPerimeters = new List <LevelPerimeter>(); var levelVolumes = new List <LevelVolume>(); var scopes = new List <ViewScope>(); var areaTotal = 0.0; var subGradeArea = 0.0; var aboveGradeArea = 0.0; var subgradeLevels = levels.Where(l => l.Elevation < grade); var aboveGradeLevels = levels.Where(l => l.Elevation >= grade).OrderBy(l => l.Elevation); foreach (var envelope in envelopes) { var envelopeRepresentation = envelope.Representation; var min = envelope.Elevation; var max = envelope.Elevation + envelope.Height; var envelopeProfile = envelope.Profile; var hasAnyNonExtrudeEnvelopes = envelope.Representation.SolidOperations.Any(so => so.GetType() != typeof(Extrude)); var envSubGrade = subgradeLevels.Where(l => l.Elevation < max && l.Elevation >= min).ToList(); for (int i = 0; i < envSubGrade.Count(); i++) { var l = envSubGrade[i]; var levelAbove = i == 0 ? aboveGradeLevels.First() : envSubGrade[i - 1]; var levelHeight = levelAbove.Elevation - l.Elevation; var levelArea = ProcessSingleLevel(l.Elevation, l.Name, levelHeight, hasAnyNonExtrudeEnvelopes, envelope, scopes, levelVolumes, envelopeProfile, levelPerimeters); areaTotal += levelArea; subGradeArea += levelArea; } if (envSubGrade.Count > 0) { // if this was a subgrade envelope, let's not consider levels above grade. continue; } // We want to make sure we start a level at the very base of the envelope. var aboveGradeLevelsWithinEnvelope = aboveGradeLevels.Where(l => l.Elevation > min + minLevelHeight && l.Elevation < max - minLevelHeight).ToList(); var nameForMin = aboveGradeLevels.LastOrDefault(l => l.Elevation < min + minLevelHeight)?.Name ?? ""; for (int i = -1; i < aboveGradeLevelsWithinEnvelope.Count(); i++) { var name = nameForMin; if (i > -1 && i < aboveGradeLevelsWithinEnvelope.Count) { name = aboveGradeLevelsWithinEnvelope[i].Name; } var levelElevation = i == -1 ? min : aboveGradeLevelsWithinEnvelope[i].Elevation; var nextLevelElevation = i == aboveGradeLevelsWithinEnvelope.Count - 1 ? max : aboveGradeLevelsWithinEnvelope[i + 1].Elevation; if (nextLevelElevation > max - minLevelHeight) { nextLevelElevation = max; } var levelHeight = nextLevelElevation - levelElevation; var levelArea = ProcessSingleLevel(levelElevation, name, levelHeight, hasAnyNonExtrudeEnvelopes, envelope, scopes, levelVolumes, envelopeProfile, levelPerimeters); aboveGradeArea += levelArea; areaTotal += levelArea; } } var output = new SimpleLevelsByEnvelopeOutputs(levels.Count, areaTotal, subGradeArea, aboveGradeArea); output.Model.AddElements(scopes); output.Model.AddElements(levels.OrderByDescending(l => l.Elevation)); output.Model.AddElements(levelPerimeters); output.Model.AddElements(levelVolumes); foreach (var levelPerimeter in levelPerimeters) { output.Model.AddElement(new Panel(levelPerimeter.Perimeter.Project(new Plane(Vector3.Origin, Vector3.ZAxis)), LevelMaterial, new Transform(0.0, 0.0, levelPerimeter.Elevation), null, false, Guid.NewGuid(), levelPerimeter.Name)); } return(output); }