private static Result placeTemporaryShoring(UIDocument uiDoc, BuildingLoadModel buildingLoadModel, List <ILoadModel> loadModels, string reshoringFamilyPathName, string reshoringFamilySymbol) { //Setup Document _doc = uiDoc.Document; //ToDo: it would be valuable to let users pick which Family & Type they want to use - so they can rapidly understand differences in layout for different reshores Dictionary <string, FamilyDefinition> _familyMappings = new List <string> { @"C:\ProgramData\Autodesk\Revit\Addins\2021\ApatosReshoring\Reshoring Poles 6x6.rfa", @"C:\ProgramData\Autodesk\Revit\Addins\2021\ApatosReshoring\Reshoring Poles Ellis 4x4.rfa", @"C:\ProgramData\Autodesk\Revit\Addins\2021\ApatosReshoring\Reshoring Poles Titan-HV.rfa", @"C:\ProgramData\Autodesk\Revit\Addins\2021\ApatosReshoring\Reshoring Poles Titan-XL.rfa", }.ToDictionary(p => Path.GetFileNameWithoutExtension(p), p => FamilyHelpers.GetOrLoadFamilyDefinition(_doc, p)); string _familyName = Path.GetFileNameWithoutExtension(reshoringFamilyPathName); FamilySymbol _temporaryShoreSymbol = null; if (_familyMappings.ContainsKey(_familyName)) { Family _family = _familyMappings[_familyName]?.Family; foreach (ElementId _symbolID in _family.GetFamilySymbolIds()) { FamilySymbol _familySymbol = _doc.GetElement(_symbolID) as FamilySymbol; if (_familySymbol == null) { continue; } if (_familySymbol.Name != reshoringFamilySymbol) { continue; } _temporaryShoreSymbol = _familySymbol; if (_temporaryShoreSymbol != null) { break; } } } if (_temporaryShoreSymbol == null) { return(Result.Failed); } if (_temporaryShoreSymbol.IsActive == false) { _temporaryShoreSymbol.Activate(); } var _levels = Getters.GetLevels(uiDoc.Document).OrderByDescending(p => p.Elevation).ToList(); var _floors = Getters.GetFloors(_doc).OrderBy(p => p.get_BoundingBox(null)?.Max.Z).ToList(); Level _topLevel = _levels.FirstOrDefault(); //Add Level Loads addLevelLoadModels(buildingLoadModel, _levels, _floors, _topLevel, loadModels); buildingLoadModel.ReadLoads(); //Place Temporary Shores Dictionary <double, double> _clearShoreHeightCapacityMappings = getClearShoreHeightCapacityMappings(buildingLoadModel, _topLevel, _temporaryShoreSymbol); LevelLoadModel _levelLoadModelAbove = buildingLoadModel.LevelLoadModels.OfType <LevelLoadModel>().FirstOrDefault(p => p.Level.Id == _topLevel.Id); if (_levelLoadModelAbove == null) { return(Result.Failed); } Dictionary <LevelLoadModel, double> _levelLoadModelXDistances = new Dictionary <LevelLoadModel, double>(); Dictionary <LevelLoadModel, double> _levelLoadModelYDistances = new Dictionary <LevelLoadModel, double>(); LevelLoadModel _lowestReshoredLevelLoadModel = null; double _levelAboveDemand = _levelLoadModelAbove.ReshoreDemandPoundsForcePerSquareFoot; foreach (LevelLoadModel _levelLoadModel in buildingLoadModel.LevelLoadModels) { if (_levelAboveDemand > 0.0) { if (_levelLoadModel.Level.Id == _topLevel.Id) { continue; } double _roundedClearShoreHeight = Math.Round(_levelLoadModel.ClearShoreHeight, 9); if (_clearShoreHeightCapacityMappings.ContainsKey(_roundedClearShoreHeight) == false) { continue; } double _loadCapacityPerShore = _clearShoreHeightCapacityMappings[_roundedClearShoreHeight]; double _minimumAreaPerShore = _loadCapacityPerShore / _levelAboveDemand; double _allowedAreaSquareSideLength = Math.Sqrt(_minimumAreaPerShore); double _roundedSideLength = Math.Floor(_allowedAreaSquareSideLength); //safer to round both down & have a smaller area _levelLoadModelXDistances.Add(_levelLoadModel, _roundedSideLength); _levelLoadModelYDistances.Add(_levelLoadModel, _roundedSideLength); //Adjust our Capacity & Demand, then determine if we can stop adding temportary reshoring _levelLoadModel.ReshoreDemandPoundsForcePerSquareFoot = _levelAboveDemand; _levelAboveDemand -= _levelLoadModel.CapacityPoundsForcePerSquareFoot; } else { if (_lowestReshoredLevelLoadModel == null) { _lowestReshoredLevelLoadModel = _levelLoadModelAbove; } _levelLoadModel.ReshoreDemandPoundsForcePerSquareFoot = 0.0; } _levelLoadModelAbove = _levelLoadModel; } //Update Level parameters foreach (LevelLoadModel _levelLoadModel in buildingLoadModel.LevelLoadModels) { _levelLoadModel.SetLevelParameters(); } //Place temporary reshoring List <FamilyInstance> _temporaryShores = new List <FamilyInstance>(); _levelLoadModelAbove = buildingLoadModel.LevelLoadModels.OfType <LevelLoadModel>().FirstOrDefault(p => p.Level.Id == _topLevel.Id); foreach (LevelLoadModel _levelLoadModel in buildingLoadModel.LevelLoadModels.Where(p => p.ReshoreDemandPoundsForcePerSquareFoot > 0.0)) { if (_levelLoadModel.Level.Id == _topLevel.Id) { continue; } if (_levelLoadModelXDistances.ContainsKey(_levelLoadModel) == false || _levelLoadModelYDistances.ContainsKey(_levelLoadModel) == false) { break; } double _sideXLength = _levelLoadModelXDistances[_levelLoadModel]; double _sideYLength = _levelLoadModelYDistances[_levelLoadModel]; //ToDo: add 1, subtract 1 from each side & check if their total area is closer to the calculated area than a square //ToDo: do this twice, and take the "middle" option List <Floor> _levelAboveFloors = _floors.Where(p => p.LevelId == _levelLoadModelAbove.Level.Id).ToList(); List <FamilyInstance> _temporaryReshores = new List <FamilyInstance>(); foreach (Floor _floor in _levelAboveFloors) { BoundingBoxXYZ _currentFloorBounds = _floor.get_BoundingBox(null); List <double> _xCoords = new List <double>(); for (double _x = 0.0; _x <= _currentFloorBounds.Max.X - _currentFloorBounds.Min.X; _x += _sideXLength) { _xCoords.Add(_x); } List <double> _yCoords = new List <double>(); for (double _y = 0.0; _y <= _currentFloorBounds.Max.Y - _currentFloorBounds.Min.Y; _y += _sideYLength) { _yCoords.Add(_y); } foreach (double _x in _xCoords) { foreach (double _y in _yCoords) { XYZ _insertionCoordinate = new XYZ( _currentFloorBounds.Min.X + _x, _currentFloorBounds.Min.Y + _y, _levelLoadModel.ElevationFeet); FamilyInstance _temporaryShore = _doc.Create.NewFamilyInstance(_insertionCoordinate, _temporaryShoreSymbol, Autodesk.Revit.DB.Structure.StructuralType.Column); Parameter _heightParam = _temporaryShore.LookupParameter("Height"); if (_heightParam != null && _heightParam.IsReadOnly == false) { _heightParam.Set(_levelLoadModel.ClearShoreHeight); } _temporaryShores.Add(_temporaryShore); } } } } //Regenerate so that the latest locations are updated in the model for our newly placed reshores - the intersects filters below will fail without this _doc.Regenerate(); var _scopeBoxes = Getters.GetScopeBoxes(_doc); Dictionary <ElementId, string> _shoreIdScopeBoxNames = new Dictionary <ElementId, string>(); foreach (Element _scopeBox in _scopeBoxes) { List <Element> _columnsInScopeBox = Getters.GetInsideElements(_doc, _temporaryShores, _scopeBox.get_BoundingBox(null)); foreach (Level _level in _levels) { List <Element> _columnsInScopeBoxOnLevel = _columnsInScopeBox.Where(p => ((p.Location as LocationPoint)?.Point.Z - _level.Elevation) <= 1.0).ToList(); string _pourName = Helpers.Views.BoundedViewCreator.GetViewName(_level, _scopeBox, string.Empty, string.Empty); foreach (Element _column in _columnsInScopeBoxOnLevel) { _column.get_Parameter(Ids.PourNameParameterId)?.Set(_pourName); } } } return(Result.Succeeded); }