public override string SolvePart2() { var step = 0; while (grid.GetCells(c => c.Value == 0).Count != grid.Count) { DoFlashes(); step++; } return(step.ToString()); }
public static void DrawGrid(Model model, Grid2d grid, List <Vector3> uPoints, List <Vector3> vPoints) { foreach (var cell in grid.GetCells()) { var polygon = (Polygon)cell.GetCellGeometry(); foreach (var vertex in polygon.Vertices) { Debug.DrawPoint(model, vertex, MagentaMaterial); } var color = RandomExtensions.NextColor(random); foreach (var cellPiece in cell.GetTrimmedCellGeometry()) { var material = new Material(color.ToString(), new Color(color.Red, color.Green, color.Blue, 0.5), unlit: true); var poly = (Polygon)cellPiece; if (poly.Vertices.Count >= 3) { model.AddElement(new Panel(poly, material: material)); } } } foreach (var pt in vPoints) { Debug.DrawPoint(model, pt, MagentaMaterial); } foreach (var pt in uPoints) { Debug.DrawPoint(model, pt, MagentaMaterial); } }
public void TrimBehavior() { Name = "TrimBehavior"; var polygonjson = "[{\"discriminator\":\"Elements.Geometry.Polygon\",\"Vertices\":[{\"X\":-14.371519985751306,\"Y\":-4.8816304299427005,\"Z\":0.0},{\"X\":-17.661873645682569,\"Y\":9.2555712951713573,\"Z\":0.0},{\"X\":12.965610421927806,\"Y\":9.2555712951713573,\"Z\":0.0},{\"X\":12.965610421927806,\"Y\":3.5538269529982784,\"Z\":0.0},{\"X\":6.4046991240848143,\"Y\":3.5538269529982784,\"Z\":0.0},{\"X\":1.3278034769444158,\"Y\":-4.8816304299427005,\"Z\":0.0}]},{\"discriminator\":\"Elements.Geometry.Polygon\",\"Vertices\":[{\"X\":-9.4508365123690652,\"Y\":0.20473478280229102,\"Z\":0.0},{\"X\":-1.8745460850979974,\"Y\":0.20473478280229102,\"Z\":0.0},{\"X\":-1.8745460850979974,\"Y\":5.4378426037008651,\"Z\":0.0},{\"X\":-9.4508365123690652,\"Y\":5.4378426037008651,\"Z\":0.0}]}]\r\n"; var polygons = JsonConvert.DeserializeObject <List <Polygon> >(polygonjson); var grid = new Grid2d(polygons); foreach (var pt in polygons[1].Vertices) { grid.SplitAtPoint(pt); } grid.CellsFlat.ForEach(c => c.U.DivideByApproximateLength(1.0, EvenDivisionMode.RoundDown)); var trimmedCells = grid.GetCells().Select(c => (TrimmedGeometry: c.GetTrimmedCellGeometry(), BaseRect: c.GetCellGeometry(), IsTrimmed: c.IsTrimmed())); foreach (var trimGeometry in trimmedCells) { var trimGeo = trimGeometry.TrimmedGeometry.OfType <Polygon>(); var material = trimGeometry.IsTrimmed ? BuiltInMaterials.XAxis : BuiltInMaterials.ZAxis; foreach (var t in trimGeo) { Model.AddElement(new ModelCurve(t)); Model.AddElement(new Mass(t, 1, material, new Transform(0, 0, -1.001))); } } Assert.Equal(87, trimmedCells.Count()); Assert.Equal(18, trimmedCells.Count(c => c.IsTrimmed)); }
public void Grid2d() { this.Name = "Elements_Spatial_Grid2d"; // <example> // Create a 2d grid that's 40 x 30 in size var grid = new Grid2d(40, 30); // Access the U and V axes directly and use 1d subdivision methods on them grid.U.DivideByFixedLength(7, FixedDivisionMode.RemainderAtBothEnds); grid.V.DivideByPattern(new[] { 2.0, 5.0 }); // Get a row by index var fifthRow = grid.GetRowAtIndex(4); // Divide U axis of all cells in row into panels of approximate width 1 fifthRow.ForEach(c => c.U.DivideByApproximateLength(1)); // Get a cell by u, v indices var cell = grid[1, 1]; // Divide the cell in the V direction cell.V.DivideByCount(4); // Create a floor from the entire grid's boundary var floor = new Floor(new Profile((Polygon)grid.GetCellGeometry()), 0.5, new Transform(0, 0, -0.51)); // Create model curves from all subdivided cells of the grid var modelCurves = grid.GetCells().Select(c => new ModelCurve(c.GetCellGeometry())); // </example> Model.AddElement(floor); Model.AddElements(modelCurves); }
/// <summary> /// Construct a set of elements from this rule for a given definition. /// </summary> /// <param name="definition">The definition to instantiate.</param> public List <Element> Instantiate(ComponentDefinition definition) { var arrayElements = new List <Element>(); var newVertices = PolylinePlacementRule.TransformPolyline(this, definition); var path = new Polygon(newVertices); var grid2d = new Grid2d(path, definition.OrientationGuide); GridCreationRule(grid2d); var cells = grid2d.GetCells().Where(c => !c.IsTrimmed()).SelectMany(c => c.GetTrimmedCellGeometry()).OfType <Polygon>().Where(c => c.Area().ApproximatelyEquals(CellDefinition.CellLength * CellDefinition.CellWidth)); foreach (var element in CellDefinition.Elements) { foreach (var cell in cells) { var transform = new Transform(element.Transform); transform.Concatenate(definition.OrientationGuide); transform.Concatenate(new Transform(cell.Vertices[2])); element.IsElementDefinition = true; var instance = element.CreateInstance(transform, null); arrayElements.Add(instance); } } arrayElements.AddRange(cells.Select(c => new ModelCurve(c))); return(arrayElements); }
public void Grid2dSerializes() { Name = "grid2d serializes"; var polyline = new Polyline(new[] { new Vector3(0, 0, 0), new Vector3(10, 2, 0), new Vector3(30, 4, 0), }); var uGrid = new Grid1d(polyline); var p2 = new Line(Vector3.Origin, new Vector3(0, 20, 0)); var vGrid = new Grid1d(p2); var grid2d = new Grid2d(uGrid, vGrid); grid2d.U.DivideByCount(10); grid2d.V.DivideByCount(3); grid2d[2, 2].U.DivideByCount(4); var json = JsonConvert.SerializeObject(grid2d); var deserialized = JsonConvert.DeserializeObject <Grid2d>(json); Assert.Equal(grid2d.GetCells().Count, deserialized.GetCells().Count); var grid2dElem = new Grid2dElement(grid2d, Guid.NewGuid(), "Grid"); Model.AddElement(grid2dElem); }
public void CellComplexExample() { this.Name = "Elements_Spatial_CellComplex_CellComplex"; // <example> // Assemble CellComplex from Grid2d var numLevels = 10; var levelHeight = 1; var cellSize = 2; var complex = new CellComplex(); var boundary = new Circle(new Vector3(), 10).ToPolygon(); var grid = new Grid2d(boundary, Vector3.Origin, Vector3.XAxis, Vector3.YAxis); var pathMaterial = new Material("Path", new Color(1, 0, 0, 0.75)); grid.U.DivideByFixedLength(cellSize); grid.V.DivideByFixedLength(cellSize); for (var i = 0; i < numLevels; i++) { foreach (var cell in grid.GetCells()) { foreach (var crv in cell.GetTrimmedCellGeometry()) { complex.AddCell((Polygon)crv, levelHeight, i * levelHeight, grid.U, grid.V); } } } // Draw base CellComplex foreach (var face in complex.GetFaces()) { this.Model.AddElement(new Panel(face.GetGeometry(), BuiltInMaterials.Mass)); } // Traverse CellComplex var start = new Vector3(15, 15, 15); var end = new Vector3(-15, -15, -15); // Draw lines from start and end to closest points, for reference foreach (var pt in new List <Vector3>() { start, end }) { var closest = complex.GetClosestVertex(pt).GetGeometry(); this.Model.AddElement(new ModelCurve(new Line(pt, closest), pathMaterial)); } var curCell = complex.GetClosestCell(start); var traversedCells = curCell.TraverseNeighbors(end); foreach (var cell in traversedCells) { var rep = new Representation(new[] { cell.GetGeometry() }); this.Model.AddElement(new GeometricElement(new Transform(), pathMaterial, rep, false, Guid.NewGuid(), "Path")); } // </example> }
/// <summary> /// The ReceptionLayout function. /// </summary> /// <param name="model">The input model.</param> /// <param name="input">The arguments to the execution.</param> /// <returns>A ReceptionLayoutOutputs instance containing computed results and the model with any new elements.</returns> public static ReceptionLayoutOutputs Execute(Dictionary <string, Model> inputModels, ReceptionLayoutInputs input) { var spacePlanningZones = inputModels["Space Planning Zones"]; var levelsModel = inputModels["Levels"]; var levels = spacePlanningZones.AllElementsOfType <LevelElements>(); var levelVolumes = levelsModel.AllElementsOfType <LevelVolume>(); var output = new ReceptionLayoutOutputs(); var configJson = File.ReadAllText("./ReceptionConfigurations.json"); var configs = JsonConvert.DeserializeObject <SpaceConfiguration>(configJson); var hasCore = inputModels.TryGetValue("Core", out var coresModel); List <Line> coreSegments = new List <Line>(); if (coresModel != null) { coreSegments.AddRange(coresModel.AllElementsOfType <ServiceCore>().SelectMany(c => c.Profile.Perimeter.Segments())); } foreach (var lvl in levels) { var corridors = lvl.Elements.OfType <Floor>(); var corridorSegments = corridors.SelectMany(p => p.Profile.Segments()); var meetingRmBoundaries = lvl.Elements.OfType <SpaceBoundary>().Where(z => z.Name == "Reception"); var levelVolume = levelVolumes.First(l => l.Name == lvl.Name); foreach (var room in meetingRmBoundaries) { var spaceBoundary = room.Boundary; Line orientationGuideEdge = hasCore ? FindEdgeClosestToCore(spaceBoundary.Perimeter, coreSegments) : FindEdgeAdjacentToSegments(spaceBoundary.Perimeter.Segments(), corridorSegments, out var wallCandidates); var orientationTransform = new Transform(Vector3.Origin, orientationGuideEdge.Direction(), Vector3.ZAxis); var boundaryCurves = new List <Polygon>(); boundaryCurves.Add(spaceBoundary.Perimeter); boundaryCurves.AddRange(spaceBoundary.Voids ?? new List <Polygon>()); var grid = new Grid2d(boundaryCurves, orientationTransform); foreach (var cell in grid.GetCells()) { var rect = cell.GetCellGeometry() as Polygon; var segs = rect.Segments(); var width = segs[0].Length(); var depth = segs[1].Length(); var trimmedGeo = cell.GetTrimmedCellGeometry(); if (!cell.IsTrimmed() && trimmedGeo.Count() > 0) { output.Model.AddElement(InstantiateLayout(configs, width, depth, rect, room.Transform)); } else if (trimmedGeo.Count() > 0) { var largestTrimmedShape = trimmedGeo.OfType <Polygon>().OrderBy(s => s.Area()).Last(); var cinchedVertices = rect.Vertices.Select(v => largestTrimmedShape.Vertices.OrderBy(v2 => v2.DistanceTo(v)).First()).ToList(); var cinchedPoly = new Polygon(cinchedVertices); // output.Model.AddElement(new ModelCurve(cinchedPoly, BuiltInMaterials.ZAxis, levelVolume.Transform)); output.Model.AddElement(InstantiateLayout(configs, width, depth, cinchedPoly, room.Transform)); Console.WriteLine("ЪциРђЇРЎѓ№ИЈ funny shape!!!"); } } } } InstancePositionOverrides(input.Overrides, output.Model); return(output); }
// Utility private static CellComplex MakeASimpleCellComplex( double uCellSize = 10, double vCellSize = 10, double uNumCells = 5, double vNumCells = 5, double cellHeight = 5, double numLevels = 3, Nullable <Vector3> origin = null, Nullable <Vector3> uDirection = null, Nullable <Vector3> vDirection = null, Polygon polygon = null ) { var orig = origin == null ? new Vector3() : (Vector3)origin; var uDir = uDirection == null ? new Vector3(1, 0, 0) : ((Vector3)uDirection).Unitized(); var vDir = vDirection == null ? new Vector3(0, 1, 0) : ((Vector3)vDirection).Unitized(); var uLength = orig.X + uCellSize * uNumCells; var vLength = orig.Y + vCellSize * vNumCells; // Create Grid2d var boundary = polygon == null?Polygon.Rectangle(orig, new Vector3(uLength, vLength)) : polygon; // Using constructor with origin var grid = new Grid2d(boundary, orig, uDir, vDir); for (var u = uCellSize; u < uLength; u += uCellSize) { grid.SplitAtPoint(orig + (uDir * u)); } for (var v = vCellSize; v < vLength; v += vCellSize) { grid.SplitAtPoint(orig + (vDir * v)); } var cellComplex = new CellComplex(Guid.NewGuid(), "Test"); for (var i = 0; i < numLevels; i++) { foreach (var cell in grid.GetCells()) { foreach (var crv in cell.GetTrimmedCellGeometry()) { cellComplex.AddCell((Polygon)crv, 5, cellHeight * i, grid.U, grid.V); } } } return(cellComplex); }
private HashSet <Edge> AddFromGrid(Grid2d grid, IEnumerable <Edge> edgesToIntersect) { var cells = grid.GetCells(); var addedEdges = new HashSet <Edge>(); var edgeCandidates = new HashSet <(ulong, ulong)>(); Action <Vector3, Vector3> add = (Vector3 start, Vector3 end) => { var v0 = AddVertex(start); var v1 = AddVertex(end); if (v0 != v1) { var pair = v0.Id < v1.Id ? (v0.Id, v1.Id) : (v1.Id, v0.Id); edgeCandidates.Add(pair); }
public void GenerateAndSubdivide2d() { var grid = new Grid2d(10, 10); grid.U.SplitAtPosition(2); grid.U.SplitAtPosition(7); grid.V.SplitAtPosition(5); var subGrid = grid[1, 0]; subGrid.U.DivideByCount(5); var subGrid2 = grid[1, 1]; subGrid2.V.DivideByFixedLengthFromPosition(0.5, 8); Assert.Equal(6, grid.CellsFlat.Count); Assert.Equal(19, grid.GetCells().Count); }
public void TrimmedCellProfileAfterSplitting() { var outer = new Polygon(new List <Vector3> { new Vector3(0, 0, 0), new Vector3(10, 0, 0), new Vector3(10, 0, 10), new Vector3(0, 0, 10) }); var inner1 = new Polygon(new List <Vector3> { new Vector3(3, 0, 9), new Vector3(3, 0, 7), new Vector3(1, 0, 7), new Vector3(1, 0, 9) }); var inner2 = new Polygon(new List <Vector3> { new Vector3(9, 0, 3), new Vector3(9, 0, 1), new Vector3(7, 0, 1), new Vector3(7, 0, 3) }); var inner3 = new Polygon(new List <Vector3> { new Vector3(6, 0, 6), new Vector3(6, 0, 3), new Vector3(3, 0, 3), new Vector3(3, 0, 6) }); var polygons = new List <Polygon>(); polygons.Add(outer); polygons.Add(inner1); polygons.Add(inner2); polygons.Add(inner3); var grid = new Grid2d(polygons); grid.SplitAtPoints(new List <Vector3> { new Vector3(4, 0, 0), new Vector3(6, 0, 0) }); var cells = grid.GetCells(); var cell = cells[0]; var profiles = cell.GetTrimmedCellProfiles(); Assert.Single(profiles); Assert.Single(profiles.First().Voids); cell = cells[1]; profiles = cell.GetTrimmedCellProfiles(); Assert.Equal(2, profiles.Count()); Assert.Empty(profiles.First().Voids); Assert.Empty(profiles.Last().Voids); cell = cells[2]; profiles = cell.GetTrimmedCellProfiles(); Assert.Single(profiles); Assert.Single(profiles.First().Voids); }
public void NoExceptionsThrownWithAnyRotation() { for (int rotation = 0; rotation < 360; rotation += 10) { var a = new Vector3(0.03, 5.08); var b = new Vector3(4.28, 9.80); var c = new Vector3(9.69, 9.50); var d = new Vector3(9.63, 2.43); var e = new Vector3(4.72, -0.86); var f = new Vector3(1.78, -0.75); var polygon = new Polygon(new[] { a, b, c, d, e, f }); var g = new Vector3(7.735064, 5.746821); var h = new Vector3(6.233137, 7.248748); var i = new Vector3(3.660163, 4.675775); var j = new Vector3(5.162091, 3.173848); var polygon2 = new Polygon(new[] { g, h, i, j }); var alignment = new Transform(); alignment.Rotate(Vector3.ZAxis, 45); var grid = new Grid2d(new[] { polygon, polygon2 }, alignment); grid.U.DivideByCount(10); var panelA = ("A", 1.0); var panelB = ("B", 0.5); var panelC = ("C", 1.5); var pattern = new[] { panelA, panelB, panelC }; var pattern2 = new[] { panelB, panelA }; var patterns = new[] { pattern, pattern2 }; for (int index = 0; index < grid.CellsFlat.Count; index++) { var vDomain = grid.CellsFlat[index].V.Domain; var start = 0.1.MapToDomain(vDomain); grid.CellsFlat[index].V.DivideByPattern(patterns[index % patterns.Count()], PatternMode.Cycle, FixedDivisionMode.RemainderAtBothEnds); } var cells = grid.GetCells(); var geo = cells.Select(cl => cl.GetTrimmedCellGeometry()); var types = cells.Select(cl => cl.Type); var trimmed = cells.Select(cl => cl.IsTrimmed()); } //Test verifies no exceptions are thrown at any rotation }
public void SeparatorsFromNestedGridFromPolygons() { var a = new Vector3(0.03, 5.08); var b = new Vector3(4.28, 9.80); var c = new Vector3(9.69, 9.50); var d = new Vector3(9.63, 2.43); var e = new Vector3(4.72, -0.86); var f = new Vector3(1.78, -0.75); var polygon = new Polygon(new[] { a, b, c, d, e, f }); var g = new Vector3(7.735064, 5.746821); var h = new Vector3(6.233137, 7.248748); var i = new Vector3(3.660163, 4.675775); var j = new Vector3(5.162091, 3.173848); var polygon2 = new Polygon(new[] { g, h, i, j }); var orientation = new Transform(); orientation.Rotate(Vector3.ZAxis, 15); var grid = new Grid2d(new[] { polygon, polygon2 }, orientation); grid.U.DivideByCount(3); grid.V.SplitAtParameter(0.5); grid[1, 0].V.DivideByCount(5); var cells = grid.GetCells(); var geo = cells.Select(cl => cl.GetTrimmedCellGeometry()); var types = cells.Select(cl => cl.Type); var trimmed = cells.Select(cl => cl.IsTrimmed()); var uLines = grid.GetCellSeparators(GridDirection.U); var vLines = grid.GetCellSeparators(GridDirection.V); var dict = new Dictionary <string, object> { { "Cell Geometry", geo }, { "U Lines", uLines }, { "V Lines", vLines } }; }
public void TrimBehavior() { var polygonjson = "[{\"discriminator\":\"Elements.Geometry.Polygon\",\"Vertices\":[{\"X\":-14.371519985751306,\"Y\":-4.8816304299427005,\"Z\":0.0},{\"X\":-17.661873645682569,\"Y\":9.2555712951713573,\"Z\":0.0},{\"X\":12.965610421927806,\"Y\":9.2555712951713573,\"Z\":0.0},{\"X\":12.965610421927806,\"Y\":3.5538269529982784,\"Z\":0.0},{\"X\":6.4046991240848143,\"Y\":3.5538269529982784,\"Z\":0.0},{\"X\":1.3278034769444158,\"Y\":-4.8816304299427005,\"Z\":0.0}]},{\"discriminator\":\"Elements.Geometry.Polygon\",\"Vertices\":[{\"X\":-9.4508365123690652,\"Y\":0.20473478280229102,\"Z\":0.0},{\"X\":-1.8745460850979974,\"Y\":0.20473478280229102,\"Z\":0.0},{\"X\":-1.8745460850979974,\"Y\":5.4378426037008651,\"Z\":0.0},{\"X\":-9.4508365123690652,\"Y\":5.4378426037008651,\"Z\":0.0}]}]\r\n"; var polygons = JsonConvert.DeserializeObject <List <Polygon> >(polygonjson); var grid = new Grid2d(polygons); foreach (var pt in polygons[1].Vertices) { grid.SplitAtPoint(pt); } grid.CellsFlat.ForEach(c => c.U.DivideByApproximateLength(1.0, EvenDivisionMode.RoundDown)); var trimmedCells = grid.GetCells().Select(c => new Dictionary <string, object> { { "TrimmedGeometry", c.GetTrimmedCellGeometry() }, { "BaseRect", c.GetCellGeometry() }, { "IsTrimmed", c.IsTrimmed() } } ); Assert.Equal(87, trimmedCells.Count()); Assert.Equal(18, trimmedCells.Count(c => (bool)c["IsTrimmed"])); }
private static List <WallPanel> ProcessWallsByProfile(PanelsFromWallsInputs input, List <WallByProfile> allWallsByProfile, out int totalCount, out int uniqueCount, out int nonStandardCount) { var panelsOut = new List <WallPanel>(); Dictionary <string, Color> colorMap = new Dictionary <string, Color>(); uniqueCount = 0; nonStandardCount = 0; var rand = new Random(); int uniqueIDCounter = 0; foreach (var wall in allWallsByProfile) { var centerline = wall.Centerline; var profile = wall.Profile; var clVec = centerline.Direction(); var wallNormal = clVec.Cross(Vector3.ZAxis); var toWall = new Transform(centerline.Start, clVec, wallNormal); var fromWall = new Transform(toWall); fromWall.Invert(); var flatProfile = fromWall.OfProfile(profile); var polygons = new[] { flatProfile.Perimeter }.Union(flatProfile.Voids).Select(p => Make2d(p)).ToList(); var grid = new Grid2d(polygons); grid.U.DivideByFixedLength(input.PanelLength, FixedDivisionMode.RemainderAtEnd); foreach (var cell in grid.GetCells()) { var cellGeometry = cell.GetTrimmedCellGeometry().OfType <Polygon>(); var isTrimmed = cell.IsTrimmed(); if (!isTrimmed) { var polygon = Make2d(cellGeometry.FirstOrDefault()); if (polygon == null) { continue; } var cellProfile = new Profile(polygon); var thicknessTransform = new Transform(0, 0, -wall.Thickness / 2.0); cellProfile = thicknessTransform.OfProfile(cellProfile); var extrude = new Extrude(cellProfile, wall.Thickness, Vector3.ZAxis, false); var identifier = $"{Math.Round(cell.U.Domain.Length, 2)} x {Math.Round(cell.V.Domain.Length, 2)}"; Color color = default(Color); if (colorMap.ContainsKey(identifier)) { color = colorMap[identifier]; } else { color = new Color(rand.NextDouble(), rand.NextDouble(), rand.NextDouble(), 1.0); colorMap.Add(identifier, color); } var material = input.ColorCodeByLength ? new Material(color, 0, 0, false, null, true, Guid.NewGuid(), color.ToString()) : BuiltInMaterials.Concrete; var geomRep = new Representation(new[] { extrude }); var panel = new WallPanel(identifier, cellProfile, true, wall.Thickness, toWall, material, geomRep, false, Guid.NewGuid(), ""); panelsOut.Add(panel); } else { foreach (var polygon in cellGeometry) { var cellProfile = new Profile(polygon); var thicknessTransform = new Transform(0, 0, -wall.Thickness / 2.0); cellProfile = thicknessTransform.OfProfile(cellProfile); var extrude = new Extrude(cellProfile, wall.Thickness, Vector3.ZAxis, false); var identifier = $"C-{uniqueIDCounter++:00}"; var color = new Color(rand.NextDouble(), rand.NextDouble(), rand.NextDouble(), 1.0); colorMap.Add(identifier, color); var material = input.ColorCodeByLength ? new Material(color, 0, 0, false, null, true, Guid.NewGuid(), color.ToString()) : BuiltInMaterials.Concrete; var geomRep = new Representation(new[] { extrude }); var panel = new WallPanel(identifier, cellProfile, true, wall.Thickness, toWall, material, geomRep, false, Guid.NewGuid(), ""); panelsOut.Add(panel); } } } } totalCount = panelsOut.Count; return(panelsOut); }
/// <summary> /// The OpenCollaborationLayout function. /// </summary> /// <param name="model">The input model.</param> /// <param name="input">The arguments to the execution.</param> /// <returns>A OpenCollaborationLayoutOutputs instance containing computed results and the model with any new elements.</returns> public static OpenCollaborationLayoutOutputs Execute(Dictionary <string, Model> inputModels, OpenCollaborationLayoutInputs input) { varietyCounter = 0; var spacePlanningZones = inputModels["Space Planning Zones"]; var levelsModel = inputModels["Levels"]; var hasOpenOffice = inputModels.TryGetValue("Open Office Layout", out var openOfficeModel); var levels = spacePlanningZones.AllElementsOfType <LevelElements>(); var levelVolumes = levelsModel.AllElementsOfType <LevelVolume>(); var output = new OpenCollaborationLayoutOutputs(); var configJson = File.ReadAllText("./OpenCollaborationConfigurations.json"); var configs = JsonConvert.DeserializeObject <SpaceConfiguration>(configJson); if (hasOpenOffice) { foreach (var sb in openOfficeModel.AllElementsOfType <SpaceBoundary>()) { if (sb.AdditionalProperties.TryGetValue("Parent Level Id", out var lvlId)) { var matchingLevel = levels.FirstOrDefault(l => l.Id.ToString() == lvlId as string); matchingLevel?.Elements.Add(sb); } } } foreach (var lvl in levels) { var corridors = lvl.Elements.OfType <Floor>(); var corridorSegments = corridors.SelectMany(p => p.Profile.Segments()); var meetingRmBoundaries = lvl.Elements.OfType <SpaceBoundary>().Where(z => z.Name == "Open Collaboration"); var levelVolume = levelVolumes.First(l => l.Name == lvl.Name); foreach (var room in meetingRmBoundaries) { var spaceBoundary = room.Boundary; Line orientationGuideEdge = FindEdgeAdjacentToSegments(spaceBoundary.Perimeter.Segments(), corridorSegments, out var wallCandidates); var orientationTransform = new Transform(Vector3.Origin, orientationGuideEdge.Direction(), Vector3.ZAxis); var boundaryCurves = new List <Polygon>(); boundaryCurves.Add(spaceBoundary.Perimeter); boundaryCurves.AddRange(spaceBoundary.Voids ?? new List <Polygon>()); var grid = new Grid2d(boundaryCurves, orientationTransform); foreach (var cell in grid.GetCells()) { var rect = cell.GetCellGeometry() as Polygon; var segs = rect.Segments(); var width = segs[0].Length(); var depth = segs[1].Length(); var trimmedGeo = cell.GetTrimmedCellGeometry(); if (!cell.IsTrimmed() && trimmedGeo.Count() > 0) { output.Model.AddElement(InstantiateLayout(configs, width, depth, rect, room.Transform)); } else if (trimmedGeo.Count() > 0) { var largestTrimmedShape = trimmedGeo.OfType <Polygon>().OrderBy(s => s.Area()).Last(); var cinchedVertices = rect.Vertices.Select(v => largestTrimmedShape.Vertices.OrderBy(v2 => v2.DistanceTo(v)).First()).ToList(); var cinchedPoly = new Polygon(cinchedVertices); // output.Model.AddElement(new ModelCurve(cinchedPoly, BuiltInMaterials.ZAxis, levelVolume.Transform)); try { output.Model.AddElement(InstantiateLayout(configs, width, depth, cinchedPoly, room.Transform)); } catch (Exception e) { Console.WriteLine("Failed to instantiate config"); } Console.WriteLine("ЪциРђЇРЎѓ№ИЈ funny shape!!!"); } } } } InstancePositionOverrides(input.Overrides, output.Model); return(output); }
/// <summary> /// The Structure function. /// </summary> /// <param name="model">The model. /// Add elements to the model to have them persisted.</param> /// <param name="input">The arguments to the execution.</param> /// <returns>A StructureOutputs instance containing computed results.</returns> public static StructureOutputs Execute(Dictionary <string, Model> models, StructureInputs input) { var model = new Model(); var warnings = new List <string>(); Elements.Validators.Validator.DisableValidationOnConstruction = true; CellComplex cellComplex = null; Line longestEdge = null; #if DEBUG var sw = new Stopwatch(); sw.Start(); #endif if (models.ContainsKey(BAYS_MODEL_NAME)) { var cellsModel = models[BAYS_MODEL_NAME]; cellComplex = cellsModel.AllElementsOfType <CellComplex>().First(); } else { warnings.Add("Adding the Bays function to your workflow will give you more configurability. We'll use the default configuration for now."); // Create a cell complex with some defaults. if (!models.ContainsKey(LEVELS_MODEL_NAME)) { throw new Exception("If Bays are not supplied Levels are required."); } var levels = models[LEVELS_MODEL_NAME]; var levelVolumes = levels.AllElementsOfType <LevelVolume>().ToList(); if (levelVolumes.Count == 0) { throw new Exception("No LevelVolumes found in your Levels model. Please use a level function that generates LevelVolumes, such as Simple Levels by Envelope"); } // Replicate the old behavior by creating a // grid using the envelope's first level base polygon's longest // edge as the U axis and its perpendicular as the // V axis. var firstLevel = levelVolumes[0]; var firstLevelPerimeter = firstLevel.Profile.Perimeter; longestEdge = firstLevelPerimeter.Segments().OrderBy(s => s.Length()).Last(); var longestEdgeTransform = longestEdge.TransformAt(0.5); var t = new Transform(longestEdge.Start, longestEdgeTransform.XAxis, longestEdge.Direction(), Vector3.ZAxis); var toWorld = new Transform(t); toWorld.Invert(); var bbox = new BBox3(firstLevelPerimeter.Vertices.Select(o => toWorld.OfVector(o)).ToList()); var l = bbox.Max.Y - bbox.Min.Y; var w = bbox.Max.X - bbox.Min.X; var origin = t.OfVector(bbox.Min); var uGrid = new Grid1d(new Line(origin, origin + t.YAxis * l)); uGrid.DivideByFixedLength(DEFAULT_U); var vGrid = new Grid1d(new Line(origin, origin + t.XAxis * w)); vGrid.DivideByFixedLength(DEFAULT_V); var grid = new Grid2d(uGrid, vGrid); var u = grid.U; var v = grid.V; cellComplex = new CellComplex(Guid.NewGuid(), "Temporary Cell Complex"); // Draw level volumes from each level down. for (var i = 1; i < levelVolumes.Count; i++) { var levelVolume = levelVolumes.ElementAt(i); var perimeter = levelVolume.Profile.Perimeter.Offset(-0.5)[0]; var g2d = new Grid2d(perimeter, grid.U, grid.V); var levelElevation = levelVolume.Transform.Origin.Z; var lastLevelVolume = levelVolumes.ElementAt(i - 1); foreach (var cell in g2d.GetCells()) { foreach (var crv in cell.GetTrimmedCellGeometry()) { cellComplex.AddCell((Polygon)crv, lastLevelVolume.Height, levelElevation - lastLevelVolume.Height, g2d.U, g2d.V); if (i == levelVolumes.Count - 1) { cellComplex.AddCell((Polygon)crv, levelVolume.Height, levelElevation, g2d.U, g2d.V); } } } } } #if DEBUG Console.WriteLine($"{sw.ElapsedMilliseconds} ms for getting or creating a cell complex."); sw.Restart(); #endif Vector3 primaryDirection; Vector3 secondaryDirection; IEnumerable <GridLine> gridLines = null; if (models.ContainsKey(GRIDS_MODEL_NAME)) { var gridsModel = models[GRIDS_MODEL_NAME]; gridLines = gridsModel.AllElementsOfType <GridLine>(); // Group by direction. var gridGroups = gridLines.GroupBy(gl => gl.Curve.TransformAt(0).ZAxis).ToList(); primaryDirection = gridGroups[0].Key; secondaryDirection = gridGroups[1].Key; } else { warnings.Add("Adding the Grids function to your workflow will enable you to position and orient the grid. We'll use the default configuration for now with the grid oriented along the longest edge of the structure."); // Define the primary direction from the longest edge of the site. primaryDirection = longestEdge.Direction(); secondaryDirection = longestEdge.TransformAt(0.5).XAxis; } #if DEBUG Console.WriteLine($"{sw.ElapsedMilliseconds} ms for getting or creating grids."); sw.Restart(); #endif var structureMaterial = new Material("Steel", Colors.Gray, 0.5, 0.3); model.AddElement(structureMaterial, false); model.AddElement(BuiltInMaterials.ZAxis, false); var wideFlangeFactory = new WideFlangeProfileFactory(); var shsProfileFactory = new SHSProfileFactory(); var rhsProfileFactory = new RHSProfileFactory(); var columnTypeName = input.ColumnType.ToString(); var columnProfile = GetProfileFromName(columnTypeName, wideFlangeFactory, rhsProfileFactory, shsProfileFactory); var colProfileBounds = columnProfile.Perimeter.Bounds(); var colProfileDepth = colProfileBounds.Max.Y - colProfileBounds.Min.Y; var girderTypeName = input.GirderType.ToString(); Profile girderProfile = null; double girderProfileDepth = 0; if (girderTypeName.StartsWith("LH")) { girderProfileDepth = Units.InchesToMeters(double.Parse(girderTypeName.Split("LH")[1])); } else { girderProfile = GetProfileFromName(girderTypeName, wideFlangeFactory, rhsProfileFactory, shsProfileFactory); var girdProfileBounds = girderProfile.Perimeter.Bounds(); girderProfileDepth = girdProfileBounds.Max.Y - girdProfileBounds.Min.Y; // Set the profile down by half its depth so that // it sits under the slab. girderProfile.Transform(new Transform(new Vector3(0, -girderProfileDepth / 2 - input.SlabThickness))); } Profile beamProfile = null; double beamProfileDepth = 0; var beamTypeName = input.BeamType.ToString(); if (beamTypeName.StartsWith("LH")) { beamProfileDepth = Units.InchesToMeters(double.Parse(beamTypeName.Split("LH")[1])); } else { beamProfile = GetProfileFromName(beamTypeName, wideFlangeFactory, rhsProfileFactory, shsProfileFactory); var beamProfileBounds = beamProfile.Perimeter.Bounds(); beamProfileDepth = beamProfileBounds.Max.Y - beamProfileBounds.Min.Y; // Set the profile down by half its depth so that // it sits under the slab. beamProfile.Transform(new Transform(new Vector3(0, -beamProfileDepth / 2 - input.SlabThickness))); } var edges = cellComplex.GetEdges(); var lowestTierSet = false; var lowestTierElevation = double.MaxValue; var columnDefintions = new Dictionary <(double memberLength, Profile memberProfile), Column>(); var girderDefinitions = new Dictionary <(double memberLength, Profile memberProfile), GeometricElement>(); var beamDefinitions = new Dictionary <(double memberLength, Profile memberProfile), GeometricElement>(); var girderJoistDefinitions = new Dictionary <(double memberLength, double depth), GeometricElement>(); var beamJoistDefinitions = new Dictionary <(double memberLength, double depth), GeometricElement>(); LProfileFactory lProfileFactory; LProfile L8 = null; LProfile L5 = null; LProfile L2 = null; LProfile L3 = null; if (girderProfile == null || beamProfile == null) { lProfileFactory = new LProfileFactory(); L8 = Task.Run(async() => await lProfileFactory.GetProfileByTypeAsync(LProfileType.L8X8X1_2)).Result; L5 = Task.Run(async() => await lProfileFactory.GetProfileByTypeAsync(LProfileType.L5X5X1_2)).Result; L2 = Task.Run(async() => await lProfileFactory.GetProfileByTypeAsync(LProfileType.L2X2X1_8)).Result; L3 = Task.Run(async() => await lProfileFactory.GetProfileByTypeAsync(LProfileType.L3X2X3_16)).Result; } #if DEBUG Console.WriteLine($"{sw.ElapsedMilliseconds} ms for getting all beam and column profiles."); sw.Restart(); #endif var xy = new Plane(Vector3.Origin, Vector3.ZAxis); // Order edges from lowest to highest. foreach (Elements.Spatial.CellComplex.Edge edge in edges.OrderBy(e => Math.Min(cellComplex.GetVertex(e.StartVertexId).Value.Z, cellComplex.GetVertex(e.EndVertexId).Value.Z) )) { var memberLength = edge.Length(cellComplex); var start = cellComplex.GetVertex(edge.StartVertexId).Value; var end = cellComplex.GetVertex(edge.EndVertexId).Value; var direction = (end - start).Unitized(); var warningRepresentation = new Representation(new List <SolidOperation>() { new Extrude(Polygon.Rectangle(0.01, 0.01), 0.01, Vector3.ZAxis, false) }); if (edge.IsVertical(cellComplex)) { // For vertical edges that are not on the grid, we need // a heuristic to determine when we should place a column. // You don't want to place a column all the time because // for non-grid-aligned structures, when you place columns // at every intersection of the envelope and the grid, you // can get columns that are too close together. Instead, we // place a column based on the distance from that column along // a grid line back to a primary grid intersection. If that // distance exceeds the maximum allowable neighbor span, // we place a column. if (!edge.StartsOnGrid(cellComplex)) { var maxDistance = double.MinValue; foreach (var e in edge.GetCells().SelectMany(c => c.GetEdges().Where(e => e != edge && e.StartsOrEndsOnGrid(cellComplex) && e.StartsOrEndsAtThisVertex(edge.StartVertexId, cellComplex) && e.IsHorizontal(cellComplex)))) { var d = e.Length(cellComplex); maxDistance = Math.Max(maxDistance, d); } if (maxDistance < input.MaximumNeighborSpan) { continue; } } var origin = start.IsLowerThan(end) ? start : end; var rotation = Vector3.XAxis.PlaneAngleTo(primaryDirection); Column columnDefinition; if (!columnDefintions.ContainsKey((memberLength, columnProfile))) { columnDefinition = new Column(Vector3.Origin, memberLength, columnProfile, structureMaterial, name: columnProfile.Name) { IsElementDefinition = true }; columnDefinition.Representation.SkipCSGUnion = true; columnDefintions.Add((memberLength, columnProfile), columnDefinition); model.AddElement(columnDefinition, false); } else { columnDefinition = columnDefintions[(memberLength, columnProfile)];
/// <summary> /// Subdivides an incoming Floor according to x- and y-axis distances and grid rotation. /// </summary> /// <param name="model">The input model.</param> /// <param name="input">The arguments to the execution.</param> /// <returns>A SubdivideSlabOutputs instance containing computed results and the model with any new elements.</returns> public static SubdivideSlabOutputs Execute(Dictionary <string, Model> inputModels, SubdivideSlabInputs input) { var allFloors = new List <Floor>(); inputModels.TryGetValue("Floors", out var flrModel); if (flrModel == null || flrModel.AllElementsOfType <Floor>().Count() == 0) { throw new ArgumentException("No Floors found."); } allFloors.AddRange(flrModel.AllElementsOfType <Floor>()); List <SlabSubdivision> subdivisions = new List <SlabSubdivision>(); for (int i = 0; i < allFloors.Count; i++) { Floor floor = allFloors[i]; var floorId = StringExtensions.NumberToString(i); var profile = floor.Profile; var perimeter = profile.Perimeter; var voids = profile.Voids; var openings = floor.Openings.Select(o => o.Profile); var elevation = floor.Elevation; var boundaries = new List <Polygon>(); boundaries.Add(perimeter); if (voids != null) { boundaries.AddRange(voids); } if (openings != null) { boundaries.AddRange(openings.Select(o => o.Perimeter)); } Transform transform = null; if (input.AlignToLongestEdge) { var longestEdge = perimeter.Segments().OrderByDescending(p => p.Length()).First(); var xAxis = (longestEdge.End - longestEdge.Start).Unitized(); if (perimeter.IsClockWise()) { xAxis = xAxis * -1; } transform = new Transform(Vector3.Origin, xAxis, Vector3.ZAxis, 0); // if (perimeter.IsClockWise()) // { // transform.Invert(); // } } var grid = new Grid2d(boundaries, transform); if (input.SubdivideAtVoidCorners && voids != null && voids.Count > 0) { foreach (var voidCrv in voids) { grid.SplitAtPoints(voidCrv.Vertices); } foreach (var cell in grid.CellsFlat) { cell.U.DivideByApproximateLength(input.Length, EvenDivisionMode.RoundUp); cell.V.DivideByApproximateLength(input.Width, EvenDivisionMode.RoundUp); } } else { grid.U.DivideByApproximateLength(input.Length, EvenDivisionMode.RoundUp); grid.V.DivideByApproximateLength(input.Width, EvenDivisionMode.RoundUp); } var cells = grid.GetCells(); for (int i1 = 0; i1 < cells.Count; i1++) { var id = $"{floorId}-{i1:000}"; Grid2d cell = cells[i1]; var cellCrvs = cell.GetTrimmedCellGeometry(); var isTrimmed = cell.IsTrimmed(); if (cellCrvs != null && cellCrvs.Length > 0) { subdivisions.Add(CreateSlabSubdivision(id, cellCrvs, floor, isTrimmed)); } } } var output = new SubdivideSlabOutputs(subdivisions.Count); output.Model.AddElements(subdivisions); foreach (var subdiv in subdivisions) { var thicknessXform = new Transform(0, 0, subdiv.Depth); var profile = subdiv.Transform.OfProfile(subdiv.Profile); profile = thicknessXform.OfProfile(profile); output.Model.AddElement(new ModelCurve(profile.Perimeter)); if (profile.Voids != null && profile.Voids.Count > 0) { output.Model.AddElements(profile.Voids.Select(v => new ModelCurve(v))); } } return(output); }
/// <summary> /// The SpacePlanningZones function. /// </summary> /// <param name="model">The input model.</param> /// <param name="input">The arguments to the execution.</param> /// <returns>A SpacePlanningZonesOutputs instance containing computed results and the model with any new elements.</returns> public static SpacePlanningZonesOutputs Execute(Dictionary <string, Model> inputModels, SpacePlanningZonesInputs input) { var corridorWidth = input.CorridorWidth; var corridorMat = SpaceBoundary.MaterialDict["Circulation"]; var output = new SpacePlanningZonesOutputs(); var levelsModel = inputModels["Levels"]; var levelVolumes = levelsModel.AllElementsOfType <LevelVolume>(); inputModels.TryGetValue("Floors", out var floorsModel); var hasCore = inputModels.TryGetValue("Core", out var coresModel); var cores = coresModel?.AllElementsOfType <ServiceCore>() ?? new List <ServiceCore>(); var random = new Random(5); var levels = new List <LevelElements>(); var levelMappings = new Dictionary <Guid, (SpaceBoundary boundary, LevelElements level)>(); if (levelVolumes.Count() == 0) { throw new Exception("This function requires LevelVolumes, produced by functions like \"Simple Levels by Envelope\". Try using a different levels function."); } foreach (var lvl in levelVolumes) { if (floorsModel != null) { var floorAtLevel = floorsModel.AllElementsOfType <Floor>().FirstOrDefault(f => Math.Abs(lvl.Transform.Origin.Z - f.Transform.Origin.Z) < (f.Thickness * 1.1)); if (floorAtLevel != null) { lvl.Height -= floorAtLevel.Thickness; var floorFaceOffset = (floorAtLevel.Transform.Origin.Z + floorAtLevel.Thickness) - lvl.Transform.Origin.Z; if (floorFaceOffset > 0.001) { lvl.Transform.Concatenate(new Transform(0, 0, floorFaceOffset)); lvl.Height -= floorFaceOffset; } } } var levelBoundary = new Profile(lvl.Profile.Perimeter, lvl.Profile.Voids, Guid.NewGuid(), null); var coresInBoundary = cores.Where(c => levelBoundary.Contains(c.Centroid)).ToList(); foreach (var core in coresInBoundary) { levelBoundary.Voids.Add(new Polygon(core.Profile.Perimeter.Vertices).Reversed()); levelBoundary.OrientVoids(); } var spaceBoundaries = new List <Element>(); List <Profile> corridorProfiles = new List <Profile>(); if (input.CirculationMode == SpacePlanningZonesInputsCirculationMode.Automatic) { var perimeter = levelBoundary.Perimeter; var perimeterSegments = perimeter.Segments(); IdentifyShortEdges(perimeter, perimeterSegments, out var shortEdges, out var shortEdgeIndices); // Single Loaded Zones var singleLoadedZones = CalculateSingleLoadedZones(input, corridorWidth, perimeterSegments, shortEdgeIndices); GenerateEndZones(input, corridorWidth, lvl, corridorProfiles, perimeterSegments, shortEdges, singleLoadedZones, out var thickenedEnds, out var thickerOffsetProfiles, out var innerOffsetMinusThickenedEnds, out var exclusionRegions); // join single loaded zones to each other (useful in bent-bar case) var allCenterLines = JoinSingleLoaded(singleLoadedZones); // thicken and extend single loaded ThickenAndExtendSingleLoaded(corridorWidth, corridorProfiles, coresInBoundary, thickenedEnds, innerOffsetMinusThickenedEnds, allCenterLines); CorridorsFromCore(corridorWidth, corridorProfiles, levelBoundary, coresInBoundary, innerOffsetMinusThickenedEnds, exclusionRegions); SplitCornersAndGenerateSpaceBoundaries(spaceBoundaries, input, lvl, corridorProfiles, levelBoundary, thickerOffsetProfiles); } else if (input.CirculationMode == SpacePlanningZonesInputsCirculationMode.Manual) { if (input.Corridors != null && input.Corridors.Count > 0) { var corridorProfilesForUnion = new List <Profile>(); foreach (var corridorPolyline in input.Corridors) { if (corridorPolyline == null || corridorPolyline.Polyline == null) { continue; } var corrPgons = corridorPolyline.Polyline.OffsetOnSide(corridorPolyline.Width, corridorPolyline.Flip); corridorProfilesForUnion.AddRange(corrPgons.Select(p => new Profile(p))); } corridorProfiles = Profile.UnionAll(corridorProfilesForUnion); } SplitCornersAndGenerateSpaceBoundaries(spaceBoundaries, input, lvl, corridorProfiles, levelBoundary); } // Construct Level var level = new LevelElements(new List <Element>(), Guid.NewGuid(), lvl.Name); levels.Add(level); // Manual Corridor Splits foreach (var pt in input.AdditionalCorridorLocations) { SplitZones(input, corridorWidth, lvl, spaceBoundaries, corridorProfiles, pt); } // Manual Split Locations foreach (var pt in input.ManualSplitLocations) { SplitZones(input, corridorWidth, lvl, spaceBoundaries, corridorProfiles, pt, false); } foreach (SpaceBoundary b in spaceBoundaries) { levelMappings.Add(b.Id, (b, level)); } corridorProfiles.Select(p => new Floor(p, 0.1, lvl.Transform, corridorMat)).ToList().ForEach(f => level.Elements.Add(f)); } List <SpaceBoundary> SubdividedBoundaries = new List <SpaceBoundary>(); // merge overrides if (input.Overrides != null && input.Overrides.MergeZones != null && input.Overrides.MergeZones.Count > 0) { var spaceBoundaries = levelMappings.Select(kvp => kvp.Value); foreach (var mz in input.Overrides.MergeZones) { var identitiesToMerge = mz.Identities; var matchingSbs = identitiesToMerge.Select(mzI => spaceBoundaries.FirstOrDefault( sb => ((Vector3)sb.boundary.AdditionalProperties["ParentCentroid"]).DistanceTo(mzI.ParentCentroid) < 1.0)).Where(s => s != (null, null)).ToList(); foreach (var msb in matchingSbs) { levelMappings.Remove(msb.boundary.Id); } var sbsByLevel = matchingSbs.GroupBy(sb => sb.level?.Id ?? Guid.Empty); foreach (var lvlGrp in sbsByLevel) { var level = lvlGrp.First().level; var profiles = lvlGrp.Select(sb => sb.boundary.Boundary); var baseobj = lvlGrp.FirstOrDefault(n => n.boundary.Name != null && n.boundary.Name != "unspecified"); if (baseobj == default) { baseobj = lvlGrp.First(); } var baseSB = baseobj.boundary; var union = Profile.UnionAll(profiles); foreach (var newProfile in union) { var rep = baseSB.Representation.SolidOperations.OfType <Extrude>().First(); var newSB = SpaceBoundary.Make(newProfile, baseSB.Name, baseSB.Transform, rep.Height, (Vector3)baseSB.AdditionalProperties["ParentCentroid"], (Vector3)baseSB.AdditionalProperties["ParentCentroid"]); newSB.SetProgram(baseSB.Name); levelMappings.Add(newSB.Id, (newSB, level)); } } } } // assignment overrides if (input.Overrides != null && input.Overrides.ProgramAssignments != null && input.Overrides.ProgramAssignments.Count > 0) { var spaceBoundaries = levelMappings.Select(kvp => kvp.Value).ToList(); // overrides where it is its own parent Console.WriteLine(JsonConvert.SerializeObject(input.Overrides.ProgramAssignments)); foreach (var overrideValue in input.Overrides.ProgramAssignments.Where(o => o.Identity.IndividualCentroid.IsAlmostEqualTo(o.Identity.ParentCentroid))) { var centroid = overrideValue.Identity.ParentCentroid; var matchingSB = spaceBoundaries .OrderBy(sb => ((Vector3)sb.boundary.AdditionalProperties["IndividualCentroid"]).DistanceTo(centroid)) .FirstOrDefault(sb => ((Vector3)sb.boundary.AdditionalProperties["IndividualCentroid"]).DistanceTo(centroid) < 2.0); // var allMatchingSBs = spaceBoundaries // .OrderBy(sb => sb.boundary.Transform.OfPoint(sb.boundary.Boundary.Perimeter.Centroid()).DistanceTo(centroid)) // .Where(sb => sb.boundary.Transform.OfPoint(sb.boundary.Boundary.Perimeter.Centroid()).DistanceTo(centroid) < 2.0); if (matchingSB.boundary != null) { if (overrideValue.Value.Split <= 1) { matchingSB.boundary.SetProgram(overrideValue.Value.ProgramType ?? input.DefaultProgramAssignment); Identity.AddOverrideIdentity(matchingSB.boundary, "Program Assignments", overrideValue.Id, overrideValue.Identity); } else { levelMappings.Remove(matchingSB.boundary.Id); var boundaries = new List <Polygon>(matchingSB.boundary.Boundary.Voids) { matchingSB.boundary.Boundary.Perimeter }; var guideVector = GetDominantAxis(boundaries.SelectMany(b => b.Segments())); var alignmentXform = new Transform(boundaries[0].Start, guideVector, Vector3.ZAxis); var grid = new Grid2d(boundaries, alignmentXform); grid.U.DivideByCount(Math.Max(overrideValue.Value.Split, 1)); foreach (var cell in grid.GetCells().SelectMany(c => c.GetTrimmedCellGeometry())) { var rep = matchingSB.boundary.Representation.SolidOperations.OfType <Extrude>().First(); var newCellSb = SpaceBoundary.Make(cell as Polygon, overrideValue.Value.ProgramType ?? input.DefaultProgramAssignment, matchingSB.boundary.Transform, rep.Height, matchingSB.boundary.AdditionalProperties["ParentCentroid"] as Vector3?); Identity.AddOverrideIdentity(newCellSb, "Program Assignments", overrideValue.Id, overrideValue.Identity); newCellSb.AdditionalProperties["Split"] = overrideValue.Value.Split; SubdividedBoundaries.Add(newCellSb); levelMappings.Add(newCellSb.Id, (newCellSb, matchingSB.level)); } } } } // overrides where it's not its own parent foreach (var overrideValue in input.Overrides.ProgramAssignments.Where(o => !o.Identity.IndividualCentroid.IsAlmostEqualTo(o.Identity.ParentCentroid))) { var matchingCell = SubdividedBoundaries.FirstOrDefault(b => (b.AdditionalProperties["IndividualCentroid"] as Vector3?)?.DistanceTo(overrideValue.Identity.IndividualCentroid) < 0.01); if (matchingCell != null) { Identity.AddOverrideIdentity(matchingCell, "Program Assignments", overrideValue.Id, overrideValue.Identity); matchingCell.SetProgram(overrideValue.Value.ProgramType); } } } foreach (var levelMapping in levelMappings) { levelMapping.Value.level.Elements.Add(levelMapping.Value.boundary); } Dictionary <string, AreaTally> areas = new Dictionary <string, AreaTally>(); foreach (var sb in levels.SelectMany(lev => lev.Elements.OfType <SpaceBoundary>())) { var area = sb.Boundary.Area(); if (sb.Name == null) { continue; } if (!areas.ContainsKey(sb.Name)) { areas[sb.Name] = new AreaTally(sb.Name, sb.Material.Color, area, area, 1, null, Guid.NewGuid(), sb.Name); } else { var existingTally = areas[sb.Name]; existingTally.AchievedArea += area; existingTally.AreaTarget += area; existingTally.DistinctAreaCount++; } output.Model.AddElements(sb.Boundary.ToModelCurves(sb.Transform.Concatenated(new Transform(0, 0, 0.03)))); } output.Model.AddElements(areas.Select(kvp => kvp.Value).OrderByDescending(a => a.AchievedArea)); output.Model.AddElements(levels); return(output); }
/// <summary> /// The SpacePlanningZones function. /// </summary> /// <param name="model">The input model.</param> /// <param name="input">The arguments to the execution.</param> /// <returns>A SpacePlanningZonesOutputs instance containing computed results and the model with any new elements.</returns> public static SpacePlanningZonesOutputs Execute(Dictionary <string, Model> inputModels, SpacePlanningZonesInputs input) { var corridorWidth = input.CorridorWidth; var corridorMat = SpaceBoundary.MaterialDict["Circulation"]; var output = new SpacePlanningZonesOutputs(); var levelsModel = inputModels["Levels"]; var levelVolumes = levelsModel.AllElementsOfType <LevelVolume>(); inputModels.TryGetValue("Floors", out var floorsModel); var coresModel = inputModels["Core"]; var cores = coresModel.AllElementsOfType <ServiceCore>(); var random = new Random(5); var levels = new List <LevelElements>(); var levelMappings = new Dictionary <Guid, (SpaceBoundary boundary, LevelElements level)>(); if (levelVolumes.Count() == 0) { throw new Exception("This function requires LevelVolumes, produced by functions like \"Simple Levels by Envelope\". Try using a different levels function."); } if (cores.Count() == 0) { throw new Exception("No ServiceCore elements were found in the model."); } foreach (var lvl in levelVolumes) { var spaceBoundaries = new List <Element>(); if (floorsModel != null) { var floorAtLevel = floorsModel.AllElementsOfType <Floor>().FirstOrDefault(f => Math.Abs(lvl.Transform.Origin.Z - f.Transform.Origin.Z) < (f.Thickness * 1.1)); if (floorAtLevel != null) { lvl.Height -= floorAtLevel.Thickness; var floorFaceOffset = (floorAtLevel.Transform.Origin.Z + floorAtLevel.Thickness) - lvl.Transform.Origin.Z; if (floorFaceOffset > 0.001) { lvl.Transform.Concatenate(new Transform(0, 0, floorFaceOffset)); lvl.Height -= floorFaceOffset; } } } List <Profile> corridorProfiles = new List <Profile>(); var TOO_SHORT = 9; var levelBoundary = new Profile(lvl.Profile.Perimeter, lvl.Profile.Voids, Guid.NewGuid(), null); var coresInBoundary = cores.Where(c => levelBoundary.Contains(c.Centroid)).ToList(); foreach (var core in coresInBoundary) { levelBoundary.Voids.Add(new Polygon(core.Profile.Perimeter.Vertices).Reversed()); levelBoundary.OrientVoids(); } var perimeter = levelBoundary.Perimeter; var perimeterSegments = perimeter.Segments(); var perimeterAngles = new List <double>(); for (int i = 0; i < perimeter.Vertices.Count; i++) { var nextIndex = (i + 1) % perimeter.Vertices.Count; var prevIndex = (i + perimeter.Vertices.Count - 1) % perimeter.Vertices.Count; var prevVec = perimeter.Vertices[i] - perimeter.Vertices[prevIndex]; var nextVec = perimeter.Vertices[nextIndex] - perimeter.Vertices[i]; var angle = prevVec.PlaneAngleTo(nextVec); perimeterAngles.Add(angle); } var allLengths = perimeterSegments.Select(s => s.Length()); var validLengths = allLengths.Where(l => l > TOO_SHORT)?.OrderBy(l => l); var shortLength = (validLengths?.FirstOrDefault() ?? 35 / 1.2) * 1.2; var longLength = Math.Min(validLengths.SkipLast(1).Last(), 50); var shortEdges = new List <Line>(); var shortEdgeIndices = new List <int>(); for (int i = 0; i < perimeterSegments.Count(); i++) { var start = perimeterAngles[i]; var end = perimeterAngles[(i + 1) % perimeterAngles.Count]; if (start > 80 && start < 100 && end > 80 && end < 100 && perimeterSegments[i].Length() < longLength) { shortEdges.Add(perimeterSegments[i]); shortEdgeIndices.Add(i); } } // Single Loaded Zones var singleLoadedZones = new List <(Polygon hull, Line centerLine)>(); var singleLoadedLengthThreshold = input.OuterBandDepth * 2 + corridorWidth * 2 + 5; // (two offsets, two corridors, and a usable space width) foreach (var sei in shortEdgeIndices) { var ps = perimeterSegments; if (ps[sei].Length() < singleLoadedLengthThreshold) { var legSegments = new[] { ps[(sei + ps.Length - 1) % ps.Length], ps[sei], ps[(sei + 1) % ps.Length] }; var legLength = Math.Min(legSegments[0].Length(), legSegments[2].Length()); legSegments[0] = new Line(ps[sei].Start, ps[sei].Start + legLength * (legSegments[0].Direction() * -1)); legSegments[2] = new Line(ps[sei].End, ps[sei].End + legLength * (legSegments[2].Direction())); var hull = ConvexHull.FromPolylines(legSegments.Select(l => l.ToPolyline(1))); var centerLine = new Line((legSegments[0].Start + legSegments[2].Start) / 2, (legSegments[0].End + legSegments[2].End) / 2); singleLoadedZones.Add((hull, centerLine)); } } var shortEdgesExtended = shortEdges.Select(l => new Line(l.Start - l.Direction() * 0.2, l.End + l.Direction() * 0.2)); var longEdges = perimeterSegments.Except(shortEdges); var shortEdgeDepth = Math.Max(input.DepthAtEnds, input.OuterBandDepth); var longEdgeDepth = input.OuterBandDepth; var perimeterMinusSingleLoaded = new List <Profile>(); perimeterMinusSingleLoaded.AddRange(Profile.Difference(new[] { lvl.Profile }, singleLoadedZones.Select(p => new Profile(p.hull)))); var innerOffset = perimeterMinusSingleLoaded.SelectMany(p => p.Perimeter.Offset(-longEdgeDepth)); var thickerOffsets = shortEdgesExtended.SelectMany(s => s.ToPolyline(1).Offset(shortEdgeDepth, EndType.Butt)).ToList(); var thickerOffsetProfiles = thickerOffsets.Select(o => new Profile(o.Offset(0.01))).ToList(); var endOffsetSegments = thickerOffsets.SelectMany(o => o.Segments()).Where(l => innerOffset.Any(o => o.Contains(l.PointAt(0.5)))); var innerOffsetMinusThicker = innerOffset.SelectMany(i => Polygon.Difference(new[] { i }, thickerOffsets)); var outerband = new Profile(lvl.Profile.Perimeter, innerOffsetMinusThicker.ToList(), Guid.NewGuid(), null); var outerbandLongEdges = Profile.Difference(new List <Profile> { outerband }, thickerOffsetProfiles); var ends = Profile.Intersection(new List <Profile> { outerband }, thickerOffsets.Select(o => new Profile(o)).ToList()); var coreSegments = coresInBoundary.SelectMany(c => c.Profile.Perimeter.Offset((corridorWidth / 2) * 0.999).FirstOrDefault()?.Segments()); var corridorInset = innerOffsetMinusThicker.Select(p => new Profile(p, p.Offset(-corridorWidth), Guid.NewGuid(), "Corridor")); corridorProfiles.AddRange(corridorInset); // join single loaded zones to each other (useful in bent-bar case) var allCenterLines = singleLoadedZones.ToArray(); var distanceThreshold = 10.0; for (int i = 0; i < allCenterLines.Count(); i++) { var crvA = allCenterLines[i].centerLine; for (int j = 0; j < i; j++) { var crvB = allCenterLines[j].centerLine; var doesIntersect = crvA.Intersects(crvB, out var intersection, true, true); Console.WriteLine($"DOES INTERSECT: " + doesIntersect.ToString()); var nearPtA = intersection.ClosestPointOn(crvA); var nearPtB = intersection.ClosestPointOn(crvB); if (nearPtA.DistanceTo(intersection) + nearPtB.DistanceTo(intersection) < distanceThreshold) { if (nearPtA.DistanceTo(crvA.Start) < 0.01) { allCenterLines[i] = (allCenterLines[i].hull, new Line(intersection, crvA.End)); } else { allCenterLines[i] = (allCenterLines[i].hull, new Line(crvA.Start, intersection)); } if (nearPtB.DistanceTo(crvB.Start) < 0.01) { allCenterLines[j] = (allCenterLines[j].hull, new Line(intersection, crvB.End)); } else { allCenterLines[j] = (allCenterLines[j].hull, new Line(crvB.Start, intersection)); } } } } // thicken and extend single loaded foreach (var singleLoadedZone in allCenterLines) { var cl = singleLoadedZone.centerLine; List <Line> centerlines = new List <Line> { cl }; foreach (var core in coresInBoundary) { List <Line> linesRunning = new List <Line>(); foreach (var curve in centerlines) { curve.Trim(core.Profile.Perimeter, out var linesTrimmedByCore); linesRunning.AddRange(linesTrimmedByCore); } centerlines = linesRunning; } cl = centerlines.OrderBy(l => l.Length()).Last(); foreach (var clCandidate in centerlines) { var extended = clCandidate.ExtendTo(innerOffsetMinusThicker.SelectMany(p => p.Segments())).ToPolyline(1); if (extended.Length() == cl.Length() && innerOffsetMinusThicker.Count() > 0) { var end = extended.End; var dist = double.MaxValue; Vector3?runningPt = null; foreach (var boundary in innerOffsetMinusThicker) { var closestDist = end.DistanceTo(boundary, out var pt); if (closestDist < dist) { dist = closestDist; runningPt = pt; } } extended = new Polyline(new[] { extended.Start, extended.End, runningPt.Value }); } //TODO - verify that newly constructed line is contained within building perimeter var thickenedCorridor = extended.Offset(corridorWidth / 2.0, EndType.Square); corridorProfiles.AddRange(Profile.Difference(thickenedCorridor.Select(c => new Profile(c)), thickerOffsets.Select(c => new Profile(c)))); } } foreach (var core in coresInBoundary) { if (singleLoadedZones.Any(z => z.hull.Covers(core.Profile.Perimeter))) { continue; } var boundary = core.Profile.Perimeter.Offset(corridorWidth / 2.0).FirstOrDefault(); var outerOffset = boundary.Offset(corridorWidth).FirstOrDefault(); var coreWrap = new Profile(outerOffset, boundary); var coreWrapWithinFloor = Profile.Intersection(new[] { coreWrap }, new[] { levelBoundary }); } var extendedLines = new List <Line>(); var corridorRegions = new List <Polygon>(); var exclusionRegions = innerOffsetMinusThicker.SelectMany(r => r.Offset(2 * corridorWidth, EndType.Square)); foreach (var enclosedRegion in innerOffsetMinusThicker) { foreach (var segment in coreSegments) { enclosedRegion.Contains(segment.Start, out var startContainment); enclosedRegion.Contains(segment.End, out var endContainment); if (endContainment == Containment.Outside && startContainment == Containment.Outside) { continue; } var extendedSegment = segment.ExtendTo(new Profile(enclosedRegion)); if (extendedSegment.Length() - segment.Length() < 2 * 8) { continue; } extendedLines.Add(extendedSegment); var thickenedCorridor = extendedSegment.ToPolyline(1).Offset(corridorWidth / 2.0, EndType.Butt); var difference = new List <Profile>(); difference = Profile.Difference(corridorProfiles, exclusionRegions.Select(r => new Profile(r))); if (difference.Count > 0 && difference.Sum(d => d.Perimeter.Area()) > 10) { corridorProfiles.AddRange(Profile.Intersection(thickenedCorridor.Select(c => new Profile(c)), new[] { levelBoundary })); } } } var remainingSpaces = Profile.Difference(new[] { levelBoundary }, corridorProfiles); foreach (var remainingSpace in remainingSpaces) { try { if (remainingSpace.Perimeter.Vertices.Any(v => v.DistanceTo(levelBoundary.Perimeter) < 0.1)) { var endCapZones = Profile.Intersection(new[] { remainingSpace }, thickerOffsetProfiles); var linearZones = Profile.Difference(new[] { remainingSpace }, thickerOffsetProfiles); foreach (var linearZone in linearZones) { var segmentsExtended = new List <Polyline>(); foreach (var line in linearZone.Segments()) { if (line.Length() < 2) { continue; } var l = new Line(line.Start - line.Direction() * 0.1, line.End + line.Direction() * 0.1); var extended = l.ExtendTo(linearZone); var endDistance = extended.End.DistanceTo(l.End); var startDistance = extended.Start.DistanceTo(l.Start); var maxExtension = Math.Max(input.OuterBandDepth, input.DepthAtEnds) * 1.6; if (startDistance > 0.1 && startDistance < maxExtension) { var startLine = new Line(extended.Start, line.Start); segmentsExtended.Add(startLine.ToPolyline(1)); } if (endDistance > 0.1 && endDistance < maxExtension) { var endLine = new Line(extended.End, line.End); segmentsExtended.Add(endLine.ToPolyline(1)); } } Console.WriteLine(JsonConvert.SerializeObject(linearZone.Perimeter)); Console.WriteLine(JsonConvert.SerializeObject(linearZone.Voids)); Console.WriteLine(JsonConvert.SerializeObject(segmentsExtended)); var splits = Profile.Split(new[] { linearZone }, segmentsExtended, Vector3.EPSILON); spaceBoundaries.AddRange(splits.Select(s => SpaceBoundary.Make(s, input.DefaultProgramAssignment, lvl.Transform, lvl.Height))); } spaceBoundaries.AddRange(endCapZones.Select(s => SpaceBoundary.Make(s, input.DefaultProgramAssignment, lvl.Transform, lvl.Height))); } else { spaceBoundaries.Add(SpaceBoundary.Make(remainingSpace, input.DefaultProgramAssignment, lvl.Transform, lvl.Height)); } } catch (Exception e) { Console.WriteLine("🚨"); Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); spaceBoundaries.Add(SpaceBoundary.Make(remainingSpace, input.DefaultProgramAssignment, lvl.Transform, lvl.Height)); } } var level = new LevelElements(new List <Element>(), Guid.NewGuid(), lvl.Name); levels.Add(level); foreach (var pt in input.AdditionalCorridorLocations) { SplitZones(input, corridorWidth, lvl, spaceBoundaries, corridorProfiles, pt); } foreach (var pt in input.ManualSplitLocations) { SplitZones(input, corridorWidth, lvl, spaceBoundaries, corridorProfiles, pt, false); } foreach (SpaceBoundary b in spaceBoundaries) { levelMappings.Add(b.Id, (b, level)); output.Model.AddElements(b.Boundary.ToModelCurves(b.Transform.Concatenated(new Transform(0, 0, 0.03)))); } corridorProfiles.Select(p => new Floor(p, 0.1, lvl.Transform, corridorMat)).ToList().ForEach(f => level.Elements.Add(f)); } List <SpaceBoundary> SubdividedBoundaries = new List <SpaceBoundary>(); if (input.Overrides != null && input.Overrides.ProgramAssignments.Count > 0) { var spaceBoundaries = levelMappings.Select(kvp => kvp.Value); foreach (var overrideValue in input.Overrides.ProgramAssignments.Where(o => o.Identity.IndividualCentroid.IsAlmostEqualTo(o.Identity.ParentCentroid))) { var centroid = overrideValue.Identity.ParentCentroid; var matchingSB = spaceBoundaries .OrderBy(sb => sb.boundary.Transform.OfPoint(sb.boundary.Boundary.Perimeter.Centroid()).DistanceTo(centroid)) .FirstOrDefault(sb => sb.boundary.Transform.OfPoint(sb.boundary.Boundary.Perimeter.Centroid()).DistanceTo(centroid) < 2.0); var allMatchingSBs = spaceBoundaries .OrderBy(sb => sb.boundary.Transform.OfPoint(sb.boundary.Boundary.Perimeter.Centroid()).DistanceTo(centroid)) .Where(sb => sb.boundary.Transform.OfPoint(sb.boundary.Boundary.Perimeter.Centroid()).DistanceTo(centroid) < 2.0); if (matchingSB.boundary != null) { if (overrideValue.Value.Split <= 1) { matchingSB.boundary.SetProgram(overrideValue.Value.ProgramType ?? input.DefaultProgramAssignment); Identity.AddOverrideIdentity(matchingSB.boundary, "Program Assignments", overrideValue.Id, overrideValue.Identity); } else { levelMappings.Remove(matchingSB.boundary.Id); var boundaries = new List <Polygon>(matchingSB.boundary.Boundary.Voids) { matchingSB.boundary.Boundary.Perimeter }; var guideVector = GetDominantAxis(boundaries.SelectMany(b => b.Segments())); var alignmentXform = new Transform(boundaries[0].Start, guideVector, Vector3.ZAxis); var grid = new Grid2d(boundaries, alignmentXform); grid.U.DivideByCount(Math.Max(overrideValue.Value.Split, 1)); output.Model.AddElements(grid.GetCellSeparators(GridDirection.V, false).Select(c => new ModelCurve(c as Line, new Material("Grey", new Color(0.3, 0.3, 0.3, 1)), matchingSB.boundary.Transform.Concatenated(new Transform(0, 0, 0.02))))); foreach (var cell in grid.GetCells().SelectMany(c => c.GetTrimmedCellGeometry())) { var rep = matchingSB.boundary.Representation.SolidOperations.OfType <Extrude>().First(); var newCellSb = SpaceBoundary.Make(cell as Polygon, overrideValue.Value.ProgramType ?? input.DefaultProgramAssignment, matchingSB.boundary.Transform, rep.Height, matchingSB.boundary.AdditionalProperties["ParentCentroid"] as Vector3?); Identity.AddOverrideIdentity(newCellSb, "Program Assignments", overrideValue.Id, overrideValue.Identity); newCellSb.AdditionalProperties["Split"] = overrideValue.Value.Split; SubdividedBoundaries.Add(newCellSb); levelMappings.Add(newCellSb.Id, (newCellSb, matchingSB.level)); } } } } foreach (var overrideValue in input.Overrides.ProgramAssignments.Where(o => !o.Identity.IndividualCentroid.IsAlmostEqualTo(o.Identity.ParentCentroid))) { var matchingCell = SubdividedBoundaries.FirstOrDefault(b => (b.AdditionalProperties["IndividualCentroid"] as Vector3?)?.DistanceTo(overrideValue.Identity.IndividualCentroid) < 0.01); if (matchingCell != null) { Identity.AddOverrideIdentity(matchingCell, "Program Assignments", overrideValue.Id, overrideValue.Identity); matchingCell.SetProgram(overrideValue.Value.ProgramType); } } } foreach (var levelMapping in levelMappings) { levelMapping.Value.level.Elements.Add(levelMapping.Value.boundary); } Dictionary <string, AreaTally> areas = new Dictionary <string, AreaTally>(); foreach (var sb in levels.SelectMany(lev => lev.Elements.OfType <SpaceBoundary>())) { var area = sb.Boundary.Area(); if (sb.Name == null) { continue; } if (!areas.ContainsKey(sb.Name)) { areas[sb.Name] = new AreaTally(sb.Name, sb.Material.Color, area, area, 1, null, Guid.NewGuid(), sb.Name); } else { var existingTally = areas[sb.Name]; existingTally.AchievedArea += area; existingTally.AreaTarget += area; existingTally.DistinctAreaCount++; } } output.Model.AddElements(areas.Select(kvp => kvp.Value).OrderByDescending(a => a.AchievedArea)); output.Model.AddElements(levels); return(output); }
/// <summary> /// The SubdivideSlabNew function. /// </summary> /// <param name="model">The input model.</param> /// <param name="input">The arguments to the execution.</param> /// <returns>A SubdivideSlabNewOutputs instance containing computed results and the model with any new elements.</returns> public static SubdivideSlabNewOutputs Execute(Dictionary <string, Model> inputModels, SubdivideSlabNewInputs input) { var model = new Model(); var allFloors = new List <Floor>(); inputModels.TryGetValue("Floors", out var flrModel); if (flrModel == null || flrModel.AllElementsOfType <Floor>().Count() == 0) { throw new ArgumentException("No Floors found."); } allFloors.AddRange(flrModel.AllElementsOfType <Floor>()); List <SlabSubdivision> subdivisions = new List <SlabSubdivision>(); List <ModelCurve> modelCurves = new List <ModelCurve>(); List <Column> columns = new List <Column>(); for (int i = 0; i < allFloors.Count; i++) { Floor floor = allFloors[i]; var floorId = StringExtensions.NumberToString(i); var profile = floor.Profile; var perimeter = profile.Perimeter; var voids = profile.Voids; var elevation = floor.Elevation; var boundaries = new List <Polygon>(); boundaries.Add(perimeter); if (voids != null) { boundaries.AddRange(voids); } Transform transform = null; if (input.AlignToLongestEdge) { var longestEdge = perimeter.Segments().OrderByDescending(p => p.Length()).First(); var xAxis = (longestEdge.End - longestEdge.Start).Unitized(); transform = new Transform(Vector3.Origin, xAxis, Vector3.ZAxis, 0); transform.Invert(); } var grid = new Grid2d(boundaries, transform); if (input.SubdivideAtVoidCorners && voids != null && voids.Count > 0) { foreach (var voidCrv in voids) { grid.SplitAtPoints(voidCrv.Vertices); } foreach (var cell in grid.CellsFlat) { cell.U.DivideByApproximateLength(input.Length, EvenDivisionMode.RoundUp); cell.V.DivideByApproximateLength(input.Width, EvenDivisionMode.RoundUp); } } else { grid.U.DivideByApproximateLength(input.Length, EvenDivisionMode.RoundUp); grid.V.DivideByApproximateLength(input.Width, EvenDivisionMode.RoundUp); } var cells = grid.GetCells(); // foreach(var pt in grid.U.GetCellSeparators()) // { // var column = new Column(new Vector3(pt.X, pt.Y, elevation), 5, Polygon.Rectangle(1.0, 1.0)); // columns.Add(column); // } for (int i1 = 0; i1 < cells.Count; i1++) { var id = $"{floorId}-{i1:000}"; Grid2d cell = cells[i1]; var cellCrvs = cell.GetTrimmedCellGeometry(); var isTrimmed = cell.IsTrimmed(); if (cellCrvs != null && cellCrvs.Length > 0) { subdivisions.Add(CreateSlabSubdivision(id, cellCrvs, floor, isTrimmed)); // var column = new Column(pt, 5, Polygon.Rectangle(0.05, 0.05)); // output.Model.AddElement(column); var outerBoundary = cellCrvs.First(); var polygon = (Polygon)outerBoundary; var pt = polygon.Centroid(); var column = new Column(new Vector3(pt.X, pt.Y, elevation), 5, Polygon.Rectangle(1.0, 1.0)); columns.Add(column); } foreach (var crv in cellCrvs) { modelCurves.Add(ToModelCurve(crv, elevation + 1.0)); } } } var output = new SubdivideSlabNewOutputs(subdivisions.Count, columns.Count); output.Model = model; // output.Model.AddElements(subdivisions); output.Model.AddElements(modelCurves); output.Model.AddElements(columns); return(output); }
/// <summary> /// The PhoneBoothLayout function. /// </summary> /// <param name="model">The input model.</param> /// <param name="input">The arguments to the execution.</param> /// <returns>A PhoneBoothLayoutOutputs instance containing computed results and the model with any new elements.</returns> public static PhoneBoothLayoutOutputs Execute(Dictionary <string, Model> inputModels, PhoneBoothLayoutInputs input) { var spacePlanningZones = inputModels["Space Planning Zones"]; var levelsModel = inputModels["Levels"]; var levels = spacePlanningZones.AllElementsOfType <LevelElements>(); var levelVolumes = levelsModel.AllElementsOfType <LevelVolume>(); var output = new PhoneBoothLayoutOutputs(); var configJson = File.ReadAllText("./PhoneBoothConfigurations.json"); var configs = JsonConvert.DeserializeObject <SpaceConfiguration>(configJson); var wallMat = new Material("Drywall", new Color(0.9, 0.9, 0.9, 1.0), 0.01, 0.01); var glassMat = new Material("Glass", new Color(0.7, 0.7, 0.7, 0.3), 0.3, 0.6); var mullionMat = new Material("Storefront Mullions", new Color(0.5, 0.5, 0.5, 1.0)); foreach (var lvl in levels) { var corridors = lvl.Elements.OfType <Floor>(); var corridorSegments = corridors.SelectMany(p => p.Profile.Segments()); var meetingRmBoundaries = lvl.Elements.OfType <SpaceBoundary>().Where(z => z.Name == "Phone Booth"); var levelVolume = levelVolumes.First(l => l.Name == lvl.Name); var wallCandidateLines = new List <(Line line, string type)>(); foreach (var room in meetingRmBoundaries) { var spaceBoundary = room.Boundary; Line orientationGuideEdge = FindEdgeAdjacentToSegments(spaceBoundary.Perimeter.Segments(), corridorSegments, out var wallCandidates); var exteriorWalls = FindEdgeAdjacentToSegments(wallCandidates, levelVolume.Profile.Segments(), out var solidWalls, 0.6); wallCandidateLines.AddRange(solidWalls.Select(s => (s, "Solid"))); var orientationTransform = new Transform(Vector3.Origin, orientationGuideEdge.Direction(), Vector3.ZAxis); var boundaryCurves = new List <Polygon>(); boundaryCurves.Add(spaceBoundary.Perimeter); boundaryCurves.AddRange(spaceBoundary.Voids ?? new List <Polygon>()); var grid = new Grid2d(boundaryCurves, orientationTransform); grid.U.DivideByApproximateLength(input.MinimumSize, EvenDivisionMode.RoundDown); foreach (var cell in grid.GetCells()) { var rect = cell.GetCellGeometry() as Polygon; var segs = rect.Segments(); var width = segs[0].Length(); var depth = segs[1].Length(); Line glassWall = null; var trimmedGeo = cell.GetTrimmedCellGeometry(); if (!cell.IsTrimmed() && trimmedGeo.Count() > 0) { glassWall = segs[0]; output.Model.AddElement(InstantiateLayout(configs, width, depth, rect, room.Transform)); } else if (trimmedGeo.Count() > 0) { var largestTrimmedShape = trimmedGeo.OfType <Polygon>().OrderBy(s => s.Area()).Last(); glassWall = largestTrimmedShape.Segments().OrderBy(s => s.PointAt(0.5).DistanceTo(segs[0].PointAt(0.5))).FirstOrDefault(); var cinchedVertices = rect.Vertices.Select(v => largestTrimmedShape.Vertices.OrderBy(v2 => v2.DistanceTo(v)).First()).ToList(); var cinchedPoly = new Polygon(cinchedVertices); output.Model.AddElement(InstantiateLayout(configs, width, depth, cinchedPoly, room.Transform)); } if (glassWall != null) { wallCandidateLines.Add((glassWall, "Glass")); } } var cellSeparators = grid.GetCellSeparators(GridDirection.V, true); wallCandidateLines.AddRange(grid.GetCellSeparators(GridDirection.V, true).OfType <Curve>().Select(c => (new Line(c.PointAt(0), c.PointAt(1)), "Partition"))); } var mullionSize = 0.07; var doorWidth = 0.9; var doorHeight = 2.1; var sideLightWidth = 0.4; var totalStorefrontHeight = Math.Min(2.7, levelVolume.Height); var mullion = new StandardWall(new Line(new Vector3(-mullionSize / 2, 0, 0), new Vector3(mullionSize / 2, 0, 0)), mullionSize, totalStorefrontHeight, mullionMat); mullion.IsElementDefinition = true; if (input.CreateWalls) { foreach (var wallCandidate in wallCandidateLines) { if (wallCandidate.type == "Solid") { output.Model.AddElement(new StandardWall(wallCandidate.line, 0.2, levelVolume.Height, wallMat, levelVolume.Transform)); } else if (wallCandidate.type == "Partition") { output.Model.AddElement(new StandardWall(wallCandidate.line, 0.1, levelVolume.Height, wallMat, levelVolume.Transform)); } else if (wallCandidate.type == "Glass") { var grid = new Grid1d(wallCandidate.line); grid.SplitAtOffsets(new[] { sideLightWidth, sideLightWidth + doorWidth }); grid[2].DivideByApproximateLength(2); var separators = grid.GetCellSeparators(true); var beam = new Beam(wallCandidate.line, Polygon.Rectangle(mullionSize, mullionSize), mullionMat, 0, 0, 0, isElementDefinition: true); output.Model.AddElement(beam.CreateInstance(levelVolume.Transform, "Base Mullion")); output.Model.AddElement(beam.CreateInstance(levelVolume.Transform.Concatenated(new Transform(0, 0, doorHeight)), "Base Mullion")); output.Model.AddElement(beam.CreateInstance(levelVolume.Transform.Concatenated(new Transform(0, 0, totalStorefrontHeight)), "Base Mullion")); foreach (var separator in separators) { // var line = new Line(separator, separator + new Vector3(0, 0, levelVolume.Height)); // output.Model.AddElement(new ModelCurve(line, BuiltInMaterials.XAxis, levelVolume.Transform)); var instance = mullion.CreateInstance(new Transform(separator, wallCandidate.line.Direction(), Vector3.ZAxis, 0).Concatenated(levelVolume.Transform), "Mullion"); output.Model.AddElement(instance); } output.Model.AddElement(new StandardWall(wallCandidate.line, 0.05, totalStorefrontHeight, glassMat, levelVolume.Transform)); var headerHeight = levelVolume.Height - totalStorefrontHeight; if (headerHeight > 0.01) { output.Model.AddElement(new StandardWall(wallCandidate.line, 0.2, headerHeight, wallMat, levelVolume.Transform.Concatenated(new Transform(0, 0, totalStorefrontHeight)))); } } } } } InstancePositionOverrides(input.Overrides, output.Model); return(output); }