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
9 changes: 0 additions & 9 deletions src/NwcIO/Model/Grids/NwcGridSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ public sealed class NwcGridSystem

public List<NwcGridLine> Lines { get; } = [];

private List<NwcGridIntersection>? _cachedIntersections;

public NwcGridSystem() { }

public NwcGridSystem(string name)
Expand All @@ -33,7 +31,6 @@ public NwcGridLine AddLine(NwcGridLine line)
{
ArgumentNullException.ThrowIfNull(line);
Lines.Add(line);
_cachedIntersections = null;
return line;
}

Expand All @@ -51,11 +48,6 @@ public NwcGridLine AddLine(string name, Vector3d start, Vector3d end, double ele
/// </summary>
public IReadOnlyList<NwcGridIntersection> GetIntersections()
{
if (_cachedIntersections is not null)
{
return _cachedIntersections;
}

var result = new List<NwcGridIntersection>();

for (int i = 0; i < Lines.Count; i++)
Expand All @@ -78,7 +70,6 @@ public IReadOnlyList<NwcGridIntersection> GetIntersections()
}
}

_cachedIntersections = result;
return result;
}

Expand Down
11 changes: 10 additions & 1 deletion src/NwcIO/Model/NwcMesh.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,15 @@ public sealed class NwcMesh
public BoundingBox3d? BoundingBox { get; init; }

/// <summary>Optional 3D world transformation associated with this mesh instance.</summary>
public NwcTransform? Transform { get; set; }
public NwcTransform? Transform
{
get => _transform;
set
{
_transform = value;
_cachedWorldBounds = null;
}
}

/// <summary>Surface material and color assigned to this mesh.</summary>
public NwcMaterial? Material { get; set; }
Expand All @@ -34,6 +42,7 @@ public sealed class NwcMesh
/// <summary>Number of triangle faces in this mesh.</summary>
public int TriangleCount => Indices.Count / 3;

private NwcTransform? _transform;
private BoundingBox3d? _cachedWorldBounds;

/// <summary>
Expand Down
62 changes: 62 additions & 0 deletions tests/NwcIO.Tests/GridSystemTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,68 @@ namespace NwcIO.Tests;

