Skip to content

Commit

Permalink
6.3 Deployment (#1406)
Browse files Browse the repository at this point in the history
  • Loading branch information
Fraser Greenroyd authored Sep 21, 2023
2 parents 0a03dae + 7fec304 commit a6b90eb
Show file tree
Hide file tree
Showing 30 changed files with 891 additions and 250 deletions.
2 changes: 1 addition & 1 deletion Revit_Adapter/Revit_Adapter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<Authors>BHoM</Authors>
<Copyright>Copyright © https://github.com/BHoM</Copyright>
<RootNamespace>BH.Adapter.Revit</RootNamespace>
<FileVersion>6.2.0.0</FileVersion>
<FileVersion>6.3.0.0</FileVersion>
</PropertyGroup>

<PropertyGroup>
Expand Down
2 changes: 1 addition & 1 deletion Revit_Core_Adapter/Revit_Core_Adapter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<Authors>BHoM</Authors>
<Copyright>Copyright © https://github.com/BHoM</Copyright>
<RootNamespace>BH.Revit.Adapter.Core</RootNamespace>
<FileVersion>6.2.0.0</FileVersion>
<FileVersion>6.3.0.0</FileVersion>
</PropertyGroup>

<PropertyGroup>
Expand Down
49 changes: 41 additions & 8 deletions Revit_Core_Engine/Convert/Facade/FromRevit/CurtainWall.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,9 @@ public static CurtainWall CurtainWallFromRevit(this Wall wall, RevitSettings set
if (curtainPanels == null || !curtainPanels.Any())
BH.Engine.Base.Compute.RecordError(String.Format("Processing of panels of Revit curtain wall failed. BHoM curtain wall without location has been returned. Revit ElementId: {0}", wall.Id.IntegerValue));


// Get external edges of whole curtain wall
List<FrameEdge> extEdges = new List<FrameEdge>();
List<IElement1D> cwEdgeCrvs = curtainPanels.ExternalEdges();
foreach (ICurve crv in cwEdgeCrvs)
{
FrameEdge frameEdge = new FrameEdge { Curve = crv };
extEdges.Add(frameEdge);
}
List<FrameEdge> allEdges = curtainPanels.SelectMany(x => x.Edges).ToList();
List<FrameEdge> extEdges = allEdges.Distinct().Where(x => allEdges.Count(y => x.ElementId() == y.ElementId()) == 1).ToList();

bHoMCurtainWall = new CurtainWall { ExternalEdges = extEdges, Openings = curtainPanels.ToList(), Name = wall.WallType.Name };

Expand All @@ -95,6 +89,45 @@ public static CurtainWall CurtainWallFromRevit(this Wall wall, RevitSettings set
}

/***************************************************/

[Description("Converts a Revit Wall to BH.oM.Facade.Elements.CurtainSystem.")]
[Input("system", "Revit CurtainSystem to be converted.")]
[Input("settings", "Revit adapter settings to be used while performing the convert.")]
[Input("refObjects", "Optional, a collection of objects already processed in the current adapter action, stored to avoid processing the same object more than once.")]
[Output("curtainWall", "BH.oM.Facade.Elements.CurtainWall resulting from converting the input Revit CurtainSystem.")]
public static CurtainWall CurtainWallFromRevit(this CurtainSystem system, RevitSettings settings = null, Dictionary<string, List<IBHoMObject>> refObjects = null)
{
if (system == null)
return null;

settings = settings.DefaultIfNull();

CurtainWall bHoMCurtainWall = refObjects.GetValue<CurtainWall>(system.Id);
if (bHoMCurtainWall != null)
return bHoMCurtainWall;

IEnumerable<oM.Facade.Elements.Opening> curtainPanels = system.ICurtainGrids().SelectMany(x => x.FacadeCurtainPanels(system.Document, settings, refObjects)).ToList();

if (curtainPanels == null || !curtainPanels.Any())
BH.Engine.Base.Compute.RecordError(String.Format("Processing of panels of Revit curtain wall failed. BHoM curtain wall without location has been returned. Revit ElementId: {0}", system.Id.IntegerValue));

// Get external edges of whole curtain wall
List<FrameEdge> allEdges = curtainPanels.SelectMany(x => x.Edges).ToList();
List<FrameEdge> extEdges = allEdges.Distinct().Where(x => allEdges.Count(y => x == y) == 1).ToList();

bHoMCurtainWall = new CurtainWall { ExternalEdges = extEdges, Openings = curtainPanels.ToList(), Name = system.FamilyTypeFullName() };

//Set identifiers, parameters & custom data
bHoMCurtainWall.SetIdentifiers(system);
bHoMCurtainWall.CopyParameters(system, settings.MappingSettings);
bHoMCurtainWall.SetProperties(system, settings.MappingSettings);

refObjects.AddOrReplace(system.Id, bHoMCurtainWall);

return bHoMCurtainWall;
}

/***************************************************/
}
}

