Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions src/NwcIO/Internal/Export/IfcExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ public static void Write(
// Export properties if requested
if (options.ExportProperties)
{
WriteMeshProperties(w, elemId, mesh, ownerHistoryId);
WriteMeshProperties(w, elemId, mesh, ownerHistoryId, vertices, scale);
}
}

Expand Down Expand Up @@ -437,7 +437,7 @@ public static void WriteAssembly(

if (options.ExportProperties)
{
WriteMeshProperties(w, elemId, mesh, ownerHistoryId);
WriteMeshProperties(w, elemId, mesh, ownerHistoryId, vertices, scale);
}
}

Expand Down Expand Up @@ -587,7 +587,13 @@ private static int AddIfcProduct(
};
}

private static void WriteMeshProperties(StepWriter w, int elementId, NwcMesh mesh, int ownerHistoryId)
private static void WriteMeshProperties(
StepWriter w,
int elementId,
NwcMesh mesh,
int ownerHistoryId,
IReadOnlyList<Vector3d> vertices,
double scale)
{
var propIds = new List<int>();

Expand All @@ -610,14 +616,21 @@ private static void WriteMeshProperties(StepWriter w, int elementId, NwcMesh mes
propIds.Add(pMat);
}

if (mesh.BoundingBox is { } bbox && !bbox.IsEmpty)
var bounds = BoundingBox3d.Empty;
foreach (var vertex in vertices)
{
bounds = bounds.Union(vertex);
}

if (!bounds.IsEmpty)
{
int pDimX = w.Add("IFCPROPERTYSINGLEVALUE",
$"'Length_X',$,IFCREAL({StepWriter.Real(bbox.Size.X)}),$");
int pDimY = w.Add("IFCPROPERTYSINGLEVALUE",
$"'Width_Y',$,IFCREAL({StepWriter.Real(bbox.Size.Y)}),$");
int pDimZ = w.Add("IFCPROPERTYSINGLEVALUE",
$"'Height_Z',$,IFCREAL({StepWriter.Real(bbox.Size.Z)}),$");
var size = bounds.Size * scale;
var pDimX = w.Add("IFCPROPERTYSINGLEVALUE",
$"'Length_X',$,IFCLENGTHMEASURE({StepWriter.Real(size.X)}),$");
var pDimY = w.Add("IFCPROPERTYSINGLEVALUE",
$"'Width_Y',$,IFCLENGTHMEASURE({StepWriter.Real(size.Y)}),$");
var pDimZ = w.Add("IFCPROPERTYSINGLEVALUE",
$"'Height_Z',$,IFCLENGTHMEASURE({StepWriter.Real(size.Z)}),$");
propIds.Add(pDimX);
propIds.Add(pDimY);
propIds.Add(pDimZ);
Expand Down
92 changes: 92 additions & 0 deletions tests/NwcIO.Tests/IfcExportTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using NwcIO.Model;
using NwcIO.Model.Sectioning;
using Xunit;

namespace NwcIO.Tests;
Expand Down Expand Up @@ -217,6 +218,97 @@ public void SaveIfc_WithMetreUnits_ScalesCoordinates()
Assert.Contains("1.", ifcM);
}

[Theory]
[InlineData(false, NwcIfcLengthUnit.Millimetre, true, "1000.")]
[InlineData(true, NwcIfcLengthUnit.Millimetre, true, "1000.")]
[InlineData(false, NwcIfcLengthUnit.Metre, true, "1.")]
[InlineData(true, NwcIfcLengthUnit.Metre, true, "1.")]
[InlineData(false, NwcIfcLengthUnit.Metre, false, "1000.")]
[InlineData(true, NwcIfcLengthUnit.Metre, false, "1000.")]
public void SaveIfc_Dimensions_UseExportedLengthUnits(
bool assembly, NwcIfcLengthUnit unit, bool convert, string expected)
{
var doc = CreateSyntheticDocument();
var ifc = ExportDimensions(doc, assembly, new NwcIfcExportOptions
{
LengthUnit = unit,
ConvertMmToMetres = convert,
});

AssertDimensions(ifc, expected, expected, expected);
}

[Theory]
[InlineData(false, true)]
[InlineData(true, true)]
[InlineData(false, false)]
[InlineData(true, false)]
public void SaveIfc_Dimensions_UseSelectedCoordinateSpace(bool assembly, bool worldCoordinates)
{
var doc = CreateSyntheticDocument();
doc.Meshes[0].Transform = new NwcTransform
{
Id = 1,
Translation = new Vector3d(5000, -3000, 2000),
// Rotate the cube 45 degrees about Z, increasing its X and Y extents.
Rotation = new Quaterniond(0, 0, Math.Sin(Math.PI / 8), Math.Cos(Math.PI / 8)),
};
var ifc = ExportDimensions(doc, assembly, new NwcIfcExportOptions
{
WorldCoordinates = worldCoordinates,
LengthUnit = NwcIfcLengthUnit.Metre,
ConvertMmToMetres = true,
});

foreach (var name in new[] { "Length_X", "Width_Y", "Height_Z" })
{
var match = System.Text.RegularExpressions.Regex.Match(
ifc, $@"IFCPROPERTYSINGLEVALUE\('{name}',\$,IFCLENGTHMEASURE\(([^)]+)\),\$\)");
Assert.True(match.Success, $"Missing length measure for {name}");
var actual = double.Parse(match.Groups[1].Value, System.Globalization.CultureInfo.InvariantCulture);
var expected = worldCoordinates && name != "Height_Z" ? Math.Sqrt(2) : 1;
Assert.Equal(expected, actual, 6);
}
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public void SaveIfc_Dimensions_RetainPostClipExtents(bool assembly)
{
var doc = CreateSyntheticDocument();
var ifc = ExportDimensions(doc, assembly, new NwcIfcExportOptions
{
LengthUnit = NwcIfcLengthUnit.Metre,
ConvertMmToMetres = true,
SectionPlanes = new[] { new NwcSectionPlane { Normal = new Vector3d(1, 0, 0), Distance = 250 } },
});

AssertDimensions(ifc, "0.75", "1.", "1.");
}

private static string ExportDimensions(NwcDocument doc, bool assembly, NwcIfcExportOptions options)
{
using var sw = new StringWriter();
if (assembly)
{
NwcAssembly.Create("Dimensions", doc).SaveIfc(sw, options);
}
else
{
doc.SaveIfc(sw, options);
}

return sw.ToString();
}

private static void AssertDimensions(string ifc, string x, string y, string z)
{
Assert.Contains($"IFCPROPERTYSINGLEVALUE('Length_X',$,IFCLENGTHMEASURE({x}),$)", ifc);
Assert.Contains($"IFCPROPERTYSINGLEVALUE('Width_Y',$,IFCLENGTHMEASURE({y}),$)", ifc);
Assert.Contains($"IFCPROPERTYSINGLEVALUE('Height_Z',$,IFCLENGTHMEASURE({z}),$)", ifc);
}

[Fact]
public void SaveIfc_SharedVerticesTrue_EmitsExactlyUniqueMeshPoints()
{
Expand Down
Loading