Skip to content

Commit

Permalink
feat(revit): send builtInCategories (#2987)
Browse files Browse the repository at this point in the history
* Revert PR #2910

* feat(revit): adds the builtInCategory when sending and uses that for type mapping on receive

* feat(revit): pass a clean name to the type mapper

* feat(revit): use the builtInCategory when falling back on DirectShape

* chore: missing {}

* chore: removes not needed parsing
  • Loading branch information
teocomi authored Oct 10, 2023
1 parent 793094e commit 195053d
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB;
using RevitSharedResources.Extensions.SpeckleExtensions;
using RevitSharedResources.Helpers;
using RevitSharedResources.Interfaces;
using Speckle.Core.Models;
using OSG = Objects.Structural.Geometry;
using BE = Objects.BuiltElements;
using BER = Objects.BuiltElements.Revit;
using RevitSharedResources.Helpers;
using OSG = Objects.Structural.Geometry;
using SHC = RevitSharedResources.Helpers.Categories;
using RevitSharedResources.Extensions.SpeckleExtensions;

namespace Objects.Converter.Revit
{
Expand Down Expand Up @@ -90,8 +90,14 @@ public IRevitCategoryInfo GetRevitCategoryInfo(Base @base)
return categoryInfo;
}

var instanceCategory = @base["category"] as string;
if (string.IsNullOrEmpty(instanceCategory)) return categoryInfo;
//2.16 onwards we check for "builtInCategory"
var instanceCategory = @base["builtInCategory"] as string;
//pre 2.16 we used the inconsistent, display value "category"
if (string.IsNullOrEmpty(instanceCategory))
instanceCategory = @base["category"] as string;

if (string.IsNullOrEmpty(instanceCategory))
return categoryInfo;

var newCategoryInfo = GetRevitCategoryInfo(instanceCategory);

Expand Down Expand Up @@ -132,21 +138,33 @@ public IRevitCategoryInfo GetRevitCategoryInfo(string categoryName)
}
#endregion



private IRevitCategoryInfo? GetCategoryInfoForObjectWithExactName(string unformattedCatName)
{
var revitCat = revitDocumentAggregateCache
.GetOrInitializeWithDefaultFactory<Category>()
.TryGet(unformattedCatName);

if (revitCat == null) return null;
var bic = BuiltInCategory.INVALID;
string formattedName = "";
// 2.16 onwards we're passing the "builtInCategory" string
if (unformattedCatName.StartsWith("OST"))
{
if (!Enum.TryParse(unformattedCatName, out bic))
{
return null;
}
formattedName = unformattedCatName.Replace("OST_", "");
}
// pre 2.16 we're passing the "category" string
else
{
var revitCat = revitDocumentAggregateCache
.GetOrInitializeWithDefaultFactory<Category>()
.TryGet(unformattedCatName);

#if REVIT2020 || REVIT2021 || REVIT2022
var bic = (BuiltInCategory)revitCat.Id.IntegerValue;
#else
var bic = revitCat.BuiltInCategory;
#endif
if (revitCat == null) return null;

var formattedName = CategoryNameFormatted(unformattedCatName);
bic = Categories.GetBuiltInCategory(revitCat);
formattedName = CategoryNameFormatted(unformattedCatName);
}

return revitDocumentAggregateCache
.GetOrInitializeWithDefaultFactory<IRevitCategoryInfo>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,31 +34,25 @@ public static RevitCategory GetSchemaBuilderCategoryFromBuiltIn(string builtInCa
/// </summary>
/// <param name="c">The RevitCategory to convert</param>
/// <returns>The name of the built-in category that corresponds to the input RevitCategory</returns>
public static bool GetBuiltInCategoryFromRevitCategory(RevitCategory c, out BuiltInCategory bic)
public static string GetBuiltInFromSchemaBuilderCategory(RevitCategory c)
{
var name = Enum.GetName(typeof(RevitCategory), c);
var builtInName = $"OST_{name}";
return Enum.TryParse(builtInName, out bic);
return $"OST_{name}";
}

public static bool GetBuiltInCategoryFromRevitCategory(RevitFamilyCategory c, out BuiltInCategory bic)
public static string GetBuiltInFromSchemaBuilderCategory(RevitFamilyCategory c)
{
var name = Enum.GetName(typeof(RevitFamilyCategory), c);
var builtInName = $"OST_{name}";
return Enum.TryParse(builtInName, out bic);
return $"OST_{name}";
}

/// <summary>
/// Returns the corresponding RevitCategory enum from a specific BuiltInCategory
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
/// <remarks>This method should mirror <see cref="GetBuiltInCategoryFromRevitCategory"/> logic for built in categories </remarks>
public static bool GetRevitCategoryFromBuiltInCategory(BuiltInCategory bic, out RevitCategory c)
public static BuiltInCategory GetBuiltInCategory(Category category)
{
var name = Enum.GetName(typeof(BuiltInCategory), bic);
var revitName = name.Split('_').Last();
return Enum.TryParse<RevitCategory>(revitName, out c);
#if REVIT2020 || REVIT2021 || REVIT2022
return (BuiltInCategory)category.Id.IntegerValue;
#else
return category.BuiltInCategory;
#endif
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,12 @@ private bool ShouldConvertHostedElement(DB.Element element, DB.Element host, Bas
var elementId = element.Id;
if (!hostedElementIds.Contains(elementId))
{
extraProps["speckleHost"] = new Base() { applicationId = host.UniqueId, ["category"] = host.Category.Name, };
extraProps["speckleHost"] = new Base()
{
applicationId = host.UniqueId,
["category"] = host.Category.Name,
["builtInCategory"] = Categories.GetBuiltInCategory(host.Category)
};
}
else
return false;
Expand Down Expand Up @@ -251,21 +256,22 @@ public void GetAllRevitParamsAndIds(Base speckleElement, DB.Element revitElement

speckleElement["worksetId"] = revitElement.WorksetId.ToString();


// assign the category if it is null
// WARN: DirectShapes have a `category` prop of type `RevitCategory` (enum), NOT `string`. This is the only exception as of 2.16.
// In all other cases this should be the display value string (localized name) of the catogory
// If the null check is removed, the DirectShape case needs to be handled.
var category = revitElement.Category;
if (speckleElement["category"] is null && category is not null)
{
var categoryName = category.Name;
// we should use RevitCategory values for BuiltInCategory strings where possible (revit 2023+)
// different BuiltInCategory may have the same name, eg "OST_Railings" and "OST_StairsRailing" both have a Category name of "Railing"
#if !(REVIT2020 || REVIT2021 || REVIT2022)
if (Categories.GetRevitCategoryFromBuiltInCategory(category.BuiltInCategory, out RevitCategory c))
categoryName = c.ToString();
#endif
speckleElement["category"] = categoryName;
speckleElement["category"] = category.Name;
}
// from 2.16 onward we're also passing the full BuiltInCategory for better handling on receive
//TODO: move this to a typed property, define full list of categories in Objects
BuiltInCategory builtInCategory = Categories.GetBuiltInCategory(category);
speckleElement["builtInCategory"] = builtInCategory.ToString();



//NOTE: adds the quantities of all materials to an element
var qs = MaterialQuantitiesToSpeckle(revitElement, speckleElement["units"] as string);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,9 @@ private static void AssignCategoryToFamilyDoc(DB.Document famDoc, string? catego
if (!success)
cat = RevitFamilyCategory.GenericModel;

// Get the BuiltInCategory corresponding to the RevitFamilyCategory
success = Categories.GetBuiltInCategoryFromRevitCategory(cat, out DB.BuiltInCategory bic);
// Get the BuiltInCategory corresponding to the RevitCategory
var catName = Categories.GetBuiltInFromSchemaBuilderCategory(cat);
success = Enum.TryParse(catName, out DB.BuiltInCategory bic);
if (!success)
bic = DB.BuiltInCategory.OST_GenericModel;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,13 @@ public ApplicationObject TryDirectShapeToNative(DirectShape o, ToNativeMeshSetti
}
}

public ApplicationObject TryDirectShapeToNative(Brep brep, ToNativeMeshSettingEnum fallbackSetting, RevitCategory cat = RevitCategory.GenericModel)
public ApplicationObject TryDirectShapeToNative(Brep brep, ToNativeMeshSettingEnum fallbackSetting, RevitCategory cat = RevitCategory.GenericModel)
{
DirectShape ds = new(
$"Brep {brep.applicationId ?? brep.id}",
cat,
new List<Base> { brep }) { applicationId = brep.applicationId, id = brep.id };
new List<Base> { brep })
{ applicationId = brep.applicationId, id = brep.id };
return TryDirectShapeToNative(ds, fallbackSetting);
}

Expand All @@ -60,10 +61,11 @@ public ApplicationObject TryDirectShapeToNative(Mesh mesh, ToNativeMeshSettingEn
DirectShape ds = new(
$"Mesh {mesh.applicationId ?? mesh.id}",
cat,
new List<Base> { mesh }) { applicationId = mesh.applicationId, id = mesh.id };
new List<Base> { mesh })
{ applicationId = mesh.applicationId, id = mesh.id };
return TryDirectShapeToNative(ds, fallbackSetting);
}

public ApplicationObject TryDirectShapeToNative(ApplicationObject appObj, List<Mesh> meshes, ToNativeMeshSettingEnum fallbackSetting, RevitCategory cat = RevitCategory.GenericModel)
{
if (meshes.Count == 0)
Expand All @@ -75,11 +77,12 @@ public ApplicationObject TryDirectShapeToNative(ApplicationObject appObj, List<M
var ds = new DirectShape(
$"{appObj.Descriptor.Split(':').LastOrDefault() ?? "Meshes"} {appObj.applicationId}",
cat,
meshes.Cast<Base>().ToList()) { applicationId = appObj.applicationId, id = appObj.OriginalId };

meshes.Cast<Base>().ToList())
{ applicationId = appObj.applicationId, id = appObj.OriginalId };

return TryDirectShapeToNative(ds, fallbackSetting);
}

/// <summary>
/// The default DirectShape conversion method. Will return a Revit DirectShape with the containing geometry.
/// </summary>
Expand Down Expand Up @@ -148,17 +151,21 @@ public ApplicationObject DirectShapeToNative(DirectShape speckleDs, ToNativeMesh
return appObj;
}

DB.Category cat = null;
if ((int)speckleDs.category == -1)
speckleDs.category = RevitCategory.GenericModel;
if (Categories.GetBuiltInCategoryFromRevitCategory(speckleDs.category, out BuiltInCategory bic))
{
cat = Doc.Settings.Categories.get_Item(bic);
}
if (cat is null)
//from 2.16 onwards use the builtInCategory field for direct shape fallback
BuiltInCategory bic = BuiltInCategory.OST_GenericModel;
if (!BuiltInCategory.TryParse(speckleDs["builtInCategory"] as string, out bic))
{
cat = Doc.Settings.Categories.get_Item(BuiltInCategory.OST_GenericModel); // default to generic model
//pre 2.16 or coming from grasshopper, using the enum
//TODO: move away from enum logic
if ((int)speckleDs.category != -1)
{
var bicName = Categories.GetBuiltInFromSchemaBuilderCategory(speckleDs.category);
_ = BuiltInCategory.TryParse(bicName, out bic);
}
}

var cat = Doc.Settings.Categories.get_Item(bic);

try
{
var revitDs = DB.DirectShape.CreateElement(Doc, cat.Id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public ApplicationObject DisplayableObjectToNative(Base obj)
var displayValue = obj.TryGetDisplayValue() ?? throw new Exception("Display value was empty or null");

var parameters = obj.TryGetParameters<Parameter>();
var category = GetSpeckleObjectCategory(obj);
var category = GetSpeckleObjectBuiltInCategory(obj);

// Create a temp DirectShape and use the DirectShape conversion routine
var ds = new DirectShape(name, category, displayValue.ToList(), parameters?.ToList())
Expand All @@ -43,10 +43,10 @@ public ApplicationObject DisplayableObjectToNative(Base obj)
var displayValue = instance.GetTransformedGeometry().Cast<Base>();

var parameters = obj.TryGetParameters<Parameter>();
var category = GetSpeckleObjectCategory(obj);
var builtInCategory = GetSpeckleObjectBuiltInCategory(obj);

// Create a temp DirectShape and use the DirectShape conversion routine
var ds = new DirectShape(name, category, displayValue.ToList(), parameters?.ToList());
var ds = new DirectShape(name, builtInCategory, displayValue.ToList(), parameters?.ToList());
return DirectShapeToNative(ds, ToNativeMeshSettingEnum.Default);
}
else
Expand All @@ -55,45 +55,50 @@ public ApplicationObject DisplayableObjectToNative(Base obj)
}
}

public RevitCategory GetSpeckleObjectCategory(Base @object)
public string GetSpeckleObjectBuiltInCategory(Base @object)
{
if (Enum.TryParse<RevitCategory>(@object["category"] as string, out RevitCategory category))
return category;
else
//from 2.16 onwards we're passing the BuiltInCategory on every object
if (@object["builtInCategory"] is not null)
return @object["builtInCategory"] as string;

if (RevitCategory.TryParse(@object["category"] as string, out RevitCategory category))
{
switch (@object)
{
case BE.Beam _:
case BE.Brace _:
case BE.TeklaStructures.TeklaContourPlate _:
return RevitCategory.StructuralFraming;
case BE.TeklaStructures.Bolts _:
return RevitCategory.StructConnectionBolts;
case BE.TeklaStructures.Welds _:
return RevitCategory.StructConnectionWelds;
case BE.Floor _:
return RevitCategory.Floors;
case BE.Ceiling _:
return RevitCategory.Ceilings;
case BE.Column _:
return RevitCategory.Columns;
case BE.Pipe _:
return RevitCategory.PipeSegments;
case BE.Rebar _:
return RevitCategory.Rebar;
case BE.Topography _:
return RevitCategory.Topography;
case BE.Wall _:
return RevitCategory.Walls;
case BE.Roof _:
return RevitCategory.Roofs;
case BE.Duct _:
return RevitCategory.DuctSystem;
case BE.CableTray _:
return RevitCategory.CableTray;
default:
return RevitCategory.GenericModel;
}
return Categories.GetBuiltInFromSchemaBuilderCategory(category);
}

switch (@object)
{
case BE.Beam _:
case BE.Brace _:
case BE.TeklaStructures.TeklaContourPlate _:
return BuiltInCategory.OST_StructuralFraming.ToString();
case BE.TeklaStructures.Bolts _:
return BuiltInCategory.OST_StructConnectionBolts.ToString();
case BE.TeklaStructures.Welds _:
return BuiltInCategory.OST_StructConnectionWelds.ToString();
case BE.Floor _:
return BuiltInCategory.OST_Floors.ToString();
case BE.Ceiling _:
return BuiltInCategory.OST_Ceilings.ToString();
case BE.Column _:
return BuiltInCategory.OST_Columns.ToString();
case BE.Pipe _:
return BuiltInCategory.OST_PipeSegments.ToString();
case BE.Rebar _:
return BuiltInCategory.OST_Rebar.ToString();
case BE.Topography _:
return BuiltInCategory.OST_Topography.ToString();
case BE.Wall _:
return BuiltInCategory.OST_Walls.ToString();
case BE.Roof _:
return BuiltInCategory.OST_Roofs.ToString();
case BE.Duct _:
return BuiltInCategory.OST_DuctSystem.ToString();
case BE.CableTray _:
return BuiltInCategory.OST_CableTray.ToString();
default:
return BuiltInCategory.OST_GenericModel.ToString();

}
}
}
Loading

0 comments on commit 195053d

Please sign in to comment.