public sealed class GridSystemTests
{
[Theory]
[InlineData(false)]
[InlineData(true)]
public void GridSystem_AfterEndpointEdit_UpdatesIntersectionsAndLocation(bool editStart)
{
var grids = new NwcGridSystem();
var line = grids.AddLine("A", new Vector3d(0, -10, 0), new Vector3d(0, 10, 0));
grids.AddLine("B", new Vector3d(5, -10, 0), new Vector3d(5, 10, 0));
grids.AddLine("1", new Vector3d(-10, 0, 0), new Vector3d(20, 0, 0));
Assert.Equal(0.0, grids.GetIntersections()[0].IntersectionPoint.X);
Assert.Equal("B / 1", grids.FormatLocation(new Vector3d(5, 0, 0)));

if (editStart)
{
line.Start = new Vector3d(20, -10, 0);
}
else
{
line.End = new Vector3d(20, 10, 0);
}

var intersection = Assert.Single(grids.GetIntersections(), i => i.Line1Name == "A" && i.Line2Name == "1");
Assert.Equal(10.0, intersection.IntersectionPoint.X);
Assert.Equal(0.0, intersection.IntersectionPoint.Y);
Assert.Equal("A / 1", grids.FormatLocation(new Vector3d(10, 0, 0)));
}

[Fact]
public void GridSystem_AfterDirectLineAddition_UpdatesIntersectionsAndLocation()
{
var grids = new NwcGridSystem();
grids.AddLine("A", new Vector3d(0, -10, 0), new Vector3d(0, 10, 0));
grids.AddLine("1", new Vector3d(-10, 0, 0), new Vector3d(30, 0, 0));
Assert.Single(grids.GetIntersections());
Assert.Equal("A (+20.00m) / 1", grids.FormatLocation(new Vector3d(20, 0, 0)));

grids.Lines.Add(new NwcGridLine("B", new Vector3d(20, -10, 0), new Vector3d(20, 10, 0)));

Assert.Equal(2, grids.GetIntersections().Count);
var intersection = Assert.Single(grids.GetIntersections(), i => i.Line2Name == "B");
Assert.Equal(20.0, intersection.IntersectionPoint.X);
Assert.Equal("1 / B", grids.FormatLocation(new Vector3d(20, 0, 0)));
}

[Fact]
public void GridSystem_AfterDirectLineRemoval_UpdatesIntersectionsAndLocation()
{
var grids = new NwcGridSystem();
var line = grids.AddLine("A", new Vector3d(0, -10, 0), new Vector3d(0, 10, 0));
grids.AddLine("B", new Vector3d(20, -10, 0), new Vector3d(20, 10, 0));
grids.AddLine("1", new Vector3d(-10, 0, 0), new Vector3d(30, 0, 0));
Assert.Equal(2, grids.GetIntersections().Count);
Assert.Equal("A / 1", grids.FormatLocation(new Vector3d(0, 0, 0)));

Assert.True(grids.Lines.Remove(line));

var intersection = Assert.Single(grids.GetIntersections());
Assert.Equal("B / 1", intersection.DisplayName);
Assert.Equal(20.0, intersection.IntersectionPoint.X);
Assert.Equal("B (-20.00m) / 1", grids.FormatLocation(new Vector3d(0, 0, 0)));
}

[Fact]
public void GridLine_CalculatesDistanceAndIntersection()
{
Expand Down
50 changes: 50 additions & 0 deletions tests/NwcIO.Tests/MeshParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,56 @@ namespace NwcIO.Tests;

public class MeshParserTests
{
[Theory]
[InlineData(false)]
[InlineData(true)]
public void Mesh_GetWorldBoundingBox_AfterTransformAssignment_UsesCurrentTransform(bool initiallyTransformed)
{
var mesh = new NwcMesh
{
Id = 1,
Vertices = [new Vector3d(0, 0, 0), new Vector3d(1, 2, 3)],
Indices = [],
BoundingBox = new BoundingBox3d(new Vector3d(0, 0, 0), new Vector3d(1, 2, 3)),
Transform = initiallyTransformed
? new NwcTransform { Id = 1, Translation = new Vector3d(10, 20, 30) }
: null,
};

Assert.Equal(initiallyTransformed ? 10.0 : 0.0, mesh.GetWorldBoundingBox().Min.X);

mesh.Transform = new NwcTransform { Id = 2, Translation = new Vector3d(100, 200, 300) };

var bounds = mesh.GetWorldBoundingBox();
var vertices = mesh.GetWorldVertices();
Assert.Equal(new Vector3d(100, 200, 300), bounds.Min);
Assert.Equal(new Vector3d(101, 202, 303), bounds.Max);
Assert.Equal(vertices[0], bounds.Min);
Assert.Equal(vertices[1], bounds.Max);
}

[Fact]
public void Mesh_GetWorldBoundingBox_AfterTransformRemoval_UsesLocalBounds()
{
var mesh = new NwcMesh
{
Id = 1,
Vertices = [new Vector3d(0, 0, 0), new Vector3d(1, 2, 3)],
Indices = [],
BoundingBox = new BoundingBox3d(new Vector3d(0, 0, 0), new Vector3d(1, 2, 3)),
Transform = new NwcTransform { Id = 1, Translation = new Vector3d(100, 200, 300) },
};

Assert.Equal(100.0, mesh.GetWorldBoundingBox().Min.X);

mesh.Transform = null;

var bounds = mesh.GetWorldBoundingBox();
Assert.Equal(mesh.BoundingBox.Value, bounds);
Assert.Equal(mesh.GetWorldVertices()[0], bounds.Min);
Assert.Equal(mesh.GetWorldVertices()[1], bounds.Max);
}

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