Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dimitrie/cnx 510 revit parameter exports perfection #300

Merged
merged 10 commits into from
Oct 12, 2024
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
Loading