diff --git a/CadRevealComposer.Tests/Utils/MeshTools/MeshToolsTests.cs b/CadRevealComposer.Tests/Utils/MeshTools/MeshToolsTests.cs index 734f9f311..ffc5b712e 100644 --- a/CadRevealComposer.Tests/Utils/MeshTools/MeshToolsTests.cs +++ b/CadRevealComposer.Tests/Utils/MeshTools/MeshToolsTests.cs @@ -11,7 +11,7 @@ public class MeshToolsTests public void Test_DeduplicateVertices() { // Sample data is not a valid mesh, but tests logic - var mesh = new Mesh(new[] { Vector3.One, Vector3.Zero, Vector3.One }, new uint[] { 0, 1, 2, 1, 0, 2 }, 0); + var mesh = new Mesh(new[] { Vector3.One, Vector3.Zero, Vector3.One }, new int[] { 0, 1, 2, 1, 0, 2 }, 0); var newMesh = MeshTools.DeduplicateVertices(mesh); Assert.That(newMesh.Vertices, Is.EquivalentTo(new[] { Vector3.One, Vector3.Zero })); @@ -23,7 +23,7 @@ public void Test_DeduplicateVertices() public void EnsureDeduplicateVertices_DoesNothing_WhenNoVertsAreDuplicates() { // Sample data is not a valid mesh, but tests logic - var inputMesh = new Mesh(new[] { Vector3.One, Vector3.Zero, -Vector3.One }, new uint[] { 0, 1, 2, 1, 0, 2 }, 0); + var inputMesh = new Mesh(new[] { Vector3.One, Vector3.Zero, -Vector3.One }, new int[] { 0, 1, 2, 1, 0, 2 }, 0); var newMesh = MeshTools.DeduplicateVertices(inputMesh); Assert.That(newMesh, Is.Not.SameAs(inputMesh), "Expected not to be reference equal"); Assert.That(newMesh, Is.EqualTo(inputMesh), "Expected to be deep equal"); diff --git a/CadRevealComposer/Tessellation/Mesh.cs b/CadRevealComposer/Tessellation/Mesh.cs index 9b547032c..5ff6cf48f 100644 --- a/CadRevealComposer/Tessellation/Mesh.cs +++ b/CadRevealComposer/Tessellation/Mesh.cs @@ -16,12 +16,12 @@ public class Mesh : IEquatable /// public Vector3[] Vertices => _vertices; - public uint[] Indices => _indices; + public int[] Indices => _indices; public int TriangleCount => _indices.Length / 3; private readonly Vector3[] _vertices; - private readonly uint[] _indices; + private readonly int[] _indices; public Mesh(IReadOnlyList vertexes, int[] indexes, float error) { @@ -33,7 +33,7 @@ public Mesh(IReadOnlyList vertexes, int[] indexes, float error) _vertices[i] = new Vector3(vertexes[i * 3], vertexes[i * 3 + 1], vertexes[i * 3 + 2]); } - _indices = new uint[indexes.Length]; + _indices = new int[indexes.Length]; Array.Copy(indexes, _indices, indexes.Length); } @@ -44,7 +44,7 @@ public Mesh(IReadOnlyList vertexes, int[] indexes, float error) /// /// /// - public Mesh(Vector3[] vertices, uint[] indices, float error) + public Mesh(Vector3[] vertices, int[] indices, float error) { Error = error; _vertices = vertices; diff --git a/CadRevealComposer/Utils/MeshTools/MeshTools.cs b/CadRevealComposer/Utils/MeshTools/MeshTools.cs index 645622eba..c7652fcb7 100644 --- a/CadRevealComposer/Utils/MeshTools/MeshTools.cs +++ b/CadRevealComposer/Utils/MeshTools/MeshTools.cs @@ -17,20 +17,20 @@ public static class MeshTools public static Mesh DeduplicateVertices(Mesh input) { var comparer = new XyzVector3EqualityComparer(); - var alreadyFoundVerticesToIndexMap = new Dictionary(comparer); + var alreadyFoundVerticesToIndexMap = new Dictionary(comparer); var newVertices = new List(); var indicesCopy = input.Indices.ToArray(); // The index in the oldVertexIndexToNewIndexRemap array is the old index, and the value is the new index. (Think of it as a dict) - var oldVertexIndexToNewIndexRemap = new uint[input.Vertices.Count()]; + var oldVertexIndexToNewIndexRemap = new int[input.Vertices.Count()]; for (uint i = 0; i < input.Vertices.Count(); i++) { var vertex = input.Vertices[(int)i]; - if (!alreadyFoundVerticesToIndexMap.TryGetValue(vertex, out uint newIndex)) + if (!alreadyFoundVerticesToIndexMap.TryGetValue(vertex, out int newIndex)) { - newIndex = (uint)newVertices.Count; + newIndex = newVertices.Count; newVertices.Add(vertex); alreadyFoundVerticesToIndexMap.Add(vertex, newIndex); } diff --git a/CadRevealComposer/Writers/GltfWriter.cs b/CadRevealComposer/Writers/GltfWriter.cs index 34d57fa5d..1301c8d7d 100644 --- a/CadRevealComposer/Writers/GltfWriter.cs +++ b/CadRevealComposer/Writers/GltfWriter.cs @@ -121,7 +121,7 @@ private static void WriteInstancedMeshes(InstancedMesh[] meshes, ModelRoot model ); // write indices - var indexBufferInt = MemoryMarshal.Cast(indexBuffer.Content.AsSpan()); + var indexBufferInt = MemoryMarshal.Cast(indexBuffer.Content.AsSpan()); sourceMesh.Indices.CopyTo(indexBufferInt); // write vertices @@ -227,11 +227,11 @@ private static void WriteTriangleMeshes(TriangleMesh[] triangleMeshes, ModelRoot // write indices var indices = sourceMesh.Indices; var indexBufferSpan = MemoryMarshal - .Cast(indexBuffer.Content.AsSpan()) + .Cast(indexBuffer.Content.AsSpan()) .Slice(indexOffset, indices.Length); for (var i = 0; i < indices.Length; i++) { - indexBufferSpan[i] = (uint)vertexOffset + indices[i]; + indexBufferSpan[i] = vertexOffset + indices[i]; } // write vertices diff --git a/CadRevealFbxProvider/BatchUtils/FbxWorkload.cs b/CadRevealFbxProvider/BatchUtils/FbxWorkload.cs index 6930ee362..dcb7b719d 100644 --- a/CadRevealFbxProvider/BatchUtils/FbxWorkload.cs +++ b/CadRevealFbxProvider/BatchUtils/FbxWorkload.cs @@ -5,6 +5,7 @@ using CadRevealComposer.IdProviders; using CadRevealComposer.Operations; using Commons; +using StringInternPool; using System.Text.RegularExpressions; public static class FbxWorkload diff --git a/CadRevealFbxProvider/FbxMeshWrapper.cs b/CadRevealFbxProvider/FbxMeshWrapper.cs index c607e446b..d8d1f2107 100644 --- a/CadRevealFbxProvider/FbxMeshWrapper.cs +++ b/CadRevealFbxProvider/FbxMeshWrapper.cs @@ -63,7 +63,7 @@ public static (Mesh Mesh, IntPtr MeshPtr)? GetGeometricData(FbxNode node) nn[i] = new Vector3(normals[3 * i], normals[3 * i + 1], normals[3 * i + 2]); } - var ii = indices.Select(a => (uint)a).ToArray(); + var ii = indices.ToArray(); const float error = 0f; // We have no tessellation error info for FBX files. diff --git a/CadRevealObjProvider/ObjProvider.cs b/CadRevealObjProvider/ObjProvider.cs index 96afb6f88..c2cdac283 100644 --- a/CadRevealObjProvider/ObjProvider.cs +++ b/CadRevealObjProvider/ObjProvider.cs @@ -90,7 +90,7 @@ InstanceIdGenerator instanceIdGenerator public record ObjMesh { public string Name { get; init; } = ""; - public uint[] Triangles { get; init; } = Array.Empty(); + public int[] Triangles { get; init; } = Array.Empty(); public Vector3[] Vertices { get; init; } = Array.Empty(); public Vector3[] Normals { get; init; } = Array.Empty(); @@ -110,9 +110,9 @@ private record VertexData(Vector3 Vertex, Vector3 Normal); private static ObjMesh? ReadMeshFromGroup(Group group, LoadResult result) { - var index = 0u; + var index = 0; var vertexData = new List(); - var triangles = new List(); + var triangles = new List(); foreach (Face groupFace in group.Faces) { Vector3? generatedNormal = null; diff --git a/CadRevealRvmProvider.Tests/Converters/RvmSnoutExtensionsTests.cs b/CadRevealRvmProvider.Tests/Converters/RvmSnoutExtensionsTests.cs new file mode 100644 index 000000000..19b47bb4f --- /dev/null +++ b/CadRevealRvmProvider.Tests/Converters/RvmSnoutExtensionsTests.cs @@ -0,0 +1,506 @@ +namespace CadRevealRvmProvider.Tests.Converters; + +using CadRevealRvmProvider.Converters; +using RvmSharp.Operations; +using RvmSharp.Primitives; +using System.Numerics; +using VectorD = MathNet.Numerics.LinearAlgebra.Vector; + +[TestFixture] +public class RvmSnoutExtensionsTests +{ + [Test] + public void RvmSnout_TestCapCalculation_Cone() + { + var snout = new RvmSnout( + 0, + new Matrix4x4(), + new RvmBoundingBox(new Vector3(), new Vector3()), + 2.0f, // bottom radius + 1.2928932188134525f, // top radius + 5.656854249492381f, // height + 0.0f, // offset x + 0.0f, // offset y + MathF.PI / 4.0f, // bottom shear x + MathF.PI / 4.0f, // bottom shear y + 0.0f, // top shear x + -MathF.PI / 4.0f // top shear y + ); + var topEllipse = snout.GetTopCapEllipse(); + + Assert.That(topEllipse.ellipse2DPolar.semiMajorAxis, Is.EqualTo(1.857449777519938f)); + Assert.That(topEllipse.ellipse2DPolar.semiMinorAxis, Is.EqualTo(1.3031138776160802)); + } + + [Test] + public void RvmSnout_TestCapCalculation_Cone_CappedInApex() + { + var snout = new RvmSnout( + 0, + new Matrix4x4(), + new RvmBoundingBox(new Vector3(), new Vector3()), + 76.0f, // bottom radius + 0.0f, // top radius + 90.0f, // height + 0.0f, // offset x + 0.0f, // offset y + 0.0f, // bottom shear x + 0.0f, // bottom shear y + 0.0f, // top shear x + 0.0f // top shear y + ); + var topEllipse = snout.GetTopCapEllipse(); + + Assert.That(topEllipse.ellipse2DPolar.semiMajorAxis, Is.EqualTo(0.0f)); + Assert.That(topEllipse.ellipse2DPolar.semiMinorAxis, Is.EqualTo(0.0f)); + } + + [Test] + public void RvmSnout_TestCapCalculation_FlippedCone_ApexInOrigin() + { + var snout = new RvmSnout( + 0, + new Matrix4x4(), + new RvmBoundingBox(new Vector3(), new Vector3()), + 0.0f, // bottom radius + 155.5f, // top radius + 187.0f, // height + 0.0f, // offset x + 0.0f, // offset y + 0.0f, // bottom shear x + 0.0f, // bottom shear y + 0.0f, // top shear x + 0.0f // top shear y + ); + var topEllipse = snout.GetTopCapEllipse(); + + Assert.That(topEllipse.ellipse2DPolar.semiMajorAxis, Is.EqualTo(155.5)); + Assert.That(topEllipse.ellipse2DPolar.semiMinorAxis, Is.EqualTo(155.5)); + } + + [Test] + public void RvmSnout_TestCapCalculation_Cone_Fzero() + { + var snout = new RvmSnout( + 0, + new Matrix4x4(), + new RvmBoundingBox(new Vector3(), new Vector3()), + 24.0f, // bottom radius + 16.5f, // top radius + 64.0f, // height + 7.5f, // offset x + 0.0f, // offset y + 0.0f, // bottom shear x + 0.0f, // bottom shear y + 0.0f, // top shear x + 0.0f // top shear y + ); + var topEllipse = snout.GetTopCapEllipse(); + + Assert.That(topEllipse.ellipse2DPolar.semiMajorAxis, Is.EqualTo(16.5)); + Assert.That(topEllipse.ellipse2DPolar.semiMinorAxis, Is.EqualTo(16.5)); + } + + [Test] + public void RvmSnout_TestCapCalculation_Cylinder() + { + var snout = new RvmSnout( + 0, + new Matrix4x4(), + new RvmBoundingBox(new Vector3(), new Vector3()), + 2.0f, // bottom radius + 2.0f, // top radius + 4.0f, // height + 0.0f, // offset x + 0.0f, // offset y + MathF.PI / 4.0f, // bottom shear x + MathF.PI / 4.0f, // bottom shear y + 0.0f, // top shear x + -MathF.PI / 4.0f // top shear y + ); + var topEllipse = snout.GetTopCapEllipse(); + + Assert.That(topEllipse.ellipse2DPolar.semiMajorAxis, Is.EqualTo(2.0 / MathF.Cos(snout.TopShearY))); + Assert.That(topEllipse.ellipse2DPolar.semiMinorAxis, Is.EqualTo(2.0)); + } + + [Test] + public void RvmSnout_TestCapCalculation_Origin_Cylinder() + { + var snout = new RvmSnout( + 0, + new Matrix4x4(), + new RvmBoundingBox(new Vector3(), new Vector3()), + 712.0f, // bottom radius + 712.0f, // top radius + 640.0f, // height + 0.0f, // offset x + 0.0f, // offset y + 0, // bottom shear x + 0, // bottom shear y + 0.0f, // top shear x + 0.17453292f // top shear y + ); + var topEllipse = snout.GetTopCapEllipse(); + + Assert.That(topEllipse.ellipse2DPolar.x0, Is.EqualTo(0.0)); + Assert.That(topEllipse.ellipse2DPolar.y0, Is.EqualTo(0.0)); + } + + //0.1 millimeters precision + [Test] + [DefaultFloatingPointTolerance(0.1)] + public void RvmSnout_CylinderCap_InSamePlane() + { + var snout1 = new RvmSnout( + 1, + new Matrix4x4( + 0.001f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0008660254f, + -0.0005f, + 0.0f, + 0.0f, + 0.0005f, + 0.0008660254f, + 0.0f, + 74.95f, + 289.608978f, + 36.95f, + 1.0f + ), + new RvmBoundingBox(new Vector3(-500.0f, -500.0f, 160.0475f), new Vector3(500.0f, 500.0f, 695.9459f)), + 500, // bottom radius + 500.0f, // top radius + 535.8984f, // height + 0.0f, // offset x + 0.0f, // offset y + 0, // bottom shear x + 0.2617994f, // bottom shear y + 0.0f, // top shear x + -0.2617994f // top shear y + ); + var ellipse1 = snout1.GetBottomCapEllipse(); + + var snout2 = new RvmSnout( + 1, + new Matrix4x4( + 0.0f, + 0.001f, + 0.0f, + 0.0f, + -0.001f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 0.001f, + 0.0f, + 74.95f, + 289.475f, + 36.5839729f, + 1.0f + ), + new RvmBoundingBox(new Vector3(-500.0f, -500.0f, -133.9746f), new Vector3(500.0f, 500.0f, 561.9713f)), + 500.0f, // bottom radius + 500.0f, // top radius + 267.9492f, // height + 0.0f, // offset x + 0.0f, // offset y + 0.0f, // bottom shear x + 0.0f, // bottom shear y + -0.2617994f, // top shear x + 0.0f // top shear y + ); + var ellipse2 = snout2.GetTopCapEllipse(); + + var origin = VectorD.Build.Dense(new double[] { 0.0, 0.0, 0.0, 1.0 }); + var pt_orig_ell1 = ellipse1.planeToModelCoord * origin; + var pt_orig_ell2 = ellipse2.planeToModelCoord * origin; + + // snout1 -> BOTTOM!! + var snout1CapCenter = -0.5f * (new Vector3(snout1.OffsetX, snout1.OffsetY, snout1.Height)); + (var snout1_n, var snout1_dc) = GeometryHelper.GetPlaneFromShearAndPoint( + snout1.BottomShearX, + snout1.BottomShearY, + snout1CapCenter + ); + var snout1_n_4d = new Vector4(snout1_n.X, snout1_n.Y, snout1_n.Z, 0.0f); + + var distance1 = + snout1_n.X * pt_orig_ell1[0] + snout1_n.Y * pt_orig_ell1[1] + snout1_n.Z * pt_orig_ell1[2] + snout1_dc; + + // snout2 -> bottom TOP!! + var snout2CapCenter = 0.5f * (new Vector3(snout2.OffsetX, snout2.OffsetY, snout2.Height)); + (var snout2_n, var snout2_dc) = GeometryHelper.GetPlaneFromShearAndPoint( + snout2.TopShearX, + snout2.TopShearY, + snout2CapCenter + ); + var snout2_n_4d = new Vector4(snout2_n.X, snout2_n.Y, snout2_n.Z, 0.0f); + + var distance2 = + snout2_n.X * pt_orig_ell2[0] + snout2_n.Y * pt_orig_ell2[1] + snout2_n.Z * pt_orig_ell2[2] + snout2_dc; + + var v4transfNormal1 = Vector4.Transform(snout1_n_4d, snout1.Matrix); + var v4transfNormal2 = Vector4.Transform(snout2_n_4d, snout2.Matrix); + var transfNormal1 = new Vector3(v4transfNormal1.X, v4transfNormal1.Y, v4transfNormal1.Z); + var transfNormal2 = new Vector3(v4transfNormal2.X, v4transfNormal2.Y, v4transfNormal2.Z); + transfNormal1 = Vector3.Normalize(transfNormal1); + transfNormal2 = Vector3.Normalize(transfNormal2); + + Assert.AreEqual(Vector3.Dot(transfNormal1, transfNormal2), 1.0f, 0.001); + + // are the planes going through the same pt? + // they are not for this snout!! + + Matrix4x4 s1Mat = (snout1.Matrix); + Matrix4x4 s2Mat = (snout2.Matrix); + Matrix4x4 sn2MatInv; + Matrix4x4.Invert(s2Mat, out sn2MatInv); + Matrix4x4 sn1MatInv; + Matrix4x4.Invert(s1Mat, out sn1MatInv); + + var p1_w = Vector4.Transform(snout1CapCenter, s1Mat); + var p1_m2 = Vector4.Transform(p1_w, sn2MatInv); + var d_c1_pl2 = Vector3.Dot(snout2_n, new Vector3(p1_m2.X, p1_m2.Y, p1_m2.Z)) + snout2_dc; + + var p2 = Vector4.Transform(Vector4.Transform(snout2CapCenter, s2Mat), sn1MatInv); + var d_c2_pl1 = Vector3.Dot(snout1_n, new Vector3(p2.X, p2.Y, p2.Z)) + snout1_dc; + + Assert.AreEqual(0.0, d_c1_pl2); + Assert.AreEqual(0.0, d_c2_pl1); + + Assert.AreEqual(0.0, distance1 * 1000.0); + Assert.AreEqual(0.0, distance2 * 1000.0); + } + + [Test] + [DefaultFloatingPointTolerance(0.1)] + public void RvmSnout_TrivialConeCap_InSamePlane() + { + var snout1 = new RvmSnout( + 1, + new Matrix4x4( + 0.0f, + 0.0f, + -0.001f, + 0.0f, + -0.000422618265f, + -0.0009063078f, + 0.0f, + 0.0f, + 0.0f, + -0.0009063078f, + 0.000422618265f, + 0.0f, + 112.532379f, + 297.9548f, + 25.335f, + 1.0f + ), + new RvmBoundingBox(new Vector3(-17.355f, -17.355f, -13.639999f), new Vector3(17.355f, 17.355f, 13.639999f)), + 17.355f, // bottom radius + 13.35f, // top radius + 27.2799988f, // height + 0.0f, // offset x + 0.0f, // offset y + 0.0f, // bottom shear x + 0.0f, // bottom shear y + 0.0f, // top shear x + 0.0f // top shear y + ); + + var snout2 = new RvmSnout( + 1, + new Matrix4x4( + 0.0f, + 0.0f, + -0.001f, + 0.0f, + -0.000422618265f, + -0.0009063078f, + 0.0f, + 0.0f, + 0.0f, + -0.0009063078f, + 0.000422618265f, + 0.0f, + 112.532379f, + 297.9548f, + 25.335f, + 1.0f + ), + new RvmBoundingBox(new Vector3(-17.355f, -17.355f, -38.64f), new Vector3(17.355f, 17.355f, 38.64f)), + 13.35f, // bottom radius + 17.355f, // top radius + 77.28f, // height + 0.0f, // offset x + 0.0f, // offset y + 0.0f, // bottom shear x + 0.0f, // bottom shear y + 0.0f, // top shear x + 0.0f // top shear y + ); + + var ellipse1 = snout1.GetTopCapEllipse(); + var ellipse2 = snout2.GetBottomCapEllipse(); + + var origin = VectorD.Build.Dense(new double[] { 0.0, 0.0, 0.0, 1.0 }); + var pt_orig_ell1 = ellipse1.planeToModelCoord * origin; + var pt_orig_ell2 = ellipse2.planeToModelCoord * origin; + + // snout1 -> top + var snout1CapCenter = 0.5f * (new Vector3(snout1.OffsetX, snout1.OffsetY, snout1.Height)); + (var snout1_n, var snout1_dc) = GeometryHelper.GetPlaneFromShearAndPoint( + snout1.TopShearX, + snout1.TopShearY, + snout1CapCenter + ); + var snout1_n_4d = new Vector4(snout1_n.X, snout1_n.Y, snout1_n.Z, 0.0f); + + var distance1 = + snout1_n.X * pt_orig_ell1[0] + snout1_n.Y * pt_orig_ell1[1] + snout1_n.Z * pt_orig_ell1[2] + snout1_dc; + + // snout2 -> bottom + var snout2CapCenter = -0.5f * (new Vector3(snout2.OffsetX, snout2.OffsetY, snout2.Height)); + (var snout2_n, var snout2_dc) = GeometryHelper.GetPlaneFromShearAndPoint( + snout2.BottomShearX, + snout2.BottomShearY, + snout2CapCenter + ); + var snout2_n_4d = new Vector4(snout2_n.X, snout2_n.Y, snout2_n.Z, 0.0f); + + var distance2 = + snout2_n.X * pt_orig_ell2[0] + snout2_n.Y * pt_orig_ell2[1] + snout2_n.Z * pt_orig_ell2[2] + snout2_dc; + + var v4transfNormal1 = Vector4.Transform(snout1_n_4d, snout1.Matrix); + var v4transfNormal2 = Vector4.Transform(snout2_n_4d, snout2.Matrix); + var transfNormal1 = new Vector3(v4transfNormal1.X, v4transfNormal1.Y, v4transfNormal1.Z); + var transfNormal2 = new Vector3(v4transfNormal2.X, v4transfNormal2.Y, v4transfNormal2.Z); + transfNormal1 = Vector3.Normalize(transfNormal1); + transfNormal2 = Vector3.Normalize(transfNormal2); + + Assert.AreEqual(Vector3.Dot(transfNormal1, transfNormal2), 1.0f, 0.001); + + Assert.AreEqual(0.0, distance1 * 1000.0); + Assert.AreEqual(0.0, distance2 * 1000.0); + } + + [Test] + [DefaultFloatingPointTolerance(0.1)] + public void RvmSnout_GeneralConeCap_InSamePlane() + { + var snout1 = new RvmSnout( + 1, + new Matrix4x4( + 0.0f, + 0.0f, + -0.001f, + 0.0f, + -0.000422618265f, + -0.0009063078f, + 0.0f, + 0.0f, + 0.0f, + -0.0009063078f, + 0.000422618265f, + 0.0f, + 112.532379f, + 297.9548f, + 25.335f, + 1.0f + ), + new RvmBoundingBox(new Vector3(-17.355f, -17.355f, -13.639999f), new Vector3(17.355f, 17.355f, 13.639999f)), + 17.355f, // bottom radius + 13.35f, // top radius + 27.2799988f, // height + 10.0f, // offset x + 20.0f, // offset y + 0.0f, // bottom shear x + 0.0f, // bottom shear y + -0.1f, // top shear x + 0.0f // top shear y + ); + + Matrix4x4 translate = Matrix4x4.CreateTranslation(snout1.OffsetX, snout1.OffsetY, 0.0f); + var snout2 = new RvmSnout( + 1, + new Matrix4x4( + 0.0f, + 0.0f, + -0.001f, + 0.0f, + -0.000422618265f, + -0.0009063078f, + 0.0f, + 0.0f, + 0.0f, + -0.0009063078f, + 0.000422618265f, + 0.0f, + 112.532379f, + 297.9548f, + 25.335f, + 1.0f + ) * translate, + new RvmBoundingBox(new Vector3(-17.355f, -17.355f, -38.64f), new Vector3(17.355f, 17.355f, 38.64f)), + 13.35f, // bottom radius + 17.355f, // top radius + 77.28f, // height + 15.0f, // offset x + 25.0f, // offset y + -0.1f, // bottom shear x + 0.0f, // bottom shear y + 0.0f, // top shear x + 0.0f // top shear y + ); + + var ellipse1 = snout1.GetTopCapEllipse(); + var ellipse2 = snout2.GetBottomCapEllipse(); + + var origin = VectorD.Build.Dense(new double[] { 0.0, 0.0, 0.0, 1.0 }); + var pt_orig_ell1 = ellipse1.planeToModelCoord * origin; + var pt_orig_ell2 = ellipse2.planeToModelCoord * origin; + + // snout1 -> top + var snout1CapCenter = 0.5f * (new Vector3(snout1.OffsetX, snout1.OffsetY, snout1.Height)); + (var snout1_n, var snout1_dc) = GeometryHelper.GetPlaneFromShearAndPoint( + snout1.TopShearX, + snout1.TopShearY, + snout1CapCenter + ); + var snout1_n_4d = new Vector4(snout1_n.X, snout1_n.Y, snout1_n.Z, 0.0f); + + var distance1 = + snout1_n.X * pt_orig_ell1[0] + snout1_n.Y * pt_orig_ell1[1] + snout1_n.Z * pt_orig_ell1[2] + snout1_dc; + + // snout2 -> bottom + var snout2CapCenter = -0.5f * (new Vector3(snout2.OffsetX, snout2.OffsetY, snout2.Height)); + (var snout2_n, var snout2_dc) = GeometryHelper.GetPlaneFromShearAndPoint( + snout2.BottomShearX, + snout2.BottomShearY, + snout2CapCenter + ); + var snout2_n_4d = new Vector4(snout2_n.X, snout2_n.Y, snout2_n.Z, 0.0f); + + var distance2 = + snout2_n.X * pt_orig_ell2[0] + snout2_n.Y * pt_orig_ell2[1] + snout2_n.Z * pt_orig_ell2[2] + snout2_dc; + + var v4transfNormal1 = Vector4.Transform(snout1_n_4d, snout1.Matrix); + var v4transfNormal2 = Vector4.Transform(snout2_n_4d, snout2.Matrix); + var transfNormal1 = new Vector3(v4transfNormal1.X, v4transfNormal1.Y, v4transfNormal1.Z); + var transfNormal2 = new Vector3(v4transfNormal2.X, v4transfNormal2.Y, v4transfNormal2.Z); + transfNormal1 = Vector3.Normalize(transfNormal1); + transfNormal2 = Vector3.Normalize(transfNormal2); + + Assert.AreEqual(Vector3.Dot(transfNormal1, transfNormal2), 1.0f, 0.001); + + Assert.AreEqual(0.0, distance1 * 1000.0); + Assert.AreEqual(0.0, distance2 * 1000.0); + } +} diff --git a/CadRevealRvmProvider/BatchUtils/RvmWorkload.cs b/CadRevealRvmProvider/BatchUtils/RvmWorkload.cs index d0237ab31..2ef4276f8 100644 --- a/CadRevealRvmProvider/BatchUtils/RvmWorkload.cs +++ b/CadRevealRvmProvider/BatchUtils/RvmWorkload.cs @@ -1,9 +1,9 @@ namespace CadRevealRvmProvider.BatchUtils; -using Commons; using RvmSharp; using RvmSharp.Containers; using RvmSharp.Operations; +using StringInternPool; using System.Text.RegularExpressions; public static class RvmWorkload diff --git a/CadRevealRvmProvider/CadRevealRvmProvider.csproj b/CadRevealRvmProvider/CadRevealRvmProvider.csproj index 129da68e9..d4bc9b794 100644 --- a/CadRevealRvmProvider/CadRevealRvmProvider.csproj +++ b/CadRevealRvmProvider/CadRevealRvmProvider.csproj @@ -10,6 +10,7 @@ + diff --git a/RvmSharp/Operations/ConicSectionsHelper.cs b/CadRevealRvmProvider/Converters/ConicSectionsHelper.cs similarity index 100% rename from RvmSharp/Operations/ConicSectionsHelper.cs rename to CadRevealRvmProvider/Converters/ConicSectionsHelper.cs diff --git a/CadRevealRvmProvider/Converters/RvmSnoutExtensions.cs b/CadRevealRvmProvider/Converters/RvmSnoutExtensions.cs new file mode 100644 index 000000000..aed860e60 --- /dev/null +++ b/CadRevealRvmProvider/Converters/RvmSnoutExtensions.cs @@ -0,0 +1,104 @@ +namespace CadRevealRvmProvider.Converters; + +using RvmSharp.Operations; +using RvmSharp.Primitives; +using System.Numerics; + +public static class RvmSnoutExtensions +{ + public static bool HasShear(this RvmSnout rvmSnout) + { + return rvmSnout.BottomShearX != 0 || rvmSnout.BottomShearY != 0 || rvmSnout.TopShearX != 0 || rvmSnout.TopShearY != 0; + } + + public static bool IsEccentric(this RvmSnout rvmSnout) + { + return rvmSnout.OffsetX != 0 || rvmSnout.OffsetY != 0; + } + + public static (Quaternion rotation, Vector3 normal, float slope) GetTopSlope(this RvmSnout rvmSnout) + { + return TranslateShearToSlope(rvmSnout.TopShearX, rvmSnout.TopShearY); + } + + public static (Quaternion rotation, Vector3 normal, float slope) GetBottomSlope(this RvmSnout rvmSnout) + { + return TranslateShearToSlope(rvmSnout.BottomShearX, rvmSnout.BottomShearY); + } + + public static bool IsCappedCylinder(this RvmSnout rvmSnout) + { + return Math.Abs(rvmSnout.RadiusBottom - rvmSnout.RadiusTop) < 0.01; + } + + public static Ellipse3D GetTopCapEllipse(this RvmSnout rvmSnout) + { + // plane that is defined by the top cap + var topCapCenter = 0.5f * new Vector3(rvmSnout.OffsetX, rvmSnout.OffsetY, rvmSnout.Height); + var xPlane = GeometryHelper.GetPlaneFromShearAndPoint(rvmSnout.TopShearX, rvmSnout.TopShearY, topCapCenter); + + return rvmSnout.GetCapEllipse(xPlane, topCapCenter, rvmSnout.RadiusTop); + } + + public static Ellipse3D GetBottomCapEllipse(this RvmSnout rvmSnout) + { + // plane that is defined by the bottom cap + var bottomCapCenter = -0.5f * new Vector3(rvmSnout.OffsetX, rvmSnout.OffsetY, rvmSnout.Height); + var xPlane = GeometryHelper.GetPlaneFromShearAndPoint(rvmSnout.BottomShearX, rvmSnout.BottomShearY, bottomCapCenter); + + return rvmSnout.GetCapEllipse(xPlane, bottomCapCenter, rvmSnout.RadiusBottom); + } + + private static Ellipse3D GetCapEllipse(this RvmSnout rvmSnout, PlaneImplicitForm xPlane, Vector3 capCenter, float capRadius) + { + // cones + if (!rvmSnout.IsCappedCylinder()) + { + var offset = new Vector3(rvmSnout.OffsetX, rvmSnout.OffsetY, rvmSnout.Height); + var cone = ConicSectionsHelper.CreateConeFromSnout(rvmSnout.RadiusBottom, rvmSnout.RadiusTop, offset); + + if (Math.Abs(capRadius) < 0.01) + { + return ConicSectionsHelper.CreateDegenerateEllipse(xPlane, cone); + } + return ConicSectionsHelper.CalcEllipseIntersectionForCone(xPlane, cone); + } + //cylinders + else + { + var cosineSlope = Vector3.Dot(xPlane.normal, new Vector3(0.0f, 0.0f, 1.0f)); + + // the most trivial case, cylinder with zero slope + if (cosineSlope == 1) + { + return ConicSectionsHelper.CalcEllipseIntersectionForCylinderWithZeroCapSlope(rvmSnout.RadiusBottom, capCenter); + } + else + { + return ConicSectionsHelper.CalcEllipseIntersectionForCylinder(xPlane, rvmSnout.RadiusBottom, capCenter); + } + } + } + + private static (Quaternion rotation, Vector3 normal, float slope) TranslateShearToSlope(float shearX, float shearY) + { + var rotationAroundY = Quaternion.CreateFromAxisAngle(Vector3.UnitY, -shearX); + var rotationAroundX = Quaternion.CreateFromAxisAngle(Vector3.UnitX, shearY); + var rotation = rotationAroundX * rotationAroundY; + var normal = Vector3.Transform(Vector3.UnitZ, rotation); + var slope = MathF.PI / 2f - MathF.Atan2(normal.Z, MathF.Sqrt(normal.X * normal.X + normal.Y * normal.Y)); + + float rotZAmount = 0; + + if (shearX != 0 || shearY != 0) + { + rotZAmount = (shearX / (shearX + shearY)) * MathF.PI / 2; + } + + Quaternion rotationAroundZ = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, rotZAmount); + + rotation *= rotationAroundZ; + + return (rotation, normal, slope); + } +} diff --git a/Commons/BenStringInternPool.cs b/Commons/BenStringInternPool.cs index 85ba4eded..325a92a99 100644 --- a/Commons/BenStringInternPool.cs +++ b/Commons/BenStringInternPool.cs @@ -1,6 +1,7 @@ namespace Commons; using Ben.Collections.Specialized; +using StringInternPool; using System; /// diff --git a/Commons/Commons.csproj b/Commons/Commons.csproj index 2574082d8..4e0271138 100644 --- a/Commons/Commons.csproj +++ b/Commons/Commons.csproj @@ -17,4 +17,8 @@ + + + + diff --git a/RvmSharp.Tests/Primitives/RvmPrimitiveTests.cs b/RvmSharp.Tests/Primitives/RvmPrimitiveTests.cs index cd3c89c0e..672a34cfd 100644 --- a/RvmSharp.Tests/Primitives/RvmPrimitiveTests.cs +++ b/RvmSharp.Tests/Primitives/RvmPrimitiveTests.cs @@ -7,7 +7,6 @@ using System.Collections; using System.Linq; using System.Numerics; -using VectorD = MathNet.Numerics.LinearAlgebra.Vector; [TestFixture] [DefaultFloatingPointTolerance(0.0001)] @@ -145,499 +144,4 @@ public void RvmPrimitive_WhenEqualValues_AreValueEqual() Assert.That(box, Is.Not.EqualTo(newIdenticalBox)); } - - [Test] - public void RvmSnout_TestCapCalculation_Cone() - { - var snout = new RvmSnout( - 0, - new Matrix4x4(), - new RvmBoundingBox(new Vector3(), new Vector3()), - 2.0f, // bottom radius - 1.2928932188134525f, // top radius - 5.656854249492381f, // height - 0.0f, // offset x - 0.0f, // offset y - MathF.PI / 4.0f, // bottom shear x - MathF.PI / 4.0f, // bottom shear y - 0.0f, // top shear x - -MathF.PI / 4.0f // top shear y - ); - var topEllipse = snout.GetTopCapEllipse(); - - Assert.That(topEllipse.ellipse2DPolar.semiMajorAxis, Is.EqualTo(1.857449777519938f)); - Assert.That(topEllipse.ellipse2DPolar.semiMinorAxis, Is.EqualTo(1.3031138776160802)); - } - - [Test] - public void RvmSnout_TestCapCalculation_Cone_CappedInApex() - { - var snout = new RvmSnout( - 0, - new Matrix4x4(), - new RvmBoundingBox(new Vector3(), new Vector3()), - 76.0f, // bottom radius - 0.0f, // top radius - 90.0f, // height - 0.0f, // offset x - 0.0f, // offset y - 0.0f, // bottom shear x - 0.0f, // bottom shear y - 0.0f, // top shear x - 0.0f // top shear y - ); - var topEllipse = snout.GetTopCapEllipse(); - - Assert.That(topEllipse.ellipse2DPolar.semiMajorAxis, Is.EqualTo(0.0f)); - Assert.That(topEllipse.ellipse2DPolar.semiMinorAxis, Is.EqualTo(0.0f)); - } - - [Test] - public void RvmSnout_TestCapCalculation_FlippedCone_ApexInOrigin() - { - var snout = new RvmSnout( - 0, - new Matrix4x4(), - new RvmBoundingBox(new Vector3(), new Vector3()), - 0.0f, // bottom radius - 155.5f, // top radius - 187.0f, // height - 0.0f, // offset x - 0.0f, // offset y - 0.0f, // bottom shear x - 0.0f, // bottom shear y - 0.0f, // top shear x - 0.0f // top shear y - ); - var topEllipse = snout.GetTopCapEllipse(); - - Assert.That(topEllipse.ellipse2DPolar.semiMajorAxis, Is.EqualTo(155.5)); - Assert.That(topEllipse.ellipse2DPolar.semiMinorAxis, Is.EqualTo(155.5)); - } - - [Test] - public void RvmSnout_TestCapCalculation_Cone_Fzero() - { - var snout = new RvmSnout( - 0, - new Matrix4x4(), - new RvmBoundingBox(new Vector3(), new Vector3()), - 24.0f, // bottom radius - 16.5f, // top radius - 64.0f, // height - 7.5f, // offset x - 0.0f, // offset y - 0.0f, // bottom shear x - 0.0f, // bottom shear y - 0.0f, // top shear x - 0.0f // top shear y - ); - var topEllipse = snout.GetTopCapEllipse(); - - Assert.That(topEllipse.ellipse2DPolar.semiMajorAxis, Is.EqualTo(16.5)); - Assert.That(topEllipse.ellipse2DPolar.semiMinorAxis, Is.EqualTo(16.5)); - } - - [Test] - public void RvmSnout_TestCapCalculation_Cylinder() - { - var snout = new RvmSnout( - 0, - new Matrix4x4(), - new RvmBoundingBox(new Vector3(), new Vector3()), - 2.0f, // bottom radius - 2.0f, // top radius - 4.0f, // height - 0.0f, // offset x - 0.0f, // offset y - MathF.PI / 4.0f, // bottom shear x - MathF.PI / 4.0f, // bottom shear y - 0.0f, // top shear x - -MathF.PI / 4.0f // top shear y - ); - var topEllipse = snout.GetTopCapEllipse(); - - Assert.That(topEllipse.ellipse2DPolar.semiMajorAxis, Is.EqualTo(2.0 / MathF.Cos(snout.TopShearY))); - Assert.That(topEllipse.ellipse2DPolar.semiMinorAxis, Is.EqualTo(2.0)); - } - - [Test] - public void RvmSnout_TestCapCalculation_Origin_Cylinder() - { - var snout = new RvmSnout( - 0, - new Matrix4x4(), - new RvmBoundingBox(new Vector3(), new Vector3()), - 712.0f, // bottom radius - 712.0f, // top radius - 640.0f, // height - 0.0f, // offset x - 0.0f, // offset y - 0, // bottom shear x - 0, // bottom shear y - 0.0f, // top shear x - 0.17453292f // top shear y - ); - var topEllipse = snout.GetTopCapEllipse(); - - Assert.That(topEllipse.ellipse2DPolar.x0, Is.EqualTo(0.0)); - Assert.That(topEllipse.ellipse2DPolar.y0, Is.EqualTo(0.0)); - } - - //0.1 millimeters precision - [Test] - [DefaultFloatingPointTolerance(0.1)] - public void RvmSnout_CylinderCap_InSamePlane() - { - var snout1 = new RvmSnout( - 1, - new Matrix4x4( - 0.001f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0008660254f, - -0.0005f, - 0.0f, - 0.0f, - 0.0005f, - 0.0008660254f, - 0.0f, - 74.95f, - 289.608978f, - 36.95f, - 1.0f - ), - new RvmBoundingBox(new Vector3(-500.0f, -500.0f, 160.0475f), new Vector3(500.0f, 500.0f, 695.9459f)), - 500, // bottom radius - 500.0f, // top radius - 535.8984f, // height - 0.0f, // offset x - 0.0f, // offset y - 0, // bottom shear x - 0.2617994f, // bottom shear y - 0.0f, // top shear x - -0.2617994f // top shear y - ); - var ellipse1 = snout1.GetBottomCapEllipse(); - - var snout2 = new RvmSnout( - 1, - new Matrix4x4( - 0.0f, - 0.001f, - 0.0f, - 0.0f, - -0.001f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.001f, - 0.0f, - 74.95f, - 289.475f, - 36.5839729f, - 1.0f - ), - new RvmBoundingBox(new Vector3(-500.0f, -500.0f, -133.9746f), new Vector3(500.0f, 500.0f, 561.9713f)), - 500.0f, // bottom radius - 500.0f, // top radius - 267.9492f, // height - 0.0f, // offset x - 0.0f, // offset y - 0.0f, // bottom shear x - 0.0f, // bottom shear y - -0.2617994f, // top shear x - 0.0f // top shear y - ); - var ellipse2 = snout2.GetTopCapEllipse(); - - var origin = VectorD.Build.Dense(new double[] { 0.0, 0.0, 0.0, 1.0 }); - var pt_orig_ell1 = ellipse1.planeToModelCoord * origin; - var pt_orig_ell2 = ellipse2.planeToModelCoord * origin; - - // snout1 -> BOTTOM!! - var snout1CapCenter = -0.5f * (new Vector3(snout1.OffsetX, snout1.OffsetY, snout1.Height)); - (var snout1_n, var snout1_dc) = GeometryHelper.GetPlaneFromShearAndPoint( - snout1.BottomShearX, - snout1.BottomShearY, - snout1CapCenter - ); - var snout1_n_4d = new Vector4(snout1_n.X, snout1_n.Y, snout1_n.Z, 0.0f); - - var distance1 = - snout1_n.X * pt_orig_ell1[0] + snout1_n.Y * pt_orig_ell1[1] + snout1_n.Z * pt_orig_ell1[2] + snout1_dc; - - // snout2 -> bottom TOP!! - var snout2CapCenter = 0.5f * (new Vector3(snout2.OffsetX, snout2.OffsetY, snout2.Height)); - (var snout2_n, var snout2_dc) = GeometryHelper.GetPlaneFromShearAndPoint( - snout2.TopShearX, - snout2.TopShearY, - snout2CapCenter - ); - var snout2_n_4d = new Vector4(snout2_n.X, snout2_n.Y, snout2_n.Z, 0.0f); - - var distance2 = - snout2_n.X * pt_orig_ell2[0] + snout2_n.Y * pt_orig_ell2[1] + snout2_n.Z * pt_orig_ell2[2] + snout2_dc; - - var v4transfNormal1 = Vector4.Transform(snout1_n_4d, snout1.Matrix); - var v4transfNormal2 = Vector4.Transform(snout2_n_4d, snout2.Matrix); - var transfNormal1 = new Vector3(v4transfNormal1.X, v4transfNormal1.Y, v4transfNormal1.Z); - var transfNormal2 = new Vector3(v4transfNormal2.X, v4transfNormal2.Y, v4transfNormal2.Z); - transfNormal1 = Vector3.Normalize(transfNormal1); - transfNormal2 = Vector3.Normalize(transfNormal2); - - Assert.AreEqual(Vector3.Dot(transfNormal1, transfNormal2), 1.0f, 0.001); - - // are the planes going through the same pt? - // they are not for this snout!! - - Matrix4x4 s1Mat = (snout1.Matrix); - Matrix4x4 s2Mat = (snout2.Matrix); - Matrix4x4 sn2MatInv; - Matrix4x4.Invert(s2Mat, out sn2MatInv); - Matrix4x4 sn1MatInv; - Matrix4x4.Invert(s1Mat, out sn1MatInv); - - var p1_w = Vector4.Transform(snout1CapCenter, s1Mat); - var p1_m2 = Vector4.Transform(p1_w, sn2MatInv); - var d_c1_pl2 = Vector3.Dot(snout2_n, new Vector3(p1_m2.X, p1_m2.Y, p1_m2.Z)) + snout2_dc; - - var p2 = Vector4.Transform(Vector4.Transform(snout2CapCenter, s2Mat), sn1MatInv); - var d_c2_pl1 = Vector3.Dot(snout1_n, new Vector3(p2.X, p2.Y, p2.Z)) + snout1_dc; - - Assert.AreEqual(0.0, d_c1_pl2); - Assert.AreEqual(0.0, d_c2_pl1); - - Assert.AreEqual(0.0, distance1 * 1000.0); - Assert.AreEqual(0.0, distance2 * 1000.0); - } - - [Test] - [DefaultFloatingPointTolerance(0.1)] - public void RvmSnout_TrivialConeCap_InSamePlane() - { - var snout1 = new RvmSnout( - 1, - new Matrix4x4( - 0.0f, - 0.0f, - -0.001f, - 0.0f, - -0.000422618265f, - -0.0009063078f, - 0.0f, - 0.0f, - 0.0f, - -0.0009063078f, - 0.000422618265f, - 0.0f, - 112.532379f, - 297.9548f, - 25.335f, - 1.0f - ), - new RvmBoundingBox(new Vector3(-17.355f, -17.355f, -13.639999f), new Vector3(17.355f, 17.355f, 13.639999f)), - 17.355f, // bottom radius - 13.35f, // top radius - 27.2799988f, // height - 0.0f, // offset x - 0.0f, // offset y - 0.0f, // bottom shear x - 0.0f, // bottom shear y - 0.0f, // top shear x - 0.0f // top shear y - ); - - var snout2 = new RvmSnout( - 1, - new Matrix4x4( - 0.0f, - 0.0f, - -0.001f, - 0.0f, - -0.000422618265f, - -0.0009063078f, - 0.0f, - 0.0f, - 0.0f, - -0.0009063078f, - 0.000422618265f, - 0.0f, - 112.532379f, - 297.9548f, - 25.335f, - 1.0f - ), - new RvmBoundingBox(new Vector3(-17.355f, -17.355f, -38.64f), new Vector3(17.355f, 17.355f, 38.64f)), - 13.35f, // bottom radius - 17.355f, // top radius - 77.28f, // height - 0.0f, // offset x - 0.0f, // offset y - 0.0f, // bottom shear x - 0.0f, // bottom shear y - 0.0f, // top shear x - 0.0f // top shear y - ); - - var ellipse1 = snout1.GetTopCapEllipse(); - var ellipse2 = snout2.GetBottomCapEllipse(); - - var origin = VectorD.Build.Dense(new double[] { 0.0, 0.0, 0.0, 1.0 }); - var pt_orig_ell1 = ellipse1.planeToModelCoord * origin; - var pt_orig_ell2 = ellipse2.planeToModelCoord * origin; - - // snout1 -> top - var snout1CapCenter = 0.5f * (new Vector3(snout1.OffsetX, snout1.OffsetY, snout1.Height)); - (var snout1_n, var snout1_dc) = GeometryHelper.GetPlaneFromShearAndPoint( - snout1.TopShearX, - snout1.TopShearY, - snout1CapCenter - ); - var snout1_n_4d = new Vector4(snout1_n.X, snout1_n.Y, snout1_n.Z, 0.0f); - - var distance1 = - snout1_n.X * pt_orig_ell1[0] + snout1_n.Y * pt_orig_ell1[1] + snout1_n.Z * pt_orig_ell1[2] + snout1_dc; - - // snout2 -> bottom - var snout2CapCenter = -0.5f * (new Vector3(snout2.OffsetX, snout2.OffsetY, snout2.Height)); - (var snout2_n, var snout2_dc) = GeometryHelper.GetPlaneFromShearAndPoint( - snout2.BottomShearX, - snout2.BottomShearY, - snout2CapCenter - ); - var snout2_n_4d = new Vector4(snout2_n.X, snout2_n.Y, snout2_n.Z, 0.0f); - - var distance2 = - snout2_n.X * pt_orig_ell2[0] + snout2_n.Y * pt_orig_ell2[1] + snout2_n.Z * pt_orig_ell2[2] + snout2_dc; - - var v4transfNormal1 = Vector4.Transform(snout1_n_4d, snout1.Matrix); - var v4transfNormal2 = Vector4.Transform(snout2_n_4d, snout2.Matrix); - var transfNormal1 = new Vector3(v4transfNormal1.X, v4transfNormal1.Y, v4transfNormal1.Z); - var transfNormal2 = new Vector3(v4transfNormal2.X, v4transfNormal2.Y, v4transfNormal2.Z); - transfNormal1 = Vector3.Normalize(transfNormal1); - transfNormal2 = Vector3.Normalize(transfNormal2); - - Assert.AreEqual(Vector3.Dot(transfNormal1, transfNormal2), 1.0f, 0.001); - - Assert.AreEqual(0.0, distance1 * 1000.0); - Assert.AreEqual(0.0, distance2 * 1000.0); - } - - [Test] - [DefaultFloatingPointTolerance(0.1)] - public void RvmSnout_GeneralConeCap_InSamePlane() - { - var snout1 = new RvmSnout( - 1, - new Matrix4x4( - 0.0f, - 0.0f, - -0.001f, - 0.0f, - -0.000422618265f, - -0.0009063078f, - 0.0f, - 0.0f, - 0.0f, - -0.0009063078f, - 0.000422618265f, - 0.0f, - 112.532379f, - 297.9548f, - 25.335f, - 1.0f - ), - new RvmBoundingBox(new Vector3(-17.355f, -17.355f, -13.639999f), new Vector3(17.355f, 17.355f, 13.639999f)), - 17.355f, // bottom radius - 13.35f, // top radius - 27.2799988f, // height - 10.0f, // offset x - 20.0f, // offset y - 0.0f, // bottom shear x - 0.0f, // bottom shear y - -0.1f, // top shear x - 0.0f // top shear y - ); - - Matrix4x4 translate = Matrix4x4.CreateTranslation(snout1.OffsetX, snout1.OffsetY, 0.0f); - var snout2 = new RvmSnout( - 1, - new Matrix4x4( - 0.0f, - 0.0f, - -0.001f, - 0.0f, - -0.000422618265f, - -0.0009063078f, - 0.0f, - 0.0f, - 0.0f, - -0.0009063078f, - 0.000422618265f, - 0.0f, - 112.532379f, - 297.9548f, - 25.335f, - 1.0f - ) * translate, - new RvmBoundingBox(new Vector3(-17.355f, -17.355f, -38.64f), new Vector3(17.355f, 17.355f, 38.64f)), - 13.35f, // bottom radius - 17.355f, // top radius - 77.28f, // height - 15.0f, // offset x - 25.0f, // offset y - -0.1f, // bottom shear x - 0.0f, // bottom shear y - 0.0f, // top shear x - 0.0f // top shear y - ); - - var ellipse1 = snout1.GetTopCapEllipse(); - var ellipse2 = snout2.GetBottomCapEllipse(); - - var origin = VectorD.Build.Dense(new double[] { 0.0, 0.0, 0.0, 1.0 }); - var pt_orig_ell1 = ellipse1.planeToModelCoord * origin; - var pt_orig_ell2 = ellipse2.planeToModelCoord * origin; - - // snout1 -> top - var snout1CapCenter = 0.5f * (new Vector3(snout1.OffsetX, snout1.OffsetY, snout1.Height)); - (var snout1_n, var snout1_dc) = GeometryHelper.GetPlaneFromShearAndPoint( - snout1.TopShearX, - snout1.TopShearY, - snout1CapCenter - ); - var snout1_n_4d = new Vector4(snout1_n.X, snout1_n.Y, snout1_n.Z, 0.0f); - - var distance1 = - snout1_n.X * pt_orig_ell1[0] + snout1_n.Y * pt_orig_ell1[1] + snout1_n.Z * pt_orig_ell1[2] + snout1_dc; - - // snout2 -> bottom - var snout2CapCenter = -0.5f * (new Vector3(snout2.OffsetX, snout2.OffsetY, snout2.Height)); - (var snout2_n, var snout2_dc) = GeometryHelper.GetPlaneFromShearAndPoint( - snout2.BottomShearX, - snout2.BottomShearY, - snout2CapCenter - ); - var snout2_n_4d = new Vector4(snout2_n.X, snout2_n.Y, snout2_n.Z, 0.0f); - - var distance2 = - snout2_n.X * pt_orig_ell2[0] + snout2_n.Y * pt_orig_ell2[1] + snout2_n.Z * pt_orig_ell2[2] + snout2_dc; - - var v4transfNormal1 = Vector4.Transform(snout1_n_4d, snout1.Matrix); - var v4transfNormal2 = Vector4.Transform(snout2_n_4d, snout2.Matrix); - var transfNormal1 = new Vector3(v4transfNormal1.X, v4transfNormal1.Y, v4transfNormal1.Z); - var transfNormal2 = new Vector3(v4transfNormal2.X, v4transfNormal2.Y, v4transfNormal2.Z); - transfNormal1 = Vector3.Normalize(transfNormal1); - transfNormal2 = Vector3.Normalize(transfNormal2); - - Assert.AreEqual(Vector3.Dot(transfNormal1, transfNormal2), 1.0f, 0.001); - - Assert.AreEqual(0.0, distance1 * 1000.0); - Assert.AreEqual(0.0, distance2 * 1000.0); - } } diff --git a/RvmSharp/Containers/RvmFile.cs b/RvmSharp/Containers/RvmFile.cs index 846cba00e..4b3311c96 100644 --- a/RvmSharp/Containers/RvmFile.cs +++ b/RvmSharp/Containers/RvmFile.cs @@ -1,7 +1,7 @@ namespace RvmSharp.Containers; -using Commons; using Primitives; +using StringInternPool; using System.Collections.Generic; using System.Linq; diff --git a/RvmSharp/PdmsTextParser.cs b/RvmSharp/PdmsTextParser.cs index 15e24f785..fdb68091a 100644 --- a/RvmSharp/PdmsTextParser.cs +++ b/RvmSharp/PdmsTextParser.cs @@ -1,6 +1,6 @@ namespace RvmSharp; -using Commons; +using StringInternPool; using System; using System.Collections.Generic; using System.IO; diff --git a/RvmSharp/Primitives/RvmSnout.cs b/RvmSharp/Primitives/RvmSnout.cs index e1f92d0eb..c1a365f10 100644 --- a/RvmSharp/Primitives/RvmSnout.cs +++ b/RvmSharp/Primitives/RvmSnout.cs @@ -1,6 +1,5 @@ namespace RvmSharp.Primitives; -using Operations; using System; using System.Numerics; @@ -17,101 +16,4 @@ public record RvmSnout( float BottomShearY, float TopShearX, float TopShearY -) : RvmPrimitive(Version, RvmPrimitiveKind.Snout, Matrix, BoundingBoxLocal) -{ - public bool HasShear() - { - return BottomShearX != 0 || BottomShearY != 0 || TopShearX != 0 || TopShearY != 0; - } - - public bool IsEccentric() - { - return OffsetX != 0 || OffsetY != 0; - } - - public (Quaternion rotation, Vector3 normal, float slope) GetTopSlope() - { - return TranslateShearToSlope(TopShearX, TopShearY); - } - - public (Quaternion rotation, Vector3 normal, float slope) GetBottomSlope() - { - return TranslateShearToSlope(BottomShearX, BottomShearY); - } - - public bool IsCappedCylinder() - { - return Math.Abs(RadiusBottom - RadiusTop) < 0.01; - } - - public Ellipse3D GetTopCapEllipse() - { - // plane that is defined by the top cap - var topCapCenter = 0.5f * new Vector3(OffsetX, OffsetY, Height); - var xPlane = GeometryHelper.GetPlaneFromShearAndPoint(TopShearX, TopShearY, topCapCenter); - - return getCapEllipse(xPlane, topCapCenter, RadiusTop); - } - - public Ellipse3D GetBottomCapEllipse() - { - // plane that is defined by the bottom cap - var bottomCapCenter = -0.5f * new Vector3(OffsetX, OffsetY, Height); - var xPlane = GeometryHelper.GetPlaneFromShearAndPoint(BottomShearX, BottomShearY, bottomCapCenter); - - return getCapEllipse(xPlane, bottomCapCenter, RadiusBottom); - } - - private Ellipse3D getCapEllipse(PlaneImplicitForm xPlane, Vector3 capCenter, float capRadius) - { - // cones - if (!IsCappedCylinder()) - { - var offset = new Vector3(OffsetX, OffsetY, Height); - var cone = ConicSectionsHelper.CreateConeFromSnout(RadiusBottom, RadiusTop, offset); - - if (Math.Abs(capRadius) < 0.01) - { - return ConicSectionsHelper.CreateDegenerateEllipse(xPlane, cone); - } - return ConicSectionsHelper.CalcEllipseIntersectionForCone(xPlane, cone); - } - //cylinders - else - { - var cosineSlope = Vector3.Dot(xPlane.normal, new Vector3(0.0f, 0.0f, 1.0f)); - - // the most trivial case, cylinder with zero slope - if (cosineSlope == 1) - { - return ConicSectionsHelper.CalcEllipseIntersectionForCylinderWithZeroCapSlope(RadiusBottom, capCenter); - } - else - { - return ConicSectionsHelper.CalcEllipseIntersectionForCylinder(xPlane, RadiusBottom, capCenter); - } - } - } - - private (Quaternion rotation, Vector3 normal, float slope) TranslateShearToSlope(float shearX, float shearY) - { - var rotationAroundY = Quaternion.CreateFromAxisAngle(Vector3.UnitY, -shearX); - var rotationAroundX = Quaternion.CreateFromAxisAngle(Vector3.UnitX, shearY); - var rotation = rotationAroundX * rotationAroundY; - var normal = Vector3.Transform(Vector3.UnitZ, rotation); - var slope = MathF.PI / 2f - MathF.Atan2(normal.Z, MathF.Sqrt(normal.X * normal.X + normal.Y * normal.Y)); - - float rotZAmount = 0; - - if (shearX != 0 || shearY != 0) - { - rotZAmount = (shearX / (shearX + shearY)) * MathF.PI / 2; - } - - Quaternion rotationAroundZ = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, rotZAmount); - - rotation *= rotationAroundZ; - - return (rotation, normal, slope); - } -}; +) : RvmPrimitive(Version, RvmPrimitiveKind.Snout, Matrix, BoundingBoxLocal); diff --git a/RvmSharp/RvmSharp.csproj b/RvmSharp/RvmSharp.csproj index fde4d6031..b49a87c9e 100644 --- a/RvmSharp/RvmSharp.csproj +++ b/RvmSharp/RvmSharp.csproj @@ -10,11 +10,10 @@ - - + diff --git a/RvmSharp/Tessellation/RvmMesh.cs b/RvmSharp/Tessellation/RvmMesh.cs index 44db03f24..8333fa049 100644 --- a/RvmSharp/Tessellation/RvmMesh.cs +++ b/RvmSharp/Tessellation/RvmMesh.cs @@ -20,13 +20,13 @@ public class RvmMesh : IEquatable public Vector3[] Normals => _normals; - public uint[] Triangles => _triangles; + public int[] Triangles => _triangles; public int TriangleCount => _triangles.Length / 3; private readonly Vector3[] _vertices; private readonly Vector3[] _normals; - private readonly uint[] _triangles; + private readonly int[] _triangles; public RvmMesh(IReadOnlyList vertexData, IReadOnlyList normalData, int[] triangleData, float error) { @@ -42,11 +42,11 @@ public RvmMesh(IReadOnlyList vertexData, IReadOnlyList normalData, _normals[i] = new Vector3(normalData[i * 3], normalData[i * 3 + 1], normalData[i * 3 + 2]); } - _triangles = new uint[triangleData.Length]; + _triangles = new int[triangleData.Length]; Array.Copy(triangleData, _triangles, triangleData.Length); } - public RvmMesh(Vector3[] vertices, Vector3[] normals, uint[] triangles, float error) + public RvmMesh(Vector3[] vertices, Vector3[] normals, int[] triangles, float error) { if (vertices.Length != normals.Length) throw new ArgumentException("Vertex and normal arrays must have equal length"); @@ -74,7 +74,7 @@ public void Apply(Matrix4x4 matrix) public static RvmMesh Merge(RvmMesh mesh1, RvmMesh mesh2) { - var mesh1VertexCount = (uint)mesh1.Vertices.Length; + var mesh1VertexCount = mesh1.Vertices.Length; var vertices = mesh1.Vertices.Concat(mesh2.Vertices).ToArray(); var normals = mesh1.Normals.Concat(mesh2.Normals).ToArray(); var triangles = mesh1.Triangles.Concat(mesh2.Triangles.Select(t => t + mesh1VertexCount)).ToArray(); diff --git a/RvmSharp/Tessellation/TessellatorBridge.cs b/RvmSharp/Tessellation/TessellatorBridge.cs index fedfa2bdd..8545c4c3c 100644 --- a/RvmSharp/Tessellation/TessellatorBridge.cs +++ b/RvmSharp/Tessellation/TessellatorBridge.cs @@ -763,7 +763,7 @@ private static RvmMesh Tessellate(RvmFacetGroup facetGroup) throw new Exception(); } - return new RvmMesh(vertices.ToArray(), normals.ToArray(), indices.Select(x => (uint)x).ToArray(), 0); + return new RvmMesh(vertices.ToArray(), normals.ToArray(), indices.ToArray(), 0); } private static RvmMesh TessellateCylinder(RvmCylinder cylinder, float scale, float tolerance) @@ -921,7 +921,7 @@ private static RvmMesh TessellateCylinder(RvmCylinder cylinder, float scale, flo Debug.Assert(l == triangles_n * 3); Debug.Assert(o == vertCount); - return new RvmMesh(vertices, normals, indices.Select(x => (uint)x).ToArray(), error); + return new RvmMesh(vertices, normals, indices.ToArray(), error); } private static RvmMesh Tessellate(RvmSnout snout, float scale, float tolerance) diff --git a/Commons/IStringInternPool.cs b/StringInternPool/IStringInternPool.cs similarity index 90% rename from Commons/IStringInternPool.cs rename to StringInternPool/IStringInternPool.cs index c428c1243..710c591ce 100644 --- a/Commons/IStringInternPool.cs +++ b/StringInternPool/IStringInternPool.cs @@ -1,4 +1,4 @@ -namespace Commons; +namespace StringInternPool; using System; diff --git a/StringInternPool/StringInternPool.csproj b/StringInternPool/StringInternPool.csproj new file mode 100644 index 000000000..11fbde6d0 --- /dev/null +++ b/StringInternPool/StringInternPool.csproj @@ -0,0 +1,11 @@ + + + + Library + enable + net7.0;netstandard2.1 + nullable; + latest + + + diff --git a/rvmsharp.sln b/rvmsharp.sln index f43f92c6a..9afdee2cd 100644 --- a/rvmsharp.sln +++ b/rvmsharp.sln @@ -35,6 +35,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Commons", "Commons\Commons. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Commons.Tests", "Commons.Tests\Commons.Tests.csproj", "{E928953C-E0A7-4EDF-9484-49CFF5E303C5}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StringInternPool", "StringInternPool\StringInternPool.csproj", "{F0679AE9-7F1C-49F7-979B-0003AC14CD29}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -105,6 +107,10 @@ Global {E928953C-E0A7-4EDF-9484-49CFF5E303C5}.Debug|Any CPU.Build.0 = Debug|Any CPU {E928953C-E0A7-4EDF-9484-49CFF5E303C5}.Release|Any CPU.ActiveCfg = Release|Any CPU {E928953C-E0A7-4EDF-9484-49CFF5E303C5}.Release|Any CPU.Build.0 = Release|Any CPU + {F0679AE9-7F1C-49F7-979B-0003AC14CD29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0679AE9-7F1C-49F7-979B-0003AC14CD29}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0679AE9-7F1C-49F7-979B-0003AC14CD29}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0679AE9-7F1C-49F7-979B-0003AC14CD29}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE