public Result CalculateLoads(Document doc, ref int totalLoops, ref StringBuilder debug) { try { //Log int nrTotal = 0; //Get the transform Transform trfO = Origo.GetTransform(); Transform trf = trfO.Inverse; //Get the list of boundaries and walls (to simplify the synthax) HashSet <CurveElement> Bd = BoundaryData.BoundaryLines; HashSet <FamilyInstance> Walls = WallsAlong.WallSymbols.ToHashSet(); HashSet <FamilyInstance> Beams = BearingBeams.WallSymbols.ToHashSet(); HashSet <Element> BdAndWalls = new HashSet <Element>(); BdAndWalls.UnionWith(Bd); BdAndWalls.UnionWith(Walls); BdAndWalls.UnionWith(Beams); //I know it's messy, but beams need to be in walls too, apparently Walls.UnionWith(Beams); HashSet <LoadArea> LoadAreas = LoadData.LoadAreas; //Roof load intensity double roofLoadIntensity = double.Parse(mySettings.Default.roofLoadIntensity, CultureInfo.InvariantCulture); //Create a list of ALL X Points of Interest ie. Start and End points IList <XYZ> allPoiX = new List <XYZ>(); foreach (Element el in BdAndWalls) { allPoiX.Add(StartPoint(el, trf)); allPoiX.Add(EndPoint(el, trf)); } //Clean list of duplicates and sort by value of X allPoiX = allPoiX.DistinctBy(pt => pt.X.FtToMillimeters()).OrderBy(pt => pt.X.FtToMillimeters()).ToList(); foreach (FamilyInstance fi in Walls) { //Debug debug.Append("\n" + fi.Id + "\n"); //Initialize variables double load = 0; double totalArea = 0; //Determine the start X double Xmin = StartPoint(fi, trf).X; //Determine the end X double Xmax = EndPoint(fi, trf).X; //The y of the wall double Ycur = StartPoint(fi, trf).Y; //Længde af væggen double length = (Xmax - Xmin).FtToMeters(); //TODO: New implementation -- start here //Determine the POIX in scope of the wall (POIX = Point Of Interest on X axis) var poixInScope = (from XYZ pt in allPoiX where pt.X.FtToMillimeters() >= Xmin.FtToMillimeters() && pt.X.FtToMillimeters() <= Xmax.FtToMillimeters() select pt).ToList(); //Iterate through the load areas for (int i = 0; i < poixInScope.Count - 1; i++) //<- -1 because theres 1 less areas than points { //Determine the X value in the middle of the span to be able to get relevant walls double x1 = poixInScope[i].X; double x2 = poixInScope[i + 1].X; double xC = (x1 + (x2 - x1) / 2).FtToMillimeters(); //Determine relevant walls (that means the walls which are crossed by the current Xcentre value iteration) var wallsX = (from FamilyInstance fin in Walls where StartPoint(fin, trf).X.FtToMillimeters() <= xC && EndPoint(fin, trf).X.FtToMillimeters() >= xC select fin).OrderBy(x => StartPoint(x, trf).Y); //<- Not using Descending because the list is defined from up to down //Determine relevant boundaries (that means the boundaries which are crossed by the current Xcentre value iteration) var boundaryX = (from CurveElement cue in Bd where StartPoint(cue, trf).X.FtToMillimeters() <= xC && EndPoint(cue, trf).X.FtToMillimeters() >= xC select cue).ToHashSet(); //First handle the walls //Create a linked list to be able select previous and next elements in sequence var wallsXlinked = new LinkedList <FamilyInstance>(wallsX); var node = wallsXlinked.Find(fi); var wallPositive = node?.Next?.Value; var wallNegative = node?.Previous?.Value; //Select boundaries if no walls found at location CurveElement bdPositive = null, bdNegative = null; if (wallPositive == null) { bdPositive = boundaryX.MaxBy(x => StartPoint(x, trf).Y); } if (wallNegative == null) { bdNegative = boundaryX.MinBy(x => StartPoint(x, trf).Y); } //Flow control bool isEdgePositive = false, isEdgeNegative = false; //<-- Indicates if the wall is on the boundary //Detect edge cases if (wallPositive == null && StartPoint(bdPositive, trf).Y.FtToMillimeters().Equals(Ycur.FtToMillimeters())) { isEdgePositive = true; } if (wallNegative == null && StartPoint(bdNegative, trf).Y.FtToMillimeters().Equals(Ycur.FtToMillimeters())) { isEdgeNegative = true; } //Prepare for roof load: if edge case detected select both boundaries if (isEdgePositive || isEdgeNegative) { if (bdPositive == null) { bdPositive = boundaryX.MaxBy(x => StartPoint(x, trf).Y); } if (bdNegative == null) { bdNegative = boundaryX.MinBy(x => StartPoint(x, trf).Y); } double widthRoofLoad = (StartPoint(bdPositive, trf).Y - StartPoint(bdNegative, trf).Y) / 2; double roofLoadArea = (widthRoofLoad * (x2 - x1)).SqrFeetToSqrMeters(); double roofLoad = roofLoadIntensity * roofLoadArea; load += roofLoad; //Write to the overall load variable } //Process the positive and negative side //Declare Y values double yP, yN; //Declare combining list for vertices List <XYZ> vertices = new List <XYZ>(); //Add points along the wall if one is edge case if (isEdgePositive || isEdgeNegative) { vertices.Add(NormPoint(x1.Round4(), Ycur.Round4(), trfO, LoadData.GS_View)); vertices.Add(NormPoint(x2.Round4(), Ycur.Round4(), trfO, LoadData.GS_View)); } #region Positive side if (!isEdgePositive) { //Calculate Y values if (wallPositive != null) { yP = Ycur + (StartPoint(wallPositive, trf).Y - Ycur) / 2; } else { yP = Ycur + (StartPoint(bdPositive, trf).Y - Ycur) / 2; } //Create points from the X and Y values XYZ PxP1 = NormPoint(x1.Round4(), yP.Round4(), trfO, LoadData.GS_View); XYZ PxP2 = NormPoint(x2.Round4(), yP.Round4(), trfO, LoadData.GS_View); //Create a list of vertices to feed the solid builder vertices.Add(PxP1); vertices.Add(PxP2); nrTotal++; } #endregion #region Negative side if (!isEdgeNegative) { if (wallNegative != null) { yN = StartPoint(wallNegative, trf).Y + (Ycur - StartPoint(wallNegative, trf).Y) / 2; } else { yN = StartPoint(bdNegative, trf).Y + (Ycur - StartPoint(bdNegative, trf).Y) / 2; } //Create points from the X and Y values XYZ PxN1 = NormPoint(x1.Round4(), yN.Round4(), trfO, LoadData.GS_View); XYZ PxN2 = NormPoint(x2.Round4(), yN.Round4(), trfO, LoadData.GS_View); //Create a list of vertices to feed the solid builder vertices.Add(PxN1); vertices.Add(PxN2); nrTotal++; } #endregion //Create a list of vertices vertices = vertices.DistinctBy(xyz => new { X = xyz.X.Round4(), Y = xyz.Y.Round4() }).ToList(); vertices = tr.ConvexHull(vertices); //Create a path from the Clipper framework Path wallLoadPath = CreatePath(vertices); //The defined precision of the Clipper objects long precision = 10000; //Debug //debug.Append((Clipper.Area(wallLoadPath) / (precision * precision)).SqrFeetToSqrMeters() + "+\n"); //Iterate through the load areas and intersect them with wall load areas foreach (LoadArea la in LoadAreas) { Paths solution = new Paths(); Clipper c = new Clipper(); c.AddPath(wallLoadPath, PolyType.ptClip, true); c.AddPath(la.Path, PolyType.ptSubject, true); c.Execute(ClipType.ctIntersection, solution); foreach (Path path in solution) { double intArea = (Clipper.Area(path) / (precision * precision)).SqrFeetToSqrMeters(); //debug.Append(intArea + " " + la.Load + " " + la.Load * intArea + "\n"); //debug.Append((Clipper.Area(la.Path)/(precision*precision)).SqrFeetToSqrMeters()+"\n"); load += intArea * la.Load; totalArea += intArea; } } } fi.LookupParameter("GS_Load").Set(load / length); debug.Append(length + " " + totalArea + " " + load + "\n" + load / length + "\n"); } totalLoops = nrTotal; return(Result.Succeeded); } catch (Exception e) { Console.WriteLine(e); throw new Exception(e.Message); //return Result.Succeeded; } }
public Result DrawLoadAreas(Document doc) { try { //Get the transform Transform trfO = Origo.GetTransform(); Transform trf = trfO.Inverse; //Get the list of boundaries and walls (to simplify the synthax) HashSet <CurveElement> Bd = BoundaryData.BoundaryLines; HashSet <FamilyInstance> Walls = WallsAlong.WallSymbols.ToHashSet(); //The analysis proceeds in steps double step = ((double)mySettings.Default.integerStepSize).MmToFeet(); foreach (FamilyInstance fi in Walls) { //Determine the start X double Xmin = StartPoint(fi, trf).X; //Determine the end X double Xmax = EndPoint(fi, trf).X; //The y of the wall double Ycur = StartPoint(fi, trf).Y; ////Divide the largest X value by the step value to determine the number iterations in X direction int nrOfX = (int)Math.Floor((Xmax - Xmin) / step); //Debug double[] X, Y; X = new double[4]; Y = new double[4]; //Iterate through the length of the current wall analyzing the load for (int i = 0; i < nrOfX; i++) { //Current x value double x1 = Xmin + i * step; double x2 = Xmin + (i + 1) * step; double xC = x1 + step / 2; //Debug if (i == 0) { X[0] = x1; X[2] = x1; } X[1] = x2; X[3] = x2; //Determine relevant walls (that means the walls which are crossed by the current X value iteration) var wallsX = (from FamilyInstance fin in Walls where StartPoint(fin, trf).X <= xC && EndPoint(fin, trf).X >= xC select fin).OrderBy(x => StartPoint(x, trf).Y); //<- Not using Descending because the list is defined from up to down //Determine relevant walls (that means the walls which are crossed by the current X value iteration) var boundaryX = (from CurveElement cue in Bd where StartPoint(cue, trf).X <= xC && EndPoint(cue, trf).X >= xC select cue).ToHashSet(); //First handle the walls var wallsXlinked = new LinkedList <FamilyInstance>(wallsX); var listNode1 = wallsXlinked.Find(fi); var wallPositive = listNode1?.Next?.Value; var wallNegative = listNode1?.Previous?.Value; //Select boundaries if no walls found at location CurveElement bdPositive = null, bdNegative = null; if (wallPositive == null) { bdPositive = boundaryX.MaxBy(x => StartPoint(x, trf).Y); } if (wallNegative == null) { bdNegative = boundaryX.MinBy(x => StartPoint(x, trf).Y); } //Flow control bool isEdgePositive = false, isEdgeNegative = false; //<-- Indicates if the wall is on the boundary //Detect edge cases if (wallPositive == null && StartPoint(bdPositive, trf).Y.FtToMillimeters().Equals(Ycur.FtToMillimeters())) { isEdgePositive = true; } if (wallNegative == null && StartPoint(bdNegative, trf).Y.FtToMillimeters().Equals(Ycur.FtToMillimeters())) { isEdgeNegative = true; } //Init loop counters int nrOfYPos, nrOfYNeg; //Determine number of iterations in Y direction POSITIVE handling all cases //The 2* multiplier on step makes sure that iteration only happens on the half of the span if (wallPositive != null) { nrOfYPos = (int)Math.Floor((StartPoint(wallPositive, trf).Y - Ycur) / (2 * step)); } else if (isEdgePositive) { nrOfYPos = 0; } else { nrOfYPos = (int)Math.Floor((StartPoint(bdPositive, trf).Y - Ycur) / (2 * step)); } //Determine number of iterations in Y direction NEGATIVE handling all cases //The 2* multiplier on step makes sure that iteration only happens on the half of the span if (wallNegative != null) { nrOfYNeg = (int)Math.Floor((-StartPoint(wallNegative, trf).Y + Ycur) / (2 * step)); } else if (isEdgeNegative) { nrOfYNeg = 0; } else { nrOfYNeg = (int)Math.Floor((-StartPoint(bdNegative, trf).Y + Ycur) / (2 * step)); } //Iterate through the POSITIVE side for (int j = 0; j < nrOfYPos; j++) { //Current y value double y1 = Ycur + j * step; double y2 = Ycur + (j + 1) * step; //Debug if (i == 0 && j == nrOfYPos - 1) { Y[0] = y2; } if (j == nrOfYPos - 1) { Y[1] = y2; if (!Y[0].FtToMillimeters().Equals(Y[1].FtToMillimeters())) { CreateLoadAreaBoundaries(doc, X[0], X[1], Y[0], trfO); X[0] = x1; Y[0] = y2; } if (i == nrOfX - 1) { CreateLoadAreaBoundaries(doc, X[0], X[1], Y[0], trfO); } } } //Iterate through the NEGATIVE side for (int k = 0; k < nrOfYNeg; k++) { //Current y value double y1 = Ycur - k * step; double y2 = Ycur - (k + 1) * step; //Debug if (i == 0 && k == nrOfYNeg - 1) { Y[2] = y2; } if (k == nrOfYNeg - 1) { Y[3] = y2; if (!Y[2].FtToMillimeters().Equals(Y[3].FtToMillimeters())) { CreateLoadAreaBoundaries(doc, X[2], X[3], Y[2], trfO); X[2] = x1; Y[2] = y2; } if (i == nrOfX - 1) { CreateLoadAreaBoundaries(doc, X[2], X[3], Y[2], trfO); } } } } } return(Result.Succeeded); } catch (Exception e) { Console.WriteLine(e); throw new Exception(e.Message); } }