Files
speckle-sharp-connectors/Importers/Ifc/Speckle.Importers.Ifc/Ara3D.IfcParser/Extensions.cs
T
Jedd Morgan ae4b1b0ab5 Merge pull request #655 from specklesystems/jrm/ifc-collections-data-objects
Send IfcProjects, IfcSites, IfcBuildings, and IfcStoreys as Collections
2025-03-12 08:32:30 +00:00

191 lines
5.0 KiB
C#

using System.Text;
using Ara3D.Buffers;
using Speckle.Importers.Ifc.Ara3D.StepParser;
namespace Speckle.Importers.Ifc.Ara3D.IfcParser;
public static class IfcExtensions
{
public static uint AsId(this StepValue value) => value is StepUnassigned ? 0u : ((StepId)value).Id;
public static string AsString(this StepValue value) =>
value is StepString ss
? ss.AsString()
: value is StepNumber sn
? sn.Value.ToString()
: value is StepId si
? si.Id.ToString()
: value is StepSymbol ssm
? ssm.Name.ToString()
: "";
public static double AsNumber(this StepValue value) => value is StepUnassigned ? 0 : ((StepNumber)value).Value;
public static List<StepValue> AsList(this StepValue value) =>
value is StepUnassigned ? new List<StepValue>() : ((StepList)value).Values;
public static List<uint> AsIdList(this StepValue value) =>
value is StepUnassigned ? new List<uint>() : value.AsList().Select(AsId).ToList();
// Uses Latin1 encoding (aka ISO-8859-1)
// Extended characters converted using an IFC specific system
public static string AsString(this ByteSpan span) => Encoding.Latin1.GetString(span.ToSpan()).IfcToUnicode();
// https://technical.buildingsmart.org/resources/ifcimplementationguidance/string-encoding/
public static string IfcToUnicode(this string input)
{
if (!input.Contains('\\'))
return input;
var output = new StringBuilder();
var i = 0;
var length = input.Length;
while (i < length)
{
if (input[i] != '\\')
{
// Regular character, append to output
output.Append(input[i++]);
continue;
}
i++; // Move past '\'
if (i >= length)
{
output.Append('\\');
break;
}
var escapeChar = input[i++];
if (escapeChar == 'S' && i < length && input[i] == '\\')
{
i++; // Move past '\'
if (i < length)
{
var c = input[i++];
var code = c + 128;
output.Append((char)code);
}
else
{
output.Append("\\S\\");
}
continue;
}
if (escapeChar == 'X')
{
if (i < length && input[i] == '\\')
{
// Handle \X\XX escape sequence (8-bit character code)
i++; // Move past '\'
if (i + 1 < length)
{
var hex = input.Substring(i, 2);
i += 2;
var code = Convert.ToInt32(hex, 16);
output.Append((char)code);
}
else
{
output.Append("\\X\\");
}
continue;
}
// Handle extended \Xn\...\X0\ escape sequence
// Skip 'n' until the next '\'
while (i < length && input[i] != '\\')
i++;
if (i < length)
i++; // Move past '\'
// Collect hex digits until '\X0\'
var hexDigits = new StringBuilder();
while (i + 3 <= length && input.Substring(i, 3) != "\\X0")
{
hexDigits.Append(input[i++]);
}
if (i + 3 <= length && input.Substring(i, 3) == "\\X0")
{
i += 3; // Move past '\X0'
if (i < length && input[i] == '\\')
i++; // Move past '\'
var hexStr = hexDigits.ToString();
// Process hex digits in chunks of 4 (representing Unicode code points)
for (var k = 0; k + 4 <= hexStr.Length; k += 4)
{
var codeHex = hexStr.Substring(k, 4);
var code = Convert.ToInt32(codeHex, 16);
output.Append(char.ConvertFromUtf32(code));
}
continue;
}
// Invalid format, append as is
output.Append("\\X");
continue;
}
// Unrecognized escape sequence, append as is
output.Append('\\').Append(escapeChar);
}
return output.ToString();
}
public static string AsString(this StepString ss) => ss.Value.AsString();
public static object? ToJsonObject(this StepValue sv)
{
switch (sv)
{
case StepEntity stepEntity:
{
var attr = stepEntity.Attributes;
if (attr.Values.Count == 0)
return stepEntity.ToString();
if (attr.Values.Count == 1)
return attr.Values[0].ToJsonObject();
return attr.Values.Select(ToJsonObject).ToList();
}
case StepId stepId:
return stepId.Id;
case StepList stepList:
return stepList.Values.Select(ToJsonObject).ToList();
case StepNumber stepNumber:
return stepNumber.AsNumber();
case StepRedeclared stepRedeclared:
return null;
case StepString stepString:
return stepString.AsString();
case StepSymbol stepSymbol:
var tmp = stepSymbol.Name.AsString();
if (tmp == "T")
return true;
if (tmp == "F")
return false;
return tmp;
case StepUnassigned stepUnassigned:
return null;
default:
throw new ArgumentOutOfRangeException(nameof(sv));
}
}
}