Expand Down
5 changes: 4 additions & 1 deletion Revit_Core_Engine/Convert/Facade/FromRevit/Opening.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ public static oM.Facade.Elements.Opening FacadeOpeningFromRevit(this FamilyInsta
if (opening != null)
return opening;

FrameEdgeProperty frameEdgeProperty = familyInstance.FrameEdgeProperty(settings, refObjects);
// Extraction of frame edge property from Revit FamilyInstance is not implemented yet
BH.Engine.Base.Compute.RecordWarning($"Extraction of frame edge property from a Revit opening is currently not supported, property set to null. ElementId: {familyInstance.Id.IntegerValue}");
FrameEdgeProperty frameEdgeProperty = null;

BH.oM.Geometry.ISurface location = familyInstance.OpeningSurface(host, settings);

List<FrameEdge> edges = new List<FrameEdge>();
Expand Down
108 changes: 106 additions & 2 deletions Revit_Core_Engine/Convert/Structure/FromRevit/Profile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public static partial class Convert
/***************************************************/
/**** Public Methods ****/
/***************************************************/

[Description("Converts a Revit FamilySymbol to BH.oM.Spatial.ShapeProfiles.IProfile.")]
[Input("familySymbol", "Revit FamilySymbol to be converted.")]
[Input("settings", "Revit adapter settings to be used while performing the convert.")]
Expand Down Expand Up @@ -131,6 +131,92 @@ public static IProfile ProfileFromRevit(this FamilySymbol familySymbol, RevitSet
return profile;
}

/***************************************************/

[Description("Converts a Revit MullionType to BH.oM.Spatial.ShapeProfiles.IProfile.")]
[Input("mullionType", "Revit MullionType to be converted.")]
[Input("settings", "Revit adapter settings to be used while performing the convert.")]
[Input("refObjects", "Optional, a collection of objects already processed in the current adapter action, stored to avoid processing the same object more than once.")]
[Output("profile", "BH.oM.Spatial.ShapeProfiles.IProfile resulting from converting the input Revit MullionType.")]
public static IProfile ProfileFromRevit(this MullionType mullionType, RevitSettings settings = null, Dictionary<string, List<IBHoMObject>> refObjects = null)
{
settings = settings.DefaultIfNull();

IProfile profile = refObjects.GetValue<IProfile>(mullionType.Id);
if (profile != null)
return profile;

// The algorithm below is so convoluted because mullion type does not have a profile
// Instead, one needs to extract the geometry of one of the instances
// The instance to be queried needs to have its start (bottom) face exposed
// In simple words, it needs to be the first mullion in a chain of mullions of same type

// First, take arbitrary mullion of a given type
Document doc = mullionType.Document;
Mullion representativeMullion = new FilteredElementCollector(doc)
.OfClass(typeof(FamilyInstance))
.Where(x => x is Mullion)
.Cast<Mullion>()
.FirstOrDefault(x => x.MullionType.Id.IntegerValue == mullionType.Id.IntegerValue && x.MullionLine() != null);

if (representativeMullion == null)
{
BH.Engine.Base.Compute.RecordError($"Profile could not be extracted from a mullion. ElementId: {mullionType.Id.IntegerValue}");
return null;
}

// Find the first mullion in the chain the arbitrary one belongs to
List<Mullion> allMullionsInWall = new FilteredElementCollector(doc)
.OfClass(typeof(FamilyInstance))
.Where(x => x is Mullion)
.Cast<Mullion>()
.Where(x => x.Host.Id.IntegerValue == representativeMullion.Host.Id.IntegerValue
&& x.MullionType.Id.IntegerValue == mullionType.Id.IntegerValue)
.ToList();

BH.oM.Geometry.Line line = representativeMullion.MullionLine();
BH.oM.Geometry.Vector direction = line.Direction();
foreach (Mullion candidateMullion in allMullionsInWall)
{
// Skip mullions that are not collinear with the representative mullion
BH.oM.Geometry.Line candidateLine = candidateMullion.MullionLine();
if (candidateLine == null || !candidateLine.IsCollinear(line))
continue;

// Update the representative mullion if the candidate:
// - is collinear (check above)
// - has the same direction
// - has start before the current representative mullion, counting along the mullion direction
if ((candidateLine.Start - line.Start).DotProduct(direction) > 0)
{
representativeMullion = candidateMullion;
line = candidateLine;
}
}

// Find the instance geometry and extract profile from it
Options options = new Options();
options.DetailLevel = ViewDetailLevel.Fine;
options.IncludeNonVisibleObjects = false;
GeometryInstance instance = representativeMullion.get_Geometry(options).FirstOrDefault(x => x is GeometryInstance) as GeometryInstance;
MullionType instanceSymbol = instance.Symbol as MullionType;
if (instanceSymbol != null)
profile = instanceSymbol.FreeFormProfileFromRevit(settings);

if (profile == null)
return null;

//Set identifiers, parameters & custom data
profile.SetIdentifiers(mullionType);
profile.CopyParameters(mullionType, settings.MappingSettings);
profile.SetProperties(mullionType, settings.MappingSettings);

profile.Name = mullionType.Name;
refObjects.AddOrReplace(mullionType.Id, profile);

return profile;
}


/***************************************************/
/**** Private Methods ****/
Expand Down Expand Up @@ -610,7 +696,7 @@ private static FreeFormProfile FreeFormProfileFromRevit(this FamilySymbol family
XYZ direction;
if (familySymbol.Family.FamilyPlacementType == FamilyPlacementType.CurveDrivenStructural)
direction = XYZ.BasisX;
else if (familySymbol.Family.FamilyPlacementType == FamilyPlacementType.TwoLevelsBased)
else if (familySymbol.Family.FamilyPlacementType == FamilyPlacementType.TwoLevelsBased || familySymbol is MullionType)
direction = XYZ.BasisZ;
else
{
Expand Down Expand Up @@ -714,6 +800,13 @@ private static FreeFormProfile FreeFormProfileFromRevit(this FamilySymbol family
if (adjustment.Length() > settings.DistanceTolerance)
profileCurves = profileCurves.Select(x => x.ITranslate(adjustment)).ToList();

// If Mullion, rotate 90 degrees to match orientation of Mullions on CurtainWalls per BHoM Standard of -X is Exterior, +X is Interior
if (familySymbol is MullionType)
{
double angle = -Math.PI * 0.5;
profileCurves = profileCurves.Select(x => x.IRotate(oM.Geometry.Point.Origin, Vector.ZAxis, angle)).ToList();
}

// Check if the curves are in the horizontal plane, if not then align them.
if (familySymbol.Family.FamilyPlacementType == FamilyPlacementType.CurveDrivenStructural)
{
Expand Down Expand Up @@ -753,6 +846,17 @@ private static Solid SingleSolid(this GeometryElement geometryElement)
return solid;
}

/***************************************************/

private static BH.oM.Geometry.Line MullionLine(this Mullion mullion)
{
Curve curve = mullion?.LocationCurve;
if (curve == null)
return null;
else
return new oM.Geometry.Line { Start = curve.GetEndPoint(0).PointFromRevit(), End = curve.GetEndPoint(1).PointFromRevit() };
}


/***************************************************/
/**** Private helpers ****/
Expand Down
74 changes: 74 additions & 0 deletions Revit_Core_Engine/Create/View/Callout.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2023, the respective contributors. All rights reserved.
*
* Each contributor holds copyright over their respective contributions.
* The project versioning (Git) records all such contribution source information.
*
*
* The BHoM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* The BHoM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using Autodesk.Revit.DB;
using BH.oM.Base.Attributes;
using System.ComponentModel;

namespace BH.Revit.Engine.Core
{
public static partial class Create
{
/***************************************************/
/**** Public methods ****/
/***************************************************/

[Description("Creates the callout view visible on the referenced view.")]
[Input("referenceView", "View on which the callout will be visible. Callouts can be created on view plans, sections and detail views.")]
[Input("boundingBoxXyz", "Bounds of the callout.")]
[Input("calloutName", "Name of the callout view. If null, default name will be set.")]
[Input("viewFamily", "ViewFamily to be used by the callout view. Detail view family set as default can be used in all views except CeilingPlans and DraftingViews (in which it is recommended to use the same view family as the reference view).")]
[Input("calloutTemplateId", "Id of the template to be applied to the callout view. If null, no template will be set.")]
[Output("callout", "New callout view.")]
public static View Callout(this View referenceView, BoundingBoxXYZ boundingBoxXyz, string calloutName = null, ViewFamily viewFamily = ViewFamily.Detail, ElementId calloutTemplateId = null)
{
if (referenceView == null)
{
BH.Engine.Base.Compute.RecordError("Could not create a callout view based on null reference view.");
return null;
}
else if (boundingBoxXyz == null)
{
BH.Engine.Base.Compute.RecordError("Could not create a callout view based on a null bounding box.");
return null;
}

Document document = referenceView.Document;
ViewFamilyType viewFamilyType = Query.ViewFamilyType(document, viewFamily);

View callout = Autodesk.Revit.DB.ViewSection.CreateCallout(document, referenceView.Id, viewFamilyType.Id, boundingBoxXyz.Min, boundingBoxXyz.Max);

callout.SetViewTemplate(calloutTemplateId);

if (!string.IsNullOrEmpty(calloutName))
{
callout.SetViewName(calloutName);
}

return callout;
}

/***************************************************/

}
}

1 change: 0 additions & 1 deletion Revit_Core_Engine/Create/View/View3D.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ public static partial class Create
/**** Public Methods ****/
/***************************************************/

[PreviousVersion("6.2", "BH.Revit.Engine.Core.Create.View3D(Autodesk.Revit.DB.Document, System.String, Autodesk.Revit.DB.BoundingBoxXYZ, Autodesk.Revit.DB.ElementId, Autodesk.Revit.DB.ViewDetailLevel)")]
[Description("Creates and returns a new ISOMETRIC 3D view in the current Revit file.")]
[Input("document", "Revit current document to be processed.")]
[Input("viewName", "Optional, name of the new view.")]
Expand Down
32 changes: 32 additions & 0 deletions Revit_Core_Engine/Create/View/ViewSection/ViewSection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
using System;
using System.ComponentModel;
using BH.oM.Base.Attributes;
using System.Linq;

namespace BH.Revit.Engine.Core
{
Expand Down Expand Up @@ -78,6 +79,37 @@ public static ViewSection ViewSection(this Document document, BoundingBoxXYZ bou
result.SetViewName(viewName);
}

// In case of non-vertical section, sometimes Revit rotates the views by 90 degrees compared to the provided input
// The lines below bring the view orientation back to the desirable shape
if (1 - Math.Abs(boundingBoxXyz.Transform.BasisY.DotProduct(XYZ.BasisZ)) > BH.oM.Geometry.Tolerance.Angle)
{
document.Regenerate();

// Check if the view got rotated and action if necessary
BoundingBoxXYZ cropBox = result.CropBox;
if (Math.Abs(cropBox.Transform.BasisX.DotProduct(boundingBoxXyz.Transform.BasisX)) < BH.oM.Geometry.Tolerance.Angle)
{
// Rotate the cropbox by 90 degrees
Element cropBoxElement = new FilteredElementCollector(document).OfCategory(BuiltInCategory.OST_Viewers).FirstOrDefault(x => x != result && x.Name == result.Name);
BoundingBoxXYZ boxOfCropBoxElement = cropBoxElement.get_BoundingBox(result);
ElementTransformUtils.RotateElement(document, cropBoxElement.Id, Line.CreateUnbound((boxOfCropBoxElement.Min + boxOfCropBoxElement.Max) / 2 + boxOfCropBoxElement.Transform.Origin, boundingBoxXyz.Transform.BasisZ), Math.PI / 2);

// Realign the crop box to show the desired part of the view
cropBox = result.CropBox;
BoundingBoxXYZ bbox = new BoundingBoxXYZ();
bbox.Enabled = true;

double xDomain = cropBox.Max.Y - cropBox.Min.Y;
double yDomain = cropBox.Max.X - cropBox.Min.X;
XYZ mid = (cropBox.Min + cropBox.Max) / 2;
bbox.Transform = cropBox.Transform;
bbox.Min = new XYZ(mid.X - xDomain / 2, mid.Y - yDomain / 2, result.CropBox.Min.Z);
bbox.Max = new XYZ(mid.X + xDomain / 2, mid.Y + yDomain / 2, result.CropBox.Max.Z);

result.CropBox = bbox;
}
}

return result;
}

Expand Down
Loading

0 comments on commit a6b90eb

Please sign in to comment.