Skip to content

Commit

Permalink
Merge pull request #300 from specklesystems/dimitrie/cnx-510-revit-pa…
Browse files Browse the repository at this point in the history
…rameter-exports-perfection

Dimitrie/cnx 510 revit parameter exports perfection
  • Loading branch information
didimitrie authored Oct 12, 2024
2 parents 188f23f + 1e53700 commit 0a27fc1
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Speckle.Converters.RevitShared;
public class RevitRootToSpeckleConverter : IRootToSpeckleConverter
{
private readonly IConverterManager<IToSpeckleTopLevelConverter> _toSpeckle;
private readonly ITypedConverter<DB.Element, List<Dictionary<string, object>>> _materialQuantityConverter;
private readonly ITypedConverter<DB.Element, Dictionary<string, object>> _materialQuantityConverter;
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
private readonly ParameterExtractor _parameterExtractor;
private readonly ILogger<RevitRootToSpeckleConverter> _logger;
Expand All @@ -22,7 +22,7 @@ public class RevitRootToSpeckleConverter : IRootToSpeckleConverter

public RevitRootToSpeckleConverter(
IConverterManager<IToSpeckleTopLevelConverter> toSpeckle,
ITypedConverter<DB.Element, List<Dictionary<string, object>>> materialQuantityConverter,
ITypedConverter<DB.Element, Dictionary<string, object>> materialQuantityConverter,
IConverterSettingsStore<RevitConversionSettings> converterSettings,
ParameterExtractor parameterExtractor,
ILogger<RevitRootToSpeckleConverter> logger
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,42 @@ ILogger<ParameterExtractor> logger
return CreateParameterDictionary(instanceParameterDictionary, null);
}

typeParameterDictionary = ParseParameterSet(type.Parameters);
typeParameterDictionary = ParseParameterSet(type.Parameters); // NOTE: type parameters should be ideally proxied out for a better data layout.
if (type is DB.HostObjAttributes hostObjectAttr)
{
// NOTE: this could be paired up and merged with material quantities - they're pretty much the same :/
var factor = _scalingServiceToSpeckle.ScaleLength(1);
var structureDictionary = new Dictionary<string, object?>();
var structure = hostObjectAttr.GetCompoundStructure();
var layers = structure.GetLayers();
foreach (var layer in layers)
{
if (_settingsStore.Current.Document.GetElement(layer.MaterialId) is DB.Material material)
{
structureDictionary[material.Name] = new Dictionary<string, object>()
{
["material"] = material.Name,
["function"] = layer.Function.ToString(),
["thickness"] = layer.Width * factor,
["units"] = _settingsStore.Current.SpeckleUnits
};
}
}

typeParameterDictionary["Structure"] = structureDictionary;
}

_typeParameterCache[typeId] = typeParameterDictionary;

return CreateParameterDictionary(instanceParameterDictionary, typeParameterDictionary);
}

/// <summary>
/// Internal utility to create the default parameter structure we expect all elements to have.
/// </summary>
/// <param name="instanceParams"></param>
/// <param name="typeParams"></param>
/// <returns></returns>
private Dictionary<string, object?> CreateParameterDictionary(
Dictionary<string, Dictionary<string, object?>> instanceParams,
Dictionary<string, Dictionary<string, object?>>? typeParams
Expand All @@ -90,15 +120,39 @@ ILogger<ParameterExtractor> logger
{
try
{
var (internalDefinitionName, humanReadableName, groupName, units) =
_parameterDefinitionHandler.HandleDefinition(parameter);

// NOTE: ids don't really have much meaning; if we discover the opposite, we can bring them back. See [CNX-556: All ID Parameters are send as Name](https://linear.app/speckle/issue/CNX-556/all-id-parameters-are-send-as-name)
if (internalDefinitionName.Contains("_ID"))
{
continue;
}

var value = GetValue(parameter);

var isNullOrEmpty = value == null || (value is string s && string.IsNullOrEmpty(s));

if (!_settingsStore.Current.SendParameterNullOrEmptyStrings && isNullOrEmpty)
{
continue;
}

var (internalDefinitionName, humanReadableName, groupName, units) =
_parameterDefinitionHandler.HandleDefinition(parameter);
if (value is (string typeName, string familyName)) // element type: same element, different expected values depending on the param definition
{
if (internalDefinitionName == "ELEM_FAMILY_PARAM") // Probably should be using the BUILTINPARAM whatever
{
value = familyName;
}
else if (internalDefinitionName == "ELEM_TYPE_PARAM")
{
value = typeName;
}
else
{
value = familyName + " " + typeName;
}
}

var param = new Dictionary<string, object?>()
{
Expand All @@ -107,7 +161,7 @@ ILogger<ParameterExtractor> logger
["internalDefinitionName"] = internalDefinitionName
};

if (units is string paramUnits)
if (units is not null)
{
param["units"] = units;
}
Expand Down Expand Up @@ -135,7 +189,7 @@ ILogger<ParameterExtractor> logger
return dict;
}

private readonly Dictionary<DB.ElementId, string?> _elementNameCache = new();
private readonly Dictionary<DB.ElementId, object?> _elementNameCache = new();

private object? GetValue(DB.Parameter parameter)
{
Expand All @@ -149,12 +203,30 @@ ILogger<ParameterExtractor> logger
: parameter.AsValueString();
case DB.StorageType.ElementId:
var elId = parameter.AsElementId()!;
if (_elementNameCache.TryGetValue(elId, out string? value))
if (elId == DB.ElementId.InvalidElementId)
{
return null;
}

if (_elementNameCache.TryGetValue(elId, out object? value))
{
return value;
}

var docElement = _settingsStore.Current.Document.GetElement(elId);
var docElementName = docElement?.Name ?? elId.ToString();
object? docElementName;

// Note: for element types, different params point at the same element. We're getting the right value out in the parent function
// based on what the actual built in param name is.
if (docElement is DB.ElementType elementType)
{
docElementName = (elementType.Name, elementType.FamilyName);
}
else
{
docElementName = docElement?.Name ?? null;
}

_elementNameCache[parameter.AsElementId()] = docElementName;
return docElementName;
case DB.StorageType.String:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Speckle.Converters.RevitShared.ToSpeckle;
/// Lighter converter for material quantities. It basically returns a For each material quantity available on the target element, it will return a dictionary containing: area, volume, units, material name, material class and material category.
/// POC: we need to validate this with user needs. It currently does not include material parameters or any other more complex props to ensure speedy sending of data and a lighter payload. We're though keen to re-add more data provided we can validate it.
/// </summary>
public class MaterialQuantitiesToSpeckleLite : ITypedConverter<DB.Element, List<Dictionary<string, object>>>
public class MaterialQuantitiesToSpeckleLite : ITypedConverter<DB.Element, Dictionary<string, object>>
{
private readonly ScalingServiceToSpeckle _scalingService;
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
Expand All @@ -30,10 +30,9 @@ IConverterSettingsStore<RevitConversionSettings> converterSettings
/// </summary>
/// <param name="target"></param>
/// <returns></returns>
public List<Dictionary<string, object>> Convert(DB.Element target)
public Dictionary<string, object> Convert(DB.Element target)
{
List<Dictionary<string, object>> quantities = new();

Dictionary<string, object> quantities = new();
if (target.Category.HasMaterialQuantities)
{
foreach (DB.ElementId matId in target.GetMaterialIds(false))
Expand All @@ -55,7 +54,7 @@ public List<Dictionary<string, object>> Convert(DB.Element target)
materialQuantity["materialName"] = material.Name;
materialQuantity["materialCategory"] = material.MaterialCategory;
materialQuantity["materialClass"] = material.MaterialClass;
quantities.Add(materialQuantity);
quantities[material.Name] = materialQuantity;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,6 @@ public override RevitFootprintRoof Convert(FootPrintRoof target)
out var topLevel
);

//POC: CNX-9403 can be null if the sides have different slopes.
//We currently don't validate the success or failure of this TryGet as it's not necessary, but will be once we start the above ticket.
_parameterValueExtractor.TryGetValueAsDouble(target, DB.BuiltInParameter.ROOF_SLOPE, out var slope);

var elementType = (ElementType)target.Document.GetElement(target.GetTypeId());
List<Speckle.Objects.Geometry.Mesh> displayValue = _displayValueExtractor.GetDisplayValue(target);

Expand All @@ -69,7 +65,6 @@ out var topLevel
family = elementType.FamilyName,
level = _levelConverter.Convert(baseLevel),
cutOffLevel = topLevel is not null ? _levelConverter.Convert(topLevel) : null,
slope = slope,
displayValue = displayValue,
units = _converterSettings.Current.SpeckleUnits
};
Expand Down

0 comments on commit 0a27fc1

Please sign in to comment.