private static void PanelGroundFloor(double bottomElevation, double topElevation, Line[] boundarySegments, double panelWidth, Model model) { foreach (var segment in boundarySegments) { var u = new Grid1d(segment); var v = new Grid1d(new Line(segment.Start, new Vector3(segment.Start.X, segment.Start.Y, segment.Start.Z + topElevation))); var grid2d = new Grid2d(u, v); grid2d.U.DivideByFixedLength(1.5); grid2d.V.DivideByCount(2); foreach (var sep in grid2d.GetCellSeparators(GridDirection.U)) { var mullion = new Beam((Line)sep, Polygon.Rectangle(0.05, 0.05), BuiltInMaterials.Black); model.AddElement(mullion); } foreach (var sep in grid2d.GetCellSeparators(GridDirection.V)) { var mullion = new Beam((Line)sep, Polygon.Rectangle(0.05, 0.05), BuiltInMaterials.Black); model.AddElement(mullion); } var panel = new Panel(new Polygon(new[] { segment.Start, segment.End, new Vector3(segment.End.X, segment.End.Y, segment.End.Z + topElevation), new Vector3(segment.Start.X, segment.Start.Y, segment.Start.Z + topElevation) }), BuiltInMaterials.Glass); model.AddElement(panel); } }
public void CellSeparatorsTrimmed() { Name = "CellSeparatorsTrimmed"; var polygon = new Polygon(new[] { Vector3.Origin, new Vector3(4, 2), new Vector3(5, 3), new Vector3(7, 7), new Vector3(3, 6) }); var polygon2 = new Polygon(new[] { new Vector3(1.1, 1), new Vector3(1.5, 1), new Vector3(1.5, 2), new Vector3(1.1, 2) }); var polygons = new[] { polygon, polygon2 }; var grid2d = new Grid2d(polygons); grid2d.U.DivideByCount(10); grid2d.V.DivideByFixedLength(1); var csu = grid2d.GetCellSeparators(GridDirection.U, true); var csv = grid2d.GetCellSeparators(GridDirection.V, true); Assert.Equal(10, csu.Count); Assert.Equal(10, csv.Count); Model.AddElements(polygons.Select(p => new ModelCurve(p, BuiltInMaterials.XAxis))); Model.AddElements(csu.Union(csv).Select(l => new ModelCurve(l as Line))); }
public static void AddRow(Grid2d row, Model model) { row.U.DivideByApproximateLength(2.7, EvenDivisionMode.RoundUp); var spaceSeparators = row.GetCellSeparators(GridDirection.V); foreach (var line in spaceSeparators) { AddLine((Line)line, model); } }
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 } }; }
/// <summary> /// The PrefabricatedPanels function. /// </summary> /// <param name="model">The input model.</param> /// <param name="input">The arguments to the execution.</param> /// <returns>A PrefabricatedPanelsOutputs instance containing computed results and the model with any new elements.</returns> public static PrefabricatedPanelsOutputs Execute(Dictionary <string, Model> inputModels, PrefabricatedPanelsInputs input) { var wallPanels = inputModels["WallPanels"].AllElementsOfType <WallPanel>(); var panelCount = 0; var totalFramingLength = 0.0; var elements = new List <Element>(); var studWidth = Elements.Units.InchesToMeters(3.625); var studDepth = Elements.Units.InchesToMeters(1.5); var tolerance = 0.005; var studProfile = new StudProfile(); var outerStudMaterial = new Material("Stud Frame", Colors.Orange); var studMaterial = new Material("Stud", new Color(0.7, 0.7, 0.7, 1.0), 0.8f, 0.8f); var headerProfile = new Profile(Polygon.Rectangle(Units.InchesToMeters(3.625), Units.InchesToMeters(3.625))); var headerTrans = new Transform(new Vector3(0, Units.InchesToMeters(3.625) / 2)); // headerProfile.Transform(headerTrans); var centerLineMaterial = new Material("Center Line", Colors.Gray); var wallBoard = new Material("Wall Board", new Color(0.9, 0.9, 0.9, 0.75), 0.0f, 0.0f); foreach (var panel in wallPanels) { // Draw the panel profiles. // var mc = new ModelCurve(panel.Transform.OfPolygon(panel.Profile.Perimeter), centerLineMaterial); // elements.Add(mc); var wallBoardOffset = panel.Profile.Perimeter.Offset(-tolerance); // Offset the panel profile var offset = panel.Profile.Perimeter.Offset(-studDepth / 2 - tolerance); if (offset.Length == 0) { continue; } var outer = (Polygon)offset[0].Transformed(panel.Transform); // Get the plane of the panel var panelPlane = outer.Plane(); // Draw the panel transform; // var panelTransform = new Transform(outer.Centroid(), panelPlane.Normal); // elements.AddRange(panelTransform.ToModelCurves()); // Draw the panel frame. foreach (var seg in outer.Segments()) { var l = seg.Length(); var d = seg.Direction(); var dot = d.Dot(Vector3.ZAxis); var t = seg.TransformAt(0); // Draw the beam transforms // elements.AddRange(t.ToModelCurves()); var beamRotation = t.XAxis.AngleTo(panelPlane.Normal); // if(Double.IsNaN(beamRotation)) // { // Console.WriteLine($"l: {l}, d:{d}, dot:{dot}"); // } Vector3 a, b; if (Math.Abs(dot) == 1) { a = seg.Start += d * studDepth / 2; b = seg.End -= d * studDepth / 2; } else { a = seg.Start -= d * studDepth / 2; b = seg.End += d * studDepth / 2; } var cl = new Line(a, b); // var beam = new Beam(cl, (dot == 0.0 && seg.Start.Z > 0.1) ? headerProfile : studProfile, outerStudMaterial, rotation: (Double.IsNaN(beamRotation) ? 0.0 : beamRotation)); var beam = new Beam(cl, studProfile, outerStudMaterial, rotation: (Double.IsNaN(beamRotation) ? 0.0 : beamRotation)); totalFramingLength += cl.Length(); elements.Add(beam); } // Draw the panel framing. var grid = new Grid2d(offset[0]); grid.U.DivideByFixedLength(input.StudSpacing); var studCls = grid.GetCellSeparators(GridDirection.V); // Only take the inner studs. var innerCls = studCls.Skip(1).Take(studCls.Count - 2).ToList().Cast <Line>().ToList(); var trimBoundary = offset[0].Segments(); var trimmedInnerCls = TrimLinesToBoundary(innerCls, trimBoundary); foreach (var trimmedLine in trimmedInnerCls) { var t = trimmedLine.TransformAt(0); var beamRotation = t.XAxis.AngleTo(panelPlane.Normal); var innerBeam = new Beam(trimmedLine.Transformed(panel.Transform), studProfile, studMaterial, rotation: beamRotation); totalFramingLength += trimmedLine.Length(); elements.Add(innerBeam); } grid.V.DivideByFixedLength(Units.FeetToMeters(4.0)); var kickerCls = grid.GetCellSeparators(GridDirection.U); var kickerInnerCls = kickerCls.Skip(1).Take(kickerCls.Count - 2).ToList().Cast <Line>().ToList();; var trimmedKickerCls = TrimLinesToBoundary(kickerInnerCls, trimBoundary); foreach (var trimmedKicker in trimmedKickerCls) { var beam = new Beam(trimmedKicker.Transformed(panel.Transform), studProfile, studMaterial); totalFramingLength += trimmedKicker.Length(); elements.Add(beam); } if (input.CreateWallBoard) { var wallBoardGrid = new Grid2d(wallBoardOffset[0]); wallBoardGrid.U.DivideByFixedLength(Units.FeetToMeters(8.0)); wallBoardGrid.V.DivideByFixedLength(Units.FeetToMeters(4.0)); if (wallBoardGrid.CellsFlat.Count > 0) { foreach (var cell in wallBoardGrid.CellsFlat) { foreach (Polygon panelPerimeter in cell.GetTrimmedCellGeometry()) { var wallBoards = CreateOffsetPanel(panel, studWidth, panelPerimeter, wallBoard); elements.AddRange(new[] { wallBoards.left, wallBoards.right }); } } } else { var panelPerimeter = wallBoardOffset[0]; var wallBoards = CreateOffsetPanel(panel, studWidth, panelPerimeter, wallBoard); elements.AddRange(new[] { wallBoards.left, wallBoards.right }); } } panelCount++; } var output = new PrefabricatedPanelsOutputs(panelCount, totalFramingLength); output.Model.AddElements(elements); return(output); }
/// <summary> /// Creates a grid network of corridors within the Story and returns a list of spatially sorted RoomRows ready for population. /// </summary> /// <param name="rowLength">Distance between cross corridors.</param> /// <param name="roomDepth">Desired depth of Rooms.</param> /// <param name="corridorWidth">Width of all corridors.</param> /// <returns>A List of RoomRow</returns> public List <RoomRow> PlanGrid(double rowLength, double rowDepth, double corridorWidth = 3.0, bool split = true) { Corridors.Clear(); Rooms.Clear(); rowLength = rowLength.NearEqual(0.0) ? 1.0 : Math.Abs(rowLength); rowDepth = rowLength.NearEqual(0.0) ? 1.0 : Math.Abs(rowDepth); corridorWidth = rowLength.NearEqual(0.0) ? 1.0 : Math.Abs(corridorWidth); var row = Perimeter.Segments().OrderByDescending(s => s.Length()).First(); var ang = Math.Atan2(row.End.Y - row.Start.Y, row.End.X - row.Start.X) * (180 / Math.PI); var perimeterJig = Perimeter.Rotate(Vector3.Origin, ang * -1); var grid = new Grid2d(perimeterJig); grid.U.DivideByFixedLength(rowLength, FixedDivisionMode.RemainderAtBothEnds); grid.V.DivideByFixedLength(rowDepth, FixedDivisionMode.RemainderAtBothEnds); var uLines = grid.GetCellSeparators(GridDirection.U).Skip(1).Reverse().Skip(1).Reverse(); var vLines = grid.GetCellSeparators(GridDirection.V).Skip(1).Reverse().Skip(1).Reverse(); var ctrLines = new List <Line>(); foreach (var curve in uLines) { ctrLines.Add((Line)curve); } foreach (var curve in vLines) { ctrLines.Add((Line)curve); } foreach (var line in ctrLines) { var corridor = line.Thicken(corridorWidth); if (perimeterJig.Compass().Box.Covers(corridor)) { AddCorridor(new Room(corridor.Rotate(Vector3.Origin, ang), Height)); } } foreach (var cell in grid.CellsFlat) { var polygon = (Polygon)cell.GetCellGeometry(); var compass = polygon.Compass(); if (split) { var north = Polygon.Rectangle(compass.W, compass.NE).Rotate(Vector3.Origin, ang); var south = Polygon.Rectangle(compass.SW, compass.E).Rotate(Vector3.Origin, ang); AddRoom(new Room(north, Height)); AddRoom(new Room(south, Height)); continue; } else if (Math.Abs(compass.SizeY - rowDepth) <= 0.0001) { AddRoom(new Room(polygon.Rotate(Vector3.Origin, ang), Height)); } } var roomRows = new List <RoomRow>(); foreach (var room in Rooms) { roomRows.Add(new RoomRow(room.Perimeter)); } Rooms.Clear(); return(roomRows); }
/// <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); }
/// <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); }