public void Grid1dFixedDivisions() { var length = 10; var panelTarget = 3; var sacrificial = 0; var inMiddle = new Grid1d(new Line(new Vector3(0, 0, 0), new Vector3(length, 0, 0))); var atStart = new Grid1d(new Line(new Vector3(0, 1, 0), new Vector3(length, 1, 0))); var atEnd = new Grid1d(new Line(new Vector3(0, 2, 0), new Vector3(length, 2, 0))); var atBothEnds = new Grid1d(new Line(new Vector3(0, 3, 0), new Vector3(length, 3, 0))); inMiddle.DivideByFixedLength(panelTarget, FixedDivisionMode.RemainderNearMiddle, sacrificial); atStart.DivideByFixedLength(panelTarget, FixedDivisionMode.RemainderAtStart, sacrificial); atEnd.DivideByFixedLength(panelTarget, FixedDivisionMode.RemainderAtEnd, sacrificial); atBothEnds.DivideByFixedLength(panelTarget, FixedDivisionMode.RemainderAtBothEnds, sacrificial); Assert.Equal(panelTarget, inMiddle.Cells.First().Domain.Length); Assert.Equal(panelTarget, inMiddle.Cells.Last().Domain.Length); Assert.NotEqual(panelTarget, atStart.Cells.First().Domain.Length); Assert.Equal(panelTarget, atStart.Cells.Last().Domain.Length); Assert.Equal(panelTarget, atEnd.Cells.First().Domain.Length); Assert.NotEqual(panelTarget, atEnd.Cells.Last().Domain.Length); Assert.NotEqual(panelTarget, atBothEnds.Cells.First().Domain.Length); Assert.NotEqual(panelTarget, atBothEnds.Cells.Last().Domain.Length); }
public void Grid1d() { this.Name = "Elements_Spatial_Grid1d"; // <example> // Create a 1d Grid from a line var line = new Line(new Vector3(5, 0, 0), new Vector3(60, 0, 0)); var grid = new Grid1d(line); // Divide the grid into sections of length 10, and leave remainders // at both ends grid.DivideByFixedLength(10, FixedDivisionMode.RemainderAtBothEnds); // Take the second grid segment and subdivide it // into 5 equal length segments grid[1].DivideByCount(5); // Take the third grid segment and subdivide it into // segments of approximate length 3 grid[2].DivideByApproximateLength(3); // Take the fourth grid segment and subdivide it by a repeating pattern var pattern = new[] { 1.0, 1.5 }; grid[3].DivideByPattern(pattern); // Retrieve all bottom-level cells. // Note that grid.Cells gets the top-level cells only, and // grid.GetCells() recursively gets the bottom-level individual cells. var cells = grid.GetCells(); // Get lines representing each cell var lines = cells.Select(c => c.GetCellGeometry()).OfType <Line>(); // Create walls from lines, and assign a random color material List <Wall> walls = new List <Wall>(); var rand = new Random(); foreach (var wallLine in lines) { var color = new Color(rand.NextDouble(), rand.NextDouble(), rand.NextDouble(), 1.0); walls.Add(new StandardWall(wallLine, 0.1, 3.0, new Material(color.ToString(), color, 0, 0, null, false, false))); } // Create rectangles from top-level grid cells var topLevelCells = grid.Cells.Select(c => c.GetCellGeometry()).OfType <Line>(); var cellRects = new List <ModelCurve>(); foreach (var topLevelCell in topLevelCells) { var rect = Polygon.Rectangle(topLevelCell.Start - new Vector3(0, 2, 0), topLevelCell.End + new Vector3(0, 2, 0)); cellRects.Add(new ModelCurve(rect)); } // </example> this.Model.AddElements(cellRects); this.Model.AddElements(walls); }
private void AddEdge(long key, long startVertexId, long endVertexId) { if (this.Points.TryGetValue(startVertexId, out var start) && this.Points.TryGetValue(endVertexId, out var end)) { var dist = start.DistanceTo(end); var line = new Line(start, end); if (!SkipSubdivide && dist > DivisionLength && (start.X != end.X || start.Y != end.Y)) { var indices = new List <long>() { startVertexId }; var grid = new Grid1d(line); grid.DivideByFixedLength(DivisionLength, FixedDivisionMode.RemainderAtBothEnds); var cells = grid.GetCells(); // Get lines representing each 10' cell var cellLines = cells.Select(c => c.GetCellGeometry()).OfType <Line>().ToArray(); // Add end of each division except for last point foreach (var cellLine in cellLines.SkipLast(1)) { var index = this._maxVertexKey + 1; var point = cellLine.PointAt(1.0); this.Points.Add(index, point); indices.Add(index); this._maxVertexKey = index; } // Add right point indices.Add(endVertexId); this.Lines.Add(key, indices); } else { this.Lines.Add(key, new List <long>() { startVertexId, endVertexId }); } } else { throw new Exception("Malformed geometry found: no vertex found at address for this edge."); } }
/// <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 = IsClosed ? new Polygon(newVertices) : new Polyline(newVertices); var grid1d = new Grid1d(path); switch (SpacingRule.SpacingMode) { case SpacingMode.ByLength: grid1d.DivideByFixedLength(SpacingRule.Value); break; case SpacingMode.ByApproximateLength: grid1d.DivideByApproximateLength(SpacingRule.Value); break; case SpacingMode.ByCount: grid1d.DivideByCount((int)SpacingRule.Value); break; } var separators = grid1d.GetCellSeparators(); foreach (var sep in separators) { ElementDefinition.IsElementDefinition = true; var transform = new Transform(definition.OrientationGuide); transform.Concatenate(new Transform(sep)); var instance = ElementDefinition.CreateInstance(transform, Guid.NewGuid().ToString()); arrayElements.Add(instance); } return(arrayElements); }
private static List <WallPanel> ProcessWallsAndStandardWalls(PanelsFromWallsInputs input, List <Wall> allWalls, out int totalCount, out int uniqueCount, out int nonStandardCount) { var wallCenterlines = allWalls.Select(TryGetCenterlineFromWall).Where(s => s != null); var endPoints = wallCenterlines.SelectMany(l => new[] { l.Start, l.End }); var network = new Network(wallCenterlines); Dictionary <Elements.Spatial.Edge, Grid1d> edgeGrids = new Dictionary <Elements.Spatial.Edge, Grid1d>(); foreach (var edge in network.Edges) { var edgeLine = network.GetEdgeLine(edge); var grid = new Grid1d(edgeLine); edgeGrids.Add(edge, grid); var cornerAtStart = network[edge.From].Valence > 1; var cornerAtEnd = network[edge.To].Valence > 1; var cornerCount = (cornerAtStart ? 1 : 0) + (cornerAtEnd ? 1 : 0); var cornerLength = input.CornerLength; if (cornerLength * cornerCount > edgeLine.Length()) { cornerLength = edgeLine.Length() / cornerCount; } if (cornerAtStart) { grid.SplitAtOffset(cornerLength); } if (cornerAtEnd) { grid.SplitAtOffset(cornerLength, true); } Grid1d gridToSubdivide = null; if (!grid.IsSingleCell) { switch (grid.Cells.Count) { case 3: gridToSubdivide = grid[1]; break; case 2: if (cornerCount == 1) { if (cornerAtStart) { gridToSubdivide = grid[1]; } if (cornerAtEnd) { gridToSubdivide = grid[0]; } } break; default: gridToSubdivide = grid; break; } if (gridToSubdivide != null) { gridToSubdivide.DivideByFixedLength(input.PanelLength, FixedDivisionMode.RemainderAtEnd); } } } List <Line> lines = new List <Line>(); foreach (var edgeGrid in edgeGrids) { if (edgeGrid.Value == null) { continue; } var cells = edgeGrid.Value.IsSingleCell ? new List <Grid1d> { edgeGrid.Value } : edgeGrid.Value.GetCells(); var cellGeometry = cells.Select(c => c.GetCellGeometry()).OfType <Line>(); lines.AddRange(cellGeometry); } // Create walls from lines, and assign a random color material var walls = new List <WallPanel>(); var rand = new Random(); var colorMap = new Dictionary <int, Color>(); colorMap.Add(KeyFromLength(input.CornerLength), Colors.Red); if (input.CornerLength != input.PanelLength) { colorMap.Add(KeyFromLength(input.PanelLength), Colors.Blue); } foreach (var wallLine in lines) { var color = default(Color); var lengthKey = KeyFromLength(wallLine.Length()); if (colorMap.ContainsKey(lengthKey)) { color = colorMap[lengthKey]; } else { color = new Color(rand.NextDouble(), rand.NextDouble(), rand.NextDouble(), 1.0); colorMap.Add(lengthKey, color); } var mat = input.ColorCodeByLength ? new Material(color, 0, 0, false, null, true, Guid.NewGuid(), color.ToString()) : BuiltInMaterials.Concrete; walls.Add(CreateSimpleWallPanel(wallLine, 0.1, 3.0, mat)); } nonStandardCount = lines.Where(l => KeyFromLength(l.Length()) != KeyFromLength(input.PanelLength) && KeyFromLength(l.Length()) != KeyFromLength(input.CornerLength)).Count(); totalCount = walls.Count; uniqueCount = Math.Min(totalCount, colorMap.Count()); return(walls); }
/// <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> /// The Noisewall function. /// </summary> /// <param name="model">The input model.</param> /// <param name="input">The arguments to the execution.</param> /// <returns>A NoisewallOutputs instance containing computed results and the model with any new elements.</returns> public static NoisewallOutputs Execute(Dictionary <string, Model> inputModels, NoisewallInputs input) { // Setup inputs var wallCentres = input.NoisewallSetoutCentres; var toleranceGap = input.ToleranceGap; double wallHeight = input.NoisewallPanelHeight; var wallDepth = input.NoisewallPanelDepth; var wallWidth = wallCentres - toleranceGap; var setoutPolylineCrv = input.SetoutCurve; var colour = input.Colour; // Model smooth setout crv var verts = setoutPolylineCrv.Vertices as List <Vector3>; var bezier = new Bezier(verts); var bezierModelCurve = new ModelCurve(bezier, new Material("Green", Colors.Green)); // Divide curve var grid = new Grid1d(bezier); grid.DivideByFixedLength(wallCentres, FixedDivisionMode.RemainderAtBothEnds); var cells = grid.GetCells(); var lines = cells.Select(c => c.GetCellGeometry()).OfType <Line>(); int numWalls = lines.Count(); List <Wall> walls = new List <Wall>(); List <Beam> beams = new List <Beam>(); // Create Beam profile var profile = WideFlangeProfileServer.Instance.GetProfileByType(WideFlangeProfileType.W10x100); // Model beam at origin Line line = new Line(Vector3.Origin, new Vector3(0, 0, wallHeight)); List <Beam> linearBeams = new List <Beam>(); // Create range between 0 and 2pi with nDivisions List <double> normalisedRange = new List <double>(); double max = 2 * Math.PI; double min = 0; int nDivisions = numWalls; double diff = (max - min) / nDivisions; double d = min; foreach (int i in Enumerable.Range(0, nDivisions - 1)) { d = d + diff; normalisedRange.Add(min + d); } // Setup random heights within range int wallMinHeight = 6; int wallMaxHeight = 9; Random rand = new Random(); List <int> randomWallHeights = new List <int>(); foreach (int i in Enumerable.Range(0, numWalls)) { randomWallHeights.Add(rand.Next(wallMinHeight, wallMaxHeight)); } // // Base sin wave function parameters // List<double> normalisedHeights = new List<double>(); // foreach (double number in normalisedRange) // { // normalisedHeights.Add(Math.Sin(number)); // } // // Remap heights // List<double> remappedHeights = new List<double>(); // foreach (double number in normalisedHeights) // { // remappedHeights.Add(Remap(number, min, max, wallMinHeight, wallMaxHeight)); // } int increment = 0; foreach (var setoutLine in lines) { // Set wall wallHeight = randomWallHeights.ElementAt(increment); // Factor in tolerance Gap using vector math // panelHeight = remappedHeights.ElementAt(increment); wallHeight = input.NoisewallPanelHeight; var noisewallLength = wallCentres - toleranceGap * 2; Vector3 lineStartPoint = setoutLine.Start; Vector3 lineEndPoint = setoutLine.End; Vector3 lineDirectionUnitVector = (lineEndPoint - lineStartPoint).Unitized(); var noisewallCentreline = new Line(lineStartPoint + lineDirectionUnitVector * toleranceGap, lineEndPoint - lineDirectionUnitVector * toleranceGap); // Create beam transforms Transform perpFrame = setoutLine.TransformAt(0); Transform centreSetoutPlane = setoutLine.TransformAt(0.5); Transform orientBeams = new Transform(setoutLine.Start, perpFrame.ZAxis, perpFrame.YAxis); // Model Beams var linearBeam = new Beam(line, profile, BuiltInMaterials.Steel, 0, 0, 0, orientBeams); beams.Add(linearBeam); // Model Walls Material lightConcrete = new Material("Light Concrete", colour, 0.1, 0.0); StandardWall wall = new StandardWall(noisewallCentreline, wallDepth, wallHeight, lightConcrete); walls.Add(wall); increment++; } // Create output object and add parameters var output = new NoisewallOutputs(walls.Count); // Add elements to output display output.Model.AddElement(bezierModelCurve); output.Model.AddElements(walls); output.Model.AddElements(beams); return(output